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

Unity实现玩家墙后可见并保留阴影及指定遮挡对象方法

解决Unity玩家角色墙后显色+保留阴影+指定遮挡物方案

问题分析

你当前的Shader靠两个Pass实现墙后显色,但第一个Pass的ZTest Always会跳过深度检测,同时缺少阴影投射/接收的必要Pass,导致阴影丢失;另外无法区分哪些物体属于遮挡物,所有物体都会触发显色效果。

分步解决

1. 先配置遮挡物分层

在Unity编辑器中新建一个Layer(比如命名为Obstacle),把需要遮挡玩家的物体都设置到这个Layer上,不需要遮挡的物体保持默认Layer即可。

2. 替换为带阴影和分层遮挡的Shader

这个Shader整合了深度检测、阴影支持,且只对指定Layer的物体触发墙后显色:

Shader "N3K/Player_WithShadow"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Color("BehindWallColor", Color) = (0, 0, 0, 0.5)
        _ObstacleLayer("Obstacle Layer", Int) = 8 // 对应你创建的Obstacle层的编号
        _DepthThreshold("Depth Threshold", Float) = 0.01 // 防止深度精度问题的阈值
    }
    SubShader
    {
        Tags {"Queue"="Geometry" "RenderType"="Opaque"}
        LOD 100

        // 阴影投射Pass,必须添加才能让物体投射阴影
        Pass
        {
            Name "ShadowCaster"
            Tags {"LightMode"="ShadowCaster"}

            ZWrite On ZTest LEqual

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                V2F_SHADOW_CASTER;
            };

            v2f vert(appdata v)
            {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i)
            }
            ENDCG
        }

        // 主渲染Pass:处理纹理显示+墙后显色
        Pass
        {
            Tags {"LightMode"="ForwardBase"}

            ZWrite On
            ZTest LEqual
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 screenPos : TEXCOORD1; // 屏幕空间位置,用于采样深度纹理
                float depth : TEXCOORD2; // 玩家当前像素的深度值
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _Color;
            int _ObstacleLayer;
            float _DepthThreshold;
            sampler2D _CameraDepthTexture; // Unity内置的相机深度纹理

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.screenPos = ComputeScreenPos(o.vertex);
                // 获取当前像素的深度值(NDC空间)
                o.depth = -UnityObjectToViewPos(v.vertex).z * _ProjectionParams.w;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                // 采样原纹理颜色
                fixed4 texCol = tex2D(_MainTex, i.uv);

                // 采样深度纹理,获取当前屏幕位置的场景深度
                float sceneDepth = UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos)));
                sceneDepth = Linear01Depth(sceneDepth);

                // 将玩家像素深度转成0-1范围
                float playerDepth = Linear01Depth(i.depth);

                // 判断是否被指定Layer的物体遮挡:场景深度 < 玩家深度 - 阈值
                bool isBehindObstacle = sceneDepth < playerDepth - _DepthThreshold;

                // 如果被遮挡,混合原纹理和墙后颜色;否则直接返回原纹理
                if(isBehindObstacle)
                {
                    // 这里可以调整混合比例,比如用_Color的alpha控制透明度
                    return lerp(texCol, _Color, _Color.a);
                }
                else
                {
                    return texCol;
                }
            }
            ENDCG
        }

        // 阴影接收Pass,让物体接收其他物体的阴影
        Pass
        {
            Name "ShadowCollector"
            Tags {"LightMode"="ShadowCollector"}

            ZWrite Off
            ZTest LEqual

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcollector
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                V2F_SHADOW_COLLECTOR;
                float2 uv : TEXCOORD5;
            };

            v2f vert(appdata v)
            {
                v2f o;
                TRANSFER_SHADOW_COLLECTOR(o)
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                SHADOW_COLLECTOR_FRAGMENT(i)
            }
            ENDCG
        }
    }
    FallBack "Diffuse" // fallback到Diffuse,保证基础渲染和阴影兼容性
}

3. 材质与相机设置

  • 给玩家角色的材质替换为这个新Shader
  • 在材质面板中:
    • 设置BehindWallColor为你需要的墙后显示颜色(注意alpha值可以控制透明度)
    • 设置Obstacle Layer为你之前创建的Obstacle层的编号(在Unity的Layer设置里看对应数字,比如Layer 8就填8)
  • 确保主相机开启了深度纹理:在相机组件的Depth Texture选项中选择Depth或Depth+Normals

关键说明

  • 阴影支持:通过添加ShadowCaster和ShadowCollector两个Pass,直接复用Unity内置的阴影逻辑,保证物体正常投射和接收阴影
  • 分层遮挡:利用相机深度纹理,结合Layer判断,只有指定Layer的物体才会触发墙后显色效果
  • 深度阈值:_DepthThreshold用于避免因深度精度问题导致的错误显色,可根据场景调整(一般0.01足够)

内容的提问来源于stack exchange,提问作者Iyad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 07:12:44