源码 / shaders/Ch12/HairKK.glsl
shaders/Ch12/HairKK.glsl
// 头发:Kajiya-Kay 双高光(Scheuermann 2004 的实时版本)。 // 发丝方向默认取网格切线 T;Unity 球体的切线是“横向”的,所以球形发帽要勾选 Strand Along V(用副切线做发丝方向)。 // (GLSL 版,对应 Ch12/HairKK.shader) Shader "Cookbook/Ch12/HairKK" { SubShader { Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" } GLSLINCLUDE uniform vec4 _ShiftTex_ST; uniform vec4 _BaseColor, _PrimaryColor, _SecondaryColor; uniform float _StrandAlongV, _ShiftAmount; uniform float _PrimaryShift, _PrimaryExp, _SecondaryShift, _SecondaryExp; uniform sampler2D _ShiftTex; ENDGLSL Pass { Name "ForwardLit" Tags { "LightMode"="UniversalForwardOnly" } GLSLPROGRAM #pragma shader_feature_local_fragment _MODE_BLINNPHONG _MODE_SINGLE _MODE_DUAL #include "Ch12/Ch12Common.glsl" #ifdef VERTEX void main() { Ch12Vert(); } #endif #ifdef FRAGMENT struct MaterialCtx { vec3 N, V, T; vec3 albedo; float shiftNoise; // 每根发丝的随机偏移 float sparkle; // 第二高光的闪烁遮罩 }; vec3 ShadeOneLight(MaterialCtx m, Light light) { vec3 L = light.direction; vec3 H = normalize(L + m.V); float NoL = dot(m.N, L); // 漫反射:发丝是半透明细圆柱,Lambert 太“硬”,压一个下限让暗部不死黑 vec3 diffuse = m.albedo * mix(0.25, 1.0, saturate(NoL)); vec3 spec = vec3(0.0); #if defined(_MODE_BLINNPHONG) spec = _PrimaryColor.rgb * pow(saturate(dot(m.N, H)), _PrimaryExp * 0.5); #else vec3 t1 = AdvShiftTangent(m.T, m.N, _PrimaryShift + m.shiftNoise); spec = _PrimaryColor.rgb * AdvKajiyaKay(t1, H, _PrimaryExp); #if defined(_MODE_DUAL) vec3 t2 = AdvShiftTangent(m.T, m.N, _SecondaryShift + m.shiftNoise); spec += _SecondaryColor.rgb * m.sparkle * AdvKajiyaKay(t2, H, _SecondaryExp); #endif #endif // 背光面不给高光(发丝本身会挡住),用平滑过渡避免硬边 spec *= smoothstep(-0.1, 0.3, NoL); return (diffuse + spec * 0.5) * light.color * (light.distanceAttenuation * light.shadowAttenuation); } #include "Ch12/LightLoop.glsl" void main() { vec3 T, B, N; Ch12BuildFrame(T, B, N); vec3 V = GetWorldSpaceNormalizeViewDir(positionWS); vec2 screenUV = GetNormalizedScreenSpaceUV(gl_FragCoord); MaterialCtx m; m.N = N; m.V = V; m.T = _StrandAlongV > 0.5 ? B : T; // 发丝方向 float n = texture(_ShiftTex, TransformTex(uv, _ShiftTex_ST)).r; m.shiftNoise = (n - 0.5) * _ShiftAmount; m.sparkle = saturate(n * 1.6 - 0.3); // 发丝颜色也带一点随机明暗,看起来才像一缕缕的 m.albedo = _BaseColor.rgb * mix(0.7, 1.15, n); vec3 color = AccumulateDirectLighting(m, positionWS, screenUV); color += SampleSH(N) * m.albedo; color = MixFog(color, fogFactor); fragColor = vec4(color, 1.0); } #endif ENDGLSL } UsePass "Cookbook/Common/SHADOWCASTER" UsePass "Cookbook/Common/DEPTHONLY" UsePass "Cookbook/Common/DEPTHNORMALS" } }