Search Unity

Please Help: Trying to convert surface shader into cg fragment shader

Discussion in 'Shaders' started by 0xsven, Jan 28, 2016.

  1. 0xsven

    0xsven

    Joined:
    Mar 5, 2015
    Posts:
    18
    I am having a surface shader that I want to make a fragment shader. I want to learn more about fragment shaders. Unfortunately I am not getting it right. Can you maybe help?

    This is the surface shader:
    Code (CSharp):
    1. shader "Custom/SolidColorShader"
    2. {
    3.    // and just click on your light and change Render Mode to "Not Important"
    4.    SubShader
    5.    {
    6.      Tags { "RenderType" = "Opaque" }
    7.      CGPROGRAM
    8.      #pragma surface surf Lambert nometa noforwardadd
    9.      struct Input
    10.      {
    11.        float3 vertColors : COLOR;
    12.      };
    13.      void surf (Input IN, inout SurfaceOutput o)
    14.      {
    15.        o.Albedo = IN.vertColors.rgb;
    16.      }
    17.      ENDCG
    18.    }
    19. Fallback "Diffuse"
    20. }
    And this is how far I got with a fragment shader, I created:

    Code (CSharp):
    1. Shader "Custom/SolidColorShader2" {
    2.     SubShader {
    3.         Tags { "LightMode" = "ForwardBase"}
    4.         Pass {
    5.             CGPROGRAM
    6.  
    7.             #pragma fragmentoption ARP_precision_hint_fastest
    8.             #pragma multi_compile_fwdbase
    9.             #pragma vertex vert
    10.             #pragma fragment frag
    11.             #include "UnityCG.cginc"
    12.             #include "AutoLight.cginc"
    13.  
    14.             uniform fixed4 _LightColor0;
    15.             struct vertexInput
    16.             {
    17.                 float4 vertex : POSITION;
    18.                 float3 normal : NORMAL;
    19.                 float3 vertColor : COLOR;
    20.             };
    21.             struct fragmentInput
    22.             {
    23.                 float4 pos : SV_POSITION;
    24.                 float4 color : COLOR0;
    25.                 LIGHTING_COORDS(0,1)
    26.             };
    27.             fragmentInput vert (vertexInput i) {
    28.  
    29.                 fragmentInput o;
    30.                 o.pos = mul( UNITY_MATRIX_MVP, i.vertex);
    31.  
    32.                 float3 normalDirection = normalize( mul( float4( i.normal, 1.0), _World2Object).xyz);
    33.                 float3 lightDirection = normalize( _WorldSpaceLightPos0.xyz);
    34.                 float3 diffuse = _LightColor0.xyz * i.vertColor.rgb * max( 0.0, dot( normalDirection, lightDirection ));
    35.                 float3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.rgb * i.vertColor.rgb * 2;
    36.  
    37.                 o.color = half4(diffuse + ambientLight, 1.0);
    38.  
    39.                 TRANSFER_VERTEX_TO_FRAGMENT(o);
    40.  
    41.                 return o;
    42.             }
    43.  
    44.             half4 frag (fragmentInput i) : COLOR
    45.             {
    46.                 return i.color * LIGHT_ATTENUATION(i);
    47.             }
    48.             ENDCG
    49.         }
    50.     }
    51.     FallBack "Diffuse"
    52. }
    I am using:
    • Vertex colors directly from the vertex
    • 1 directional light
    • No textures

    This is how it looks:



    As you can see, I got the shadows wrong. Can someone explain what went wrong?
    Thank you.
     
  2. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    For what you've done wrong... I'd say, you recalculated lights yourself in vertex shader and it changed result.
    You can check 'show generated code' button on surface shader in unity editor. Then compare what you did with that. It's epic button ;)
    Unfortunately, it uses a lot of built in functions so completely copying it without calling them will not be easy.
     
    Last edited: Jan 28, 2016
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
  4. 0xsven

    0xsven

    Joined:
    Mar 5, 2015
    Posts:
    18
    Thank you. Those two answers helped a lot.
     
  5. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    It seems to me you're mainly just missing the ambient color.
     
  6. Ippokratis

    Ippokratis

    Joined:
    Oct 13, 2008
    Posts:
    1,521
  7. 0xsven

    0xsven

    Joined:
    Mar 5, 2015
    Posts:
    18
    Thank you. i did it.

    Here is my (unoptimized but perfectly working) fragment shader:

    Code (CSharp):
    1. Shader "Custom/SolidColorShader2" {
    2.     SubShader {
    3.        
    4.         Pass {
    5.             Tags {"LightMode" = "ForwardBase"}
    6.             CGPROGRAM
    7.             #pragma fragmentoption ARP_precision_hint_fastest
    8.            
    9.             #pragma vertex vert
    10.             #pragma fragment frag
    11.             #include "UnityCG.cginc"
    12.             #include "Lighting.cginc"
    13.             #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
    14.             #include "AutoLight.cginc"
    15.            
    16.             struct vertexInput
    17.             {
    18.                 float4 vertex : POSITION;
    19.                 float3 normal : NORMAL;
    20.                 fixed4 vertColor : COLOR;
    21.             };
    22.  
    23.             struct v2f
    24.             {
    25.                 float4 pos : SV_POSITION;
    26.                 float4 diffuse : COLOR0;
    27.                 float3 ambient : COLOR1;
    28.                 fixed4 color : TEXCOORD0;
    29.                 SHADOW_COORDS(1)
    30.             };
    31.  
    32.             v2f vert (vertexInput i) {
    33.                 v2f o;
    34.                 o.pos = mul( UNITY_MATRIX_MVP, i.vertex);
    35.  
    36.                 fixed3 worldNormal = UnityObjectToWorldNormal(i.normal);
    37.                 half3 lightDir = normalize( _WorldSpaceLightPos0.xyz);
    38.                 half3 nDotL = max(0, dot(worldNormal, lightDir));
    39.                
    40.                 o.ambient = ShadeSH9(half4(worldNormal,1));
    41.                 o.diffuse = float4(_LightColor0.rgb * nDotL, 1.0);
    42.                 o.color = i.vertColor;
    43.                 TRANSFER_SHADOW(o);
    44.  
    45.                 return o;
    46.             }
    47.  
    48.             half4 frag (v2f i) : COLOR
    49.             {
    50.                 fixed shadow = SHADOW_ATTENUATION(i);
    51.                 fixed3 lighting = i.diffuse * shadow + i.ambient;
    52.  
    53.                 return half4(i.color.rgb * lighting, 1.0);
    54.             }
    55.             ENDCG
    56.         }
    57.         // shadow casting support
    58.         UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
    59.     }
    60.     FallBack "Diffuse"
    61. }
    62.  
    Can anyone explain what ShadeSH9 does?
    It looks slightly different than UNITY_LIGHTMODEL_AMBIENT*2
     
  8. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    Code (CSharp):
    1. // normal should be normalized, w=1.0
    2. // output in active color space
    3. half3 ShadeSH9 (half4 normal)
    4. {
    5.     // Linear + constant polynomial terms
    6.     half3 res = SHEvalLinearL0L1 (normal);
    7.  
    8.     // Quadratic polynomials
    9.     res += SHEvalLinearL2 (normal);
    10.  
    11.     if (IsGammaSpace())
    12.         res = LinearToGammaSpace (res);
    13.  
    14.     return res;
    15. }
    16.  
    From Unity/Editor/Data/CGIncludes/UnityCG.cginc
    All methods used in it are also found in that file.
     
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Since the above doesn't really answer the question, ShadeSH9 and similar functions are spherical harmonic ambient vs. UNITY_LIGHTMODEL_AMBIENT which is just a single flat color ambient. Spherical harmonics weren't created for this, they're just a set of equations for evaluating differential equations. However at some point someone realized they could be used to store an approximation of diffuse lighting that is very cheap to calculate, store and evaluate.

    https://en.m.wikipedia.org/wiki/Spherical_harmonic_lighting
     
  10. 0xsven

    0xsven

    Joined:
    Mar 5, 2015
    Posts:
    18
    Shader doesnt work on my iphone. Works fine in the editor though. Any idea why?



     
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Look over the editor's console for a shader error or warning. It'll probably be something cryptic, and likely a case of doing something like float3(mySingleFloatValue).
     
  12. brownboot67

    brownboot67

    Joined:
    Jan 5, 2013
    Posts:
    375
    You need to cast here.
     
  13. 0xsven

    0xsven

    Joined:
    Mar 5, 2015
    Posts:
    18
    I checked both the editor and the device. No errors.

    Could you please elaborate more? I have tried:
    Code (CSharp):
    1. half3 lighting= i.diffuse* shadow + i.ambient;
    without success.
     
  14. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    try:
    fixed3 lighting = i.diffuse.rgb * fixed3(shadow, shadow, shadow) + i.ambient;
     
  15. brownboot67

    brownboot67

    Joined:
    Jan 5, 2013
    Posts:
    375
    bgolus is on it. i.diffuse is a float4 and you're making a fixed3. The cross compiler is pretty finnicky but over time you'll be more consistent from dealing with it.

    This will work too:
    fixed3 lighting = i.diffuse.rgb * shadow.rrr + i.ambient

    edit:
    or... fixed3 lighting = (i.diffuse.rgb * shadow) + i.ambient... i.diffuse is the problem here, shadow should be fine mostly in way you do it
     
  16. 0xsven

    0xsven

    Joined:
    Mar 5, 2015
    Posts:
    18
    @bgolus, @brownboot67

    Unfortunately your tips didn't solve the problem. I have tried all of them and many other variations. I also removed all unnecessary #pragma statemets.

    Really don't get whats wrong on the device :/