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): Shader "Grid/SimpleGrid" { Properties { _Color ("Color", Color) = (1,1,1,1) } SubShader { CGPROGRAM #pragma surface surf Lambert alpha struct Input { float3 worldPos; }; fixed4 _Color; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = _Color.rgb; o.Alpha = 0; fixed moduX = ((IN.worldPos.x + 1.2) % 2.4); fixed moduZ = ((IN.worldPos.z + 1.2) % 2.4); if (moduX < 0.1 || moduX > 2.3 || moduZ < 0.1 || moduZ > 2.3) { o.Alpha = 1; } } ENDCG } FallBack "Diffuse" } Thanks
I would suggest a simple quad with suitable tiled signed distance field texture of a + Then perform regular SDF style shading lookup for the right texel, you'll be able to have it anti-aliased for free as well as (optionally) keeping the line thickness over distance. https://assetstore.unity.com/packages/tools/utilities/sdf-toolkit-free-50191 etc... lots of resources!
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.
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.
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.
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.
Yes and no. Below is a breakdown of a common "if-less" grid. Code (csharp): float2 gridUV = worldPos.xz * 2.4; // this produces a per axis SDF float2 gridSawTooth = abs(frac(gridUV) * 2.0 - 1.0); // really just an if statement! float2 gridLines = step(gridSawTooth, 0.1 / 2.4); // the above function is identical to the below, which is a fast inline conditional // float2 gridLines = (0.1 / 2.4) >= gridSawTooth ? 1.0 : 0.0; // combine the two lines, also functionally an if! float grid = max(gridLines.x, gridLines.y); // also an if statement! clip(grid - 0.5); // the above line is identical to // if (grid - 0.5 < 0) discard; Here's a version that produces antialiased grids. http://iquilezles.org/www/articles/filterableprocedurals/filterableprocedurals.htm
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;
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).
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.