Search Unity

How to Flip Normals in Vertex/Fragment Shader?

Discussion in 'Shaders' started by valtteri_m, Jan 27, 2020.

  1. valtteri_m

    valtteri_m

    Joined:
    Aug 6, 2014
    Posts:
    15
    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..
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Code (csharp):
    1. normal = -normal;
     
  3. valtteri_m

    valtteri_m

    Joined:
    Aug 6, 2014
    Posts:
    15
    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.

    Code (CSharp):
    1. Shader ".valtterim/Instanced/i_m_TextureArray"{
    2.     Properties{
    3.         _Textures("Textures", 2DArray) = "" {}
    4.     }
    5.  
    6.     SubShader{
    7.         Tags { "Queue"="Geometry" "RenderType"="Opaque" }
    8.         Lighting Off
    9.  
    10.         Pass{
    11.             CGPROGRAM
    12.             #pragma vertex vert
    13.             #pragma fragment frag
    14.             #pragma multi_compile_instancing
    15.             #include "UnityCG.cginc"
    16.  
    17.             struct vertexInput{
    18.                 float4 vertex : POSITION;
    19.                 float2 uv : TEXCOORD0;
    20.                 float3 normal : NORMAL;
    21.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    22.             };
    23.  
    24.             struct vertexOutput{
    25.                 float4 vertex : SV_POSITION;
    26.                 float2 uv : TEXCOORD0;
    27.                 float3 normal : NORMAL;
    28.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    29.             };
    30.  
    31.             UNITY_DECLARE_TEX2DARRAY(_Textures);
    32.  
    33.             UNITY_INSTANCING_BUFFER_START(MPB)
    34.                 UNITY_DEFINE_INSTANCED_PROP(float4, _UVs)
    35.                 UNITY_DEFINE_INSTANCED_PROP(float, _TextureIndex)
    36.             UNITY_INSTANCING_BUFFER_END(MPB)
    37.  
    38.             vertexOutput vert(vertexInput input){
    39.                 vertexOutput output;
    40.  
    41.                 UNITY_SETUP_INSTANCE_ID(input);
    42.                 UNITY_TRANSFER_INSTANCE_ID(input, output);
    43.  
    44.                 output.vertex = UnityObjectToClipPos(input.vertex);
    45.                 output.uv = input.uv;
    46.                 output.normal = -input.normal;
    47.  
    48.                 return output;
    49.             }
    50.  
    51.             fixed4 frag(vertexOutput output) : SV_Target{
    52.                 UNITY_SETUP_INSTANCE_ID(output);
    53.  
    54.                 float4 uniqueUV = UNITY_ACCESS_INSTANCED_PROP(MPB, _UVs);
    55.                 float2 finalUV = output.uv * uniqueUV.xy + uniqueUV.zw;
    56.  
    57.                 return UNITY_SAMPLE_TEX2DARRAY(_Textures, float3(finalUV, UNITY_ACCESS_INSTANCED_PROP(MPB, _TextureIndex)));
    58.             }
    59.  
    60.             ENDCG
    61.         }
    62.     }
    63. }
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    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.
     
    AAAAAAAAAE and masterton like this.
  5. valtteri_m

    valtteri_m

    Joined:
    Aug 6, 2014
    Posts:
    15
    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.
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    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.
    Code (csharp):
    1. // in properties, using built in enum where none == 0, front == 1, back == 2
    2. [Enum(UnityEngine.Rendering.CullMode)] _CullMode ("Cull Mode", Float) = 2.0 // default to back face culling
    3.  
    4. // disable culling on the pass
    5. Cull Off
    6.  
    7. // setup as a float property in your instanced props
    8. UNITY_DEFINE_INSTANCED_PROP(float, _CullMode)
    9.  
    10. // have VFACE in your fragment shader function
    11. fixed4 frag(vertexOutput output, fixed facing : VFACE) : SV_Target
    12.  
    13. // switch
    14. float cullMode = UNITY_ACCESS_INSTANCED_PROP(MPB, _CullMode);
    15. if (cullMode > 0.0)
    16.   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.
     
  7. valtteri_m

    valtteri_m

    Joined:
    Aug 6, 2014
    Posts:
    15
    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:
    Code (CSharp):
    1. Shader ".valtterim/Instanced/i_m_TextureArray"{
    2.     Properties{
    3.         [HideInInspector] _Textures("Textures", 2DArray) = "" {}
    4.     }
    5.  
    6.     SubShader{
    7.         Tags { "Queue"="Geometry" "RenderType"="Opaque" }
    8.         Lighting Off
    9.         Cull Off
    10.  
    11.         Pass{
    12.             CGPROGRAM
    13.             #pragma vertex vert
    14.             #pragma fragment frag
    15.             #pragma multi_compile_instancing
    16.             #include "UnityCG.cginc"
    17.  
    18.             struct vertexInput{
    19.                 float4 vertex : POSITION;
    20.                 float2 uv : TEXCOORD0;
    21.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    22.             };
    23.  
    24.             struct vertexOutput{
    25.                 float4 vertex : SV_POSITION;
    26.                 float2 uv : TEXCOORD0;
    27.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    28.             };
    29.  
    30.             UNITY_DECLARE_TEX2DARRAY(_Textures);
    31.  
    32.             UNITY_INSTANCING_BUFFER_START(MPB)
    33.                 UNITY_DEFINE_INSTANCED_PROP(float4, _UVs)
    34.                 UNITY_DEFINE_INSTANCED_PROP(float, _TextureIndex)
    35.                 UNITY_DEFINE_INSTANCED_PROP(float, _Normal)
    36.             UNITY_INSTANCING_BUFFER_END(MPB)
    37.  
    38.             vertexOutput vert(vertexInput input){
    39.                 vertexOutput output;
    40.  
    41.                 UNITY_SETUP_INSTANCE_ID(input);
    42.                 UNITY_TRANSFER_INSTANCE_ID(input, output);
    43.  
    44.                 output.vertex = UnityObjectToClipPos(input.vertex);
    45.                 output.uv = input.uv;
    46.  
    47.                 return output;
    48.             }
    49.  
    50.             fixed4 frag(vertexOutput output, fixed facing : VFACE) : SV_Target{
    51.                 UNITY_SETUP_INSTANCE_ID(output);
    52.  
    53.                 float normal = UNITY_ACCESS_INSTANCED_PROP(MPB, _Normal);
    54.                 if(normal == 2) clip(-1);
    55.                 else clip(facing * normal);
    56.  
    57.                 float4 uniqueUV = UNITY_ACCESS_INSTANCED_PROP(MPB, _UVs);
    58.                 float2 finalUV = output.uv * uniqueUV.xy + uniqueUV.zw;
    59.                
    60.                 return UNITY_SAMPLE_TEX2DARRAY(_Textures, float3(finalUV, UNITY_ACCESS_INSTANCED_PROP(MPB, _TextureIndex)));
    61.             }
    62.  
    63.             ENDCG
    64.         }
    65.     }
    66. }
     
    Nastomeya and masterton like this.
  8. x2stone

    x2stone

    Joined:
    Dec 17, 2014
    Posts:
    22
    @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 !