Search Unity

Grid Shader Critique

Discussion in 'Shaders' started by SpaceCadet, Mar 17, 2019.

  1. SpaceCadet

    SpaceCadet

    Joined:
    Jul 29, 2013
    Posts:
    16
    Hi all,

    I'm very new to shaders - just started learning about them yesterday. I made a shader to display a simple grid on a floor plane. I am concerned about the if statements in the shader - I have heard it's very bad to have conditional branching in shader scripts.

    Please let me know if you know a more efficient way of achieving the following:

    Code (csharp):
    1.  
    2. Shader "Grid/SimpleGrid"
    3. {
    4.     Properties
    5.     {
    6.         _Color ("Color", Color) = (1,1,1,1)
    7.     }
    8.     SubShader
    9.     {
    10.         CGPROGRAM
    11.         #pragma surface surf Lambert alpha
    12.         struct Input
    13.         {
    14.             float3 worldPos;
    15.         };
    16.         fixed4 _Color;
    17.         void surf (Input IN, inout SurfaceOutput o)
    18.         {          
    19.             o.Albedo = _Color.rgb;
    20.             o.Alpha = 0;
    21.             fixed moduX = ((IN.worldPos.x + 1.2) % 2.4);
    22.             fixed moduZ = ((IN.worldPos.z + 1.2) % 2.4);
    23.             if (moduX < 0.1 || moduX > 2.3 || moduZ < 0.1 || moduZ > 2.3) {
    24.                 o.Alpha = 1;
    25.             }
    26.         }
    27.         ENDCG
    28.     }
    29.     FallBack "Diffuse"
    30. }
    31.  
    Thanks :)
     
  2. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    SpaceCadet likes this.
  3. SpaceCadet

    SpaceCadet

    Joined:
    Jul 29, 2013
    Posts:
    16
    Thanks for the suggestion hippo, SDFs look really cool! I'm going to experiment.

    Another question - if you turn off a gameobject in the hierarchy, will the shader code still run?

    The reason I'm asking is that I turned off the plane with this shader attached and the profiler showed no change in performance as far as I could see.
     
  4. razzraziel

    razzraziel

    Joined:
    Sep 13, 2018
    Posts:
    396
    its hard to test with a single plane (especially on empty scene). if you want to see real difference use 100 or 1000 planes with that shader and try turn on & off.
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Don't be so afraid of using an if.

    You still shouldn't use them, but they're not the end of the world either.
     
  6. SpaceCadet

    SpaceCadet

    Joined:
    Jul 29, 2013
    Posts:
    16
    Can you think of a way to achieve write a grid shader based on worldPos that doesn't have to use an if statement?

    Just curious - I want to get better at writing shaders.
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Yes and no. Below is a breakdown of a common "if-less" grid.
    Code (csharp):
    1. float2 gridUV = worldPos.xz * 2.4;
    2.  
    3. // this produces a per axis SDF
    4. float2 gridSawTooth = abs(frac(gridUV) * 2.0 - 1.0);
    5.  
    6. // really just an if statement!
    7. float2 gridLines = step(gridSawTooth, 0.1 / 2.4);
    8.  
    9. // the above function is identical to the below, which is a fast inline conditional
    10. // float2 gridLines = (0.1 / 2.4) >= gridSawTooth ? 1.0 : 0.0;
    11.  
    12. // combine the two lines, also functionally an if!
    13. float grid = max(gridLines.x, gridLines.y);
    14.  
    15. // also an if statement!
    16. clip(grid - 0.5);
    17.  
    18. // the above line is identical to
    19. // if (grid - 0.5 < 0) discard;
    Here's a version that produces antialiased grids.
    http://iquilezles.org/www/articles/filterableprocedurals/filterableprocedurals.htm
     
  8. razzraziel

    razzraziel

    Joined:
    Sep 13, 2018
    Posts:
    396
    yea for example that max function is also a if statement. you can use simple statements like that without problem.


    grid = gridLinex.x > gridLines.y ? gridLines.x : gridLines.y;
     
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Yes and no. Functionally, yes, but the compiled shader function is a GPU assembly function, where as the other two I called out do literally produce identical code as using those inline if statements (and on most compilers even a literal if statement).
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    float value = step(foo, bar);

    float value = bar >= foo ? 1.0 : 0.0;

    float value = 0.0;
    if (bar >= foo) value = 1.0;


    Those will all produce the exact same bit of sm4 assembly (DirectX 10 shader assembly). Something like this:
    ge r0.x, v0.y, v0.x
    and r0.x, r0.x, l(0x3f800000)



    These lines do too, but because compilers are smart enough to realize the second should be the first.
    float value = max(foo, bar);

    float value = foo > bar ? foo : bar;

    // sm4 asm
    max r0.x, v0.y, v0.x

    One thing to note, sm4 asm does have an actual if, it just doesn't use it unless it really needs to, because it is more expensive.
     
    SpaceCadet and razzraziel like this.