// 案例 22.8–22.9:网格贴花(Mesh Decal)——贴在表面上的一块真实网格,用深度偏移(Offset)避免 Z-Fighting Shader "Cookbook/Ch22/MeshDecal" { Properties { [MainTexture] _BaseMap ("Decal (RGBA)", 2D) = "white" {} [MainColor] _BaseColor ("Color", Color) = (1,1,1,1) [Toggle(_NORMALMAP)] _UseNormal ("Use Normal Map", Float) = 0 [NoScaleOffset][Normal] _BumpMap ("Normal Map", 2D) = "bump" {} _BumpScale ("Normal Scale", Range(0, 2)) = 1 _Smoothness ("Smoothness", Range(0, 1)) = 0.3 _OffsetFactor ("Depth Offset Factor", Float) = -1 _OffsetUnits ("Depth Offset Units", Float) = -1 [Toggle] _DebugColor ("Debug: Flat Color", Float) = 0 } SubShader { Tags { "RenderType"="Transparent" "RenderPipeline"="UniversalPipeline" "Queue"="Transparent-100" "IgnoreProjector"="True" } Pass { Name "MeshDecal" Tags { "LightMode"="UniversalForward" } //#region state Blend SrcAlpha OneMinusSrcAlpha ZWrite Off ZTest LEqual // 深度偏移:depth += factor × 斜率 + units × 最小可分辨深度单位 // 负值 = 往相机方向拉一点,让与底面共面的贴花稳定地赢下深度测试 Offset [_OffsetFactor], [_OffsetUnits] //#endregion HLSLPROGRAM #pragma vertex vert #pragma fragment frag #pragma shader_feature_local_fragment _NORMALMAP #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH #pragma multi_compile _ _FORWARD_PLUS #pragma multi_compile_instancing #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" CBUFFER_START(UnityPerMaterial) float4 _BaseMap_ST; half4 _BaseColor; half _BumpScale, _Smoothness; float _OffsetFactor, _OffsetUnits; half _DebugColor; CBUFFER_END TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); TEXTURE2D(_BumpMap); SAMPLER(sampler_BumpMap); struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float4 tangentOS : TANGENT; float2 uv : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct Varyings { float4 positionCS : SV_POSITION; float3 positionWS : TEXCOORD0; half3 normalWS : TEXCOORD1; half4 tangentWS : TEXCOORD2; float2 uv : TEXCOORD3; }; Varyings vert(Attributes v) { Varyings o; UNITY_SETUP_INSTANCE_ID(v); VertexPositionInputs p = GetVertexPositionInputs(v.positionOS.xyz); VertexNormalInputs n = GetVertexNormalInputs(v.normalOS, v.tangentOS); o.positionCS = p.positionCS; o.positionWS = p.positionWS; o.normalWS = n.normalWS; o.tangentWS = half4(n.tangentWS, v.tangentOS.w * GetOddNegativeScale()); o.uv = TRANSFORM_TEX(v.uv, _BaseMap); return o; } half4 frag(Varyings i) : SV_Target { half4 decal = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, i.uv) * _BaseColor; if (_DebugColor > 0.5) return half4(0.1, 0.8, 1.0, 0.55); // 调试:显示生成的网格覆盖范围 half3 n = normalize(i.normalWS); #if defined(_NORMALMAP) half3 nTS = UnpackNormalScale(SAMPLE_TEXTURE2D(_BumpMap, sampler_BumpMap, i.uv), _BumpScale); float3 bitangent = i.tangentWS.w * cross(i.normalWS, i.tangentWS.xyz); n = normalize(TransformTangentToWorld(nTS, half3x3(i.tangentWS.xyz, bitangent, i.normalWS))); #endif InputData inputData = (InputData)0; inputData.positionWS = i.positionWS; inputData.normalWS = n; inputData.viewDirectionWS = GetWorldSpaceNormalizeViewDir(i.positionWS); inputData.shadowCoord = TransformWorldToShadowCoord(i.positionWS); inputData.bakedGI = SampleSH(n); inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(i.positionCS); inputData.shadowMask = half4(1, 1, 1, 1); SurfaceData s = (SurfaceData)0; s.albedo = decal.rgb; s.smoothness = _Smoothness; s.occlusion = 1; s.alpha = decal.a; half3 color = UniversalFragmentPBR(inputData, s).rgb; return half4(color, decal.a); } ENDHLSL } } }