Shaders must die, part 3
Continuing the series (see Part 1, Part 2)…
Got different lighting models (BRDFs) working. Without further ado, code snippets that produce real actual working shaders that work with lights & shadows and whatnot:
Simple Lambert (single color):
Properties
Color _Color
EndProperties
Surface
o.Albedo = _Color;
EndSurface
Lighting Lambert
Let’s add a texture:
Properties
2D _MainTex
Color _Color
EndProperties
Surface
o.Albedo = SAMPLE(_MainTex) * _Color;
EndSurface
Lighting Lambert
Change light model to Half-Lambert (a.k.a. wrapped diffuse):
// ...everything the same
Lighting HalfLambert
Blinn-Phong, with constant exponent & constant specular color, modulated by gloss map in main texture’s alpha:
Properties
2D _MainTex
Color _Color
Color _SpecColor
Float _Exponent
EndProperties
Surface
half4 col = SAMPLE(_MainTex);
o.Albedo = col * _Color;
o.Specular = _SpecColor.rgb * col.a;
o.Exponent = _Exponent;
EndSurface
Lighting BlinnPhong
The same Blinn-Phong, with added normal map:
Properties
2D _MainTex
2D _BumpMap
Color _Color
Color _SpecColor
Float _Exponent
EndProperties
Surface
half4 col = SAMPLE(_MainTex);
o.Albedo = col * _Color;
o.Specular = _SpecColor.rgb * col.a;
o.Exponent = _Exponent;
o.Normal = SAMPLE_NORMAL(_BumpMap);
EndSurface
Lighting BlinnPhong
I also made an illustrative-style BRDF (see Illustrative Rendering in Team Fortress 2), but that only requires above sample to have “Lighting TF2” at the end.
Another thing I tried is surface that has Albedo dependent on a viewing angle, similar to Layered Car Paint Shader. It works:
Properties
2D _MainTex
2D _BumpMap
2D _SparkleTex
Float _Sparkle
Color _PrimaryColor
Color _HighlightColor
EndProperties
Surface
half4 main = SAMPLE(_MainTex);
half3 normal = SAMPLE_NORMAL(_BumpMap);
half3 normalN = normalize(SAMPLE_NORMAL(_SparkleTex));
half3 ns = normalize (normal + normalN * _Sparkle);
half3 nss = normalize (normal + normalN);
i.viewDir = normalize(i.viewDir);
half nsv = max(0,dot(ns, i.viewDir));
half3 c0 = _PrimaryColor.rgb;
half3 c2 = _HighlightColor.rgb;
half3 c1 = c2 * 0.5;
half3 cs = c2 * 0.4;
half3 tone =
c0 * nsv +
c1 * (nsv*nsv) +
c2 * (nsv*nsv*nsv*nsv) +
cs * pow(saturate(dot(nss,i.viewDir)), 32);
main.rgb *= tone;
o.Albedo = main;
o.Normal = normal;
EndSurface
Lighting Lambert
Up next:
-
How and where emissive terms should be placed. I cautiously omitted all emissive terms from the above examples (so my layered car shader is without reflections right now).
-
Where should things like rim lighting go? I’m not sure if it’s a surface property (increasing albedo/emission with angle) or a lighting property (a back light).
My impressions so far:
-
I like that I don’t have to write down vertex-to-fragment structures or the vertex shader. In most cases all the vertex shader does is transform stuff and pass it down to later stages, plus occasional computations that are linear over the triangle. No good reason to write it by hand.
-
I like that the above shaders do not deal with how the rendering is actually done. For Unity’s case, I’m compiling them into single pass per light forward renderer, but they should just work with multiple lights per pass, deferred etc. Of course, that still has to be proven!
So far so good.