You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:13:58