Unity投影贴花着色器:如何解决透明表面重复投影问题?
解决Unity投影器贴花在透明表面重复渲染的问题
嘿,我刚好碰到过类似的投影器着色器问题,给你梳理下解决方案!
问题根源
你的投影贴花在透明表面(比如带alpha的草地)重复渲染,核心原因是透明物体默认不会写入深度缓冲区。原着色器用的ZTest Less只会对比当前片元和深度缓冲区里的内容,但透明草地没把自己的深度写进去,深度缓冲区里还是后面路面的深度,所以投影会同时渲染草地和路面,造成叠加重复的效果。
修改后的完整着色器代码
Shader "Custom/color_projector" { Properties { _Color ("Tint Color", Color) = (1,1,1,1) _MainTex ("Cookie", 2D) = "gray" {} } Subshader { Tags {"Queue"="Transparent" "RenderType"="Transparent"} // 第一个Pass:仅写入投影的最近深度,不渲染颜色 Pass { ZTest Less ZWrite On ColorMask 0 // 关闭颜色输出,只更新深度缓冲区 CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 uvShadow : TEXCOORD0; float4 pos : SV_POSITION; }; float4x4 unity_Projector; v2f vert (float4 vertex : POSITION) { v2f o; o.pos = UnityObjectToClipPos(vertex); o.uvShadow = mul(unity_Projector, vertex); return o; } fixed4 frag (v2f i) : SV_Target { // 裁剪投影范围外的区域,避免无效深度写入 clip(i.uvShadow.w - 0.001); return fixed4(0,0,0,0); } ENDCG } // 第二个Pass:只渲染深度匹配的最近表面 Pass { ZTest Equal // 仅渲染和第一个Pass写入深度一致的片元 ZWrite Off ColorMask RGB Blend One OneMinusSrcAlpha Offset -1, -1 CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 uvShadow : TEXCOORD0; float4 pos : SV_POSITION; }; float4x4 unity_Projector; float4x4 unity_ProjectorClip; v2f vert (float4 vertex : POSITION) { v2f o; o.pos = UnityObjectToClipPos(vertex); o.uvShadow = mul(unity_Projector, vertex); return o; } sampler2D _MainTex; fixed4 _Color; fixed4 frag (v2f i) : SV_Target { // 裁剪投影范围外的区域 clip(mul(unity_ProjectorClip, i.pos).xyz); // 投影纹理采样 fixed4 tex = tex2Dproj(_MainTex, UNITY_PROJ_COORD(i.uvShadow)); return _Color * tex.a; } ENDCG } } FallBack "Projector/Light" }
关键修改说明
- 双Pass深度锁定:
- 第一个Pass专门负责把投影路径上最近表面的深度写入缓冲区(不管这个表面是透明还是不透明),同时关闭颜色输出,不影响视觉效果。
- 第二个Pass用
ZTest Equal精准匹配第一个Pass写入的深度,只渲染这个最近表面上的投影,彻底避免穿透到后面的物体。
- 裁剪优化:两个Pass都加入了裁剪逻辑,过滤掉投影范围外的无效片元,提升性能。
- 保留原有效果:第二个Pass保留了你原来的混合模式和透明处理,确保投影的alpha通道正常工作。
额外调试建议
- 确认你的草地材质
RenderType设置为Transparent,并且保持默认的ZWrite Off(透明材质不需要写深度)。 - 如果出现深度冲突的闪烁问题,可以微调第一个Pass的
Offset参数,比如改成Offset -2, -2。
内容的提问来源于stack exchange,提问作者Absinthe
相关产品推荐
相关产品推荐

