How to Flip Normals in Vertex/Fragment Shader?

I can’t seem to get anything working… How do I flip vertex normals inside the shader? In a vertex/fragment sort of shader and not surf…

normal = -normal;

I still can’t seem to get it.
This is the code I’m using… It’s an instanced texturearray with uvs. I just want to test it by flipping all the normals.

Shader ".valtterim/Instanced/i_m_TextureArray"{
    Properties{
        _Textures("Textures", 2DArray) = "" {}
    }

    SubShader{
        Tags { "Queue"="Geometry" "RenderType"="Opaque" }
        Lighting Off

        Pass{
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_instancing
            #include "UnityCG.cginc"

            struct vertexInput{
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct vertexOutput{
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            UNITY_DECLARE_TEX2DARRAY(_Textures);

            UNITY_INSTANCING_BUFFER_START(MPB)
                UNITY_DEFINE_INSTANCED_PROP(float4, _UVs)
                UNITY_DEFINE_INSTANCED_PROP(float, _TextureIndex)
            UNITY_INSTANCING_BUFFER_END(MPB)

            vertexOutput vert(vertexInput input){
                vertexOutput output;

                UNITY_SETUP_INSTANCE_ID(input);
                UNITY_TRANSFER_INSTANCE_ID(input, output);

                output.vertex = UnityObjectToClipPos(input.vertex);
                output.uv = input.uv;
                output.normal = -input.normal;

                return output;
            }

            fixed4 frag(vertexOutput output) : SV_Target{
                UNITY_SETUP_INSTANCE_ID(output);

                float4 uniqueUV = UNITY_ACCESS_INSTANCED_PROP(MPB, _UVs);
                float2 finalUV = output.uv * uniqueUV.xy + uniqueUV.zw;

                return UNITY_SAMPLE_TEX2DARRAY(_Textures, float3(finalUV, UNITY_ACCESS_INSTANCED_PROP(MPB, _TextureIndex)));
            }

            ENDCG
        }
    }
}

You’re not using the vertex normals for anything. I think you’re mis-equating triangle facing with vertex normals. Vertex normals are generally used for lighting, but aren’t used by the GPU to determine which side of a triangle is rendered. You want to set the culling mode. By default Unity uses Cull Back if nothing is defined in the shader, which skips rendering of back faces. If you want to render the back faces, use Cull Off to render both the front and back, or Cull Front to render only the back. Those lines should be outside of the CGPROGRAM block, either in the SubShader {} or Pass {} depending on if you want to set it for all passes or an individual pass. Basically where you have the Lighting Off line (which you can remove because it does nothing) is where you want to add the culling mode line.

2 Likes

Oh I see. But is it possible to have it set with a material property block so that each instance could have it’s own triangle normals? Not just limit to Front or Back, but have it as dynamic.

Yes and no. The culling mode is part of the render state. You can’t change the render state with a material property block for various reasons. However you can get the same visual results using VFACE and clip() in the fragment shader.

// in properties, using built in enum where none == 0, front == 1, back == 2
[Enum(UnityEngine.Rendering.CullMode)] _CullMode ("Cull Mode", Float) = 2.0 // default to back face culling

// disable culling on the pass
Cull Off

// setup as a float property in your instanced props
UNITY_DEFINE_INSTANCED_PROP(float, _CullMode)

// have VFACE in your fragment shader function
fixed4 frag(vertexOutput output, fixed facing : VFACE) : SV_Target

// switch
float cullMode = UNITY_ACCESS_INSTANCED_PROP(MPB, _CullMode);
if (cullMode > 0.0)
  clip(facing * (cullMode - 0.5)); // hack

VFACE is a 1 or -1 depending on if it’s the front or back face.
clip() discards any fragments if the input value is < 0.0, so by default clip(facing); will replicate the usual back face culling.
Multiplying VFACE (facing) by (cullMode - 1.5) means that when the cullMode is a value of 2.0 it’s multiply the VFACE by 0.5. The front face will be 0.5 and back face is -0.5, so the behavior doesn’t change vs using VFACE by itself, so the back face is discard. When cullMode is a value of 1.0 it’s now multiplying by -0.5 meaning the front and back faces flip which is -0.5 and 0.5, discarding the front face.

Again, not normals, triangle facing.

4 Likes

Thank you bgolus!

I used VFACE to get the facing direction. Then with the material property block I can set which sides to render.
-1 is reversed
0 is both
1 is default side
2 is none

This is the final shader:

Shader ".valtterim/Instanced/i_m_TextureArray"{
    Properties{
        [HideInInspector] _Textures("Textures", 2DArray) = "" {}
    }

    SubShader{
        Tags { "Queue"="Geometry" "RenderType"="Opaque" }
        Lighting Off
        Cull Off

        Pass{
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_instancing
            #include "UnityCG.cginc"

            struct vertexInput{
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct vertexOutput{
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            UNITY_DECLARE_TEX2DARRAY(_Textures);

            UNITY_INSTANCING_BUFFER_START(MPB)
                UNITY_DEFINE_INSTANCED_PROP(float4, _UVs)
                UNITY_DEFINE_INSTANCED_PROP(float, _TextureIndex)
                UNITY_DEFINE_INSTANCED_PROP(float, _Normal)
            UNITY_INSTANCING_BUFFER_END(MPB)

            vertexOutput vert(vertexInput input){
                vertexOutput output;

                UNITY_SETUP_INSTANCE_ID(input);
                UNITY_TRANSFER_INSTANCE_ID(input, output);

                output.vertex = UnityObjectToClipPos(input.vertex);
                output.uv = input.uv;

                return output;
            }

            fixed4 frag(vertexOutput output, fixed facing : VFACE) : SV_Target{
                UNITY_SETUP_INSTANCE_ID(output);

                float normal = UNITY_ACCESS_INSTANCED_PROP(MPB, _Normal);
                if(normal == 2) clip(-1);
                else clip(facing * normal);

                float4 uniqueUV = UNITY_ACCESS_INSTANCED_PROP(MPB, _UVs);
                float2 finalUV = output.uv * uniqueUV.xy + uniqueUV.zw;
               
                return UNITY_SAMPLE_TEX2DARRAY(_Textures, float3(finalUV, UNITY_ACCESS_INSTANCED_PROP(MPB, _TextureIndex)));
            }

            ENDCG
        }
    }
}
2 Likes

@bgolus
Hi, it seems you master in shader programming, not me…
I’m trying to achieve something “simple” (well, I think) : I would like to have a copy a the “Standard Shader” but with an additional parameter called let’s say “InverseNormal” (Boolean, checkbox) that drive the normals direction (if true, I would like to have the normals inverted)
Is it possible ? Can you tell me how to edit the Standard Shader to add that functionality ?
Thanks a lot for your answer !