源码 / Ch14/ToonOutline.shader
Ch14/ToonOutline.shader
// 背面外扩描边(Back-face Outline / Inverted Hull)。 // 第二个 Pass 只画背面,并把顶点沿法线推出去,露出来的一圈就是轮廓线。 Shader "Cookbook/Ch14/ToonOutline" { Properties { [MainColor] _BaseColor ("Base Color", Color) = (0.95, 0.75, 0.35, 1) _ShadeColor ("Shade Color", Color) = (0.7, 0.42, 0.3, 1) [Header(Outline)] _OutlineColor ("Outline Color", Color) = (0.12, 0.08, 0.1, 1) [KeywordEnum(Object, Clip)] _OutlineSpace ("Extrude Space", Float) = 1 _OutlineWidth ("Width (px, Clip mode)", Range(0, 10)) = 3 _OutlineWidthOS ("Width (object units, Object mode)", Range(0, 0.1)) = 0.02 [KeywordEnum(Mesh, Tangent, VertexColor)] _OutlineNormal ("Extrude Normal Source", Float) = 0 } SubShader { Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" } HLSLINCLUDE #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl" CBUFFER_START(UnityPerMaterial) half4 _BaseColor, _ShadeColor, _OutlineColor; half _OutlineWidth, _OutlineWidthOS; CBUFFER_END ENDHLSL Pass { Name "ToonForward" Tags { "LightMode"="UniversalForwardOnly" } HLSLPROGRAM #pragma vertex ToonVert #pragma fragment frag #pragma multi_compile_fog #pragma multi_compile_instancing #include "Assets/Cookbook/Ch14/ToonCommon.hlsl" half4 frag(ToonVaryings i) : SV_Target { half3 N = normalize(i.normalWS); Light light = GetMainLight(); half band = ToonBand(dot(N, light.direction) * 0.5 + 0.5, 0.5, 0.02); half3 color = lerp(_ShadeColor.rgb, _BaseColor.rgb, band) * light.color; color += SampleSH(half3(0, 0, 0)) * _BaseColor.rgb * 0.3; return half4(MixFog(color, i.fogFactor), 1); } ENDHLSL } Pass { Name "Outline" // URP 的不透明队列会画 UniversalForward 和 SRPDefaultUnlit 两种 LightMode, // 所以第二个 Pass 用 SRPDefaultUnlit 就能自动被画到,不需要 Renderer Feature Tags { "LightMode"="SRPDefaultUnlit" } Cull Front // 只画背面 ZWrite On HLSLPROGRAM #pragma vertex vert #pragma fragment frag #pragma shader_feature_local_vertex _OUTLINESPACE_OBJECT _OUTLINESPACE_CLIP #pragma shader_feature_local_vertex _OUTLINENORMAL_MESH _OUTLINENORMAL_TANGENT _OUTLINENORMAL_VERTEXCOLOR #pragma multi_compile_fog #pragma multi_compile_instancing #include "Assets/Cookbook/Ch14/ToonCommon.hlsl" struct OutlineVaryings { float4 positionCS : SV_POSITION; float fogFactor : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; // 取“用来外扩的法线”:原始法线 / 烘焙在切线里的平滑法线 / 烘焙在顶点色里的切线空间平滑法线 float3 GetOutlineNormalOS(ToonAttributes v) { #if defined(_OUTLINENORMAL_TANGENT) return v.tangentOS.xyz; // 物体空间平滑法线 #elif defined(_OUTLINENORMAL_VERTEXCOLOR) float3 nTS = v.color.rgb * 2.0 - 1.0; // 切线空间平滑法线 float3 bitangentOS = cross(v.normalOS, v.tangentOS.xyz) * v.tangentOS.w; return normalize(nTS.x * v.tangentOS.xyz + nTS.y * bitangentOS + nTS.z * v.normalOS); #else return v.normalOS; #endif } OutlineVaryings vert(ToonAttributes v) { OutlineVaryings o; UNITY_SETUP_INSTANCE_ID(v); UNITY_TRANSFER_INSTANCE_ID(v, o); float3 n = GetOutlineNormalOS(v); #if defined(_OUTLINESPACE_OBJECT) o.positionCS = OutlinePositionCS_ObjectSpace(v.positionOS.xyz, n, _OutlineWidthOS); #else o.positionCS = OutlinePositionCS(v.positionOS.xyz, n, _OutlineWidth); #endif o.fogFactor = ComputeFogFactor(o.positionCS.z); return o; } half4 frag(OutlineVaryings i) : SV_Target { return half4(MixFog(_OutlineColor.rgb, i.fogFactor), 1); } ENDHLSL } Pass { Name "ShadowCaster" Tags { "LightMode"="ShadowCaster" } ZWrite On ZTest LEqual ColorMask 0 Cull Back HLSLPROGRAM #pragma vertex ShadowPassVertex #pragma fragment ShadowPassFragment #pragma multi_compile_instancing #pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW // URP 自带的阴影 Pass 只在 _ALPHATEST_ON 时才读 _BaseMap,其余部分与材质无关,直接复用 #include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl" ENDHLSL } Pass { Name "DepthOnly" Tags { "LightMode"="DepthOnly" } ZWrite On ColorMask R Cull Back HLSLPROGRAM #pragma vertex DepthOnlyVertex #pragma fragment DepthOnlyFragment #pragma multi_compile_instancing #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl" ENDHLSL } Pass { Name "DepthNormals" Tags { "LightMode"="DepthNormals" } ZWrite On Cull Back HLSLPROGRAM #pragma vertex DepthNormalsVertex #pragma fragment DepthNormalsFragment #pragma multi_compile_instancing #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthNormalsPass.hlsl" ENDHLSL } } }