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
相关产品推荐
相关产品推荐

