1. 项目背景与核心需求拆解
1.1 为什么需要Projector来做阴影投射
在Unity场景里做阴影,大多数人第一反应是直接用实时阴影,也就是在光源组件里把Shadow Type打开,让引擎自己算。这个方案在中小型场景里确实省事,但一旦场景规模上去、光源数量变多,性能开销就会变得非常难看。尤其是移动端项目,实时阴影几乎是性能杀手,Draw Call和Overdraw都会明显上升。
那用Lightmap烘焙行不行?行,但烘焙的代价是静态的。场景里一旦有动态物体经过,或者光源需要移动,烘焙出来的阴影就对不上了。而且烘焙一张高质量Lightmap的时间成本很高,迭代起来很痛苦。
Projector这个组件就是在这种夹缝里生存的。它本质上是一个投影器,可以把一张贴图按照指定的方向和范围投射到场景表面上。你可以把它理解成一个"幻灯片放映机",它把一张带透明通道的阴影贴图打到地面、墙面、模型表面上,从视觉上模拟出阴影效果。它不参与光照计算,不走实时阴影管线,纯粹是贴图投影,所以性能开销非常可控。
这个方案最适合的场景包括:固定视角的塔防游戏、俯视角策略游戏、需要大量角色阴影但不想开实时阴影的移动端项目、以及需要特殊形状阴影(比如云影、树影、窗户栅格影)的效果实现。如果你正在做这类项目,Projector方案值得认真研究一下。
1.2 Projector阴影的核心原理
Projector的工作方式和相机有点像,但方向反过来。相机是把三维场景拍成二维图像,Projector是把一张二维贴图"照"回三维场景。它有一个投影范围,通常用一个视锥体或者正交盒子来定义,范围内的物体表面会接收到这张贴图。
具体到阴影投射,我们需要一张带Alpha通道的阴影贴图。贴图上有阴影的地方Alpha值高(或者低,取决于混合模式),没有阴影的地方透明。Projector把这张贴图投射到场景模型表面后,通过合适的混合模式,就能让模型表面出现暗色区域,看起来就像被什么东西挡住了光。
这里有个关键点:Projector投射的是贴图,不是真正的几何阴影。所以它不会因为场景物体的遮挡关系自动变化。也就是说,如果场景里多了一个箱子挡在光源和地面之间,Projector不会自动在地面上多出一块阴影。你需要手动控制Projector的开关或者贴图切换来模拟这种变化。这是它最大的局限,也是使用时最容易踩坑的地方。
1.3 适用场景与不适用场景对比
| 场景类型 | 是否适合Projector | 原因 |
|---|---|---|
| 固定视角塔防 | 非常适合 | 视角固定,阴影贴图可以预先做好,性能极佳 |
| 俯视角策略 | 适合 | 阴影方向统一,可以用一张通用贴图 |
| 移动端大量角色 | 适合 | 比实时阴影省太多,角色脚下用简单圆形阴影即可 |
| 第一人称探索 | 不太适合 | 视角变化大,Projector边缘容易穿帮 |
| 动态光源场景 | 不适合 | 光源一动,投影方向就对不上了 |
| 需要精确遮挡 | 不适合 | Projector不感知遮挡关系,需要额外逻辑 |
2. 阴影贴图的制作与参数设计
2.1 阴影贴图的基本要求
Projector用的阴影贴图,和普通贴图不太一样。它需要一张带Alpha通道的RGBA贴图,通常用PNG格式。RGB通道决定投影出来的颜色,Alpha通道决定透明度。做阴影的话,RGB一般填纯黑或者深灰色,Alpha通道控制哪里显示阴影、哪里透明。
贴图的尺寸不用太大,256x256或者512x512就够用了。因为Projector投射到场景表面后,贴图会被拉伸,太高的分辨率反而浪费显存。我一般用512x512,边缘用柔和的渐变过渡,这样投影出来的阴影边缘不会太硬。
贴图的边缘处理很关键。如果贴图边缘是硬切的黑边,投影到地面上就会看到明显的方形边界,非常穿帮。解决办法是在贴图四周留出足够的透明过渡区域,让阴影从中心向外逐渐淡出。用Photoshop或者任何绘图工具,把阴影形状画在中间,四周用软笔刷擦出渐变即可。
2.2 不同阴影形状的制作思路
圆形阴影是最常用的,角色脚下、道具底部都可以用。制作方法很简单,新建一个正方形画布,用椭圆选框工具画一个圆,填充黑色,然后羽化边缘,羽化半径根据贴图尺寸来定,512的贴图用30到50像素的羽化就差不多。
长条形阴影适合柱子、树木这类细长物体。画一个细长的椭圆或者矩形,同样做羽化处理。注意长条阴影的方向要和Projector的投射方向匹配,不然投影出来方向是歪的。
特殊形状阴影比如窗户栅格、树叶缝隙,可以用图案叠加或者蒙版来做。这类阴影通常用于营造氛围,比如阳光透过窗户在地面上形成的光斑和阴影交替效果。制作时先把基础形状画好,然后用图案画笔或者蒙版叠加出细节。
注意:贴图的Alpha通道必须是独立的,不能依赖RGB的亮度来做透明。有些绘图工具默认导出时Alpha是拼合的,需要在导出设置里确认"存储透明度"或者"Alpha通道"选项是打开的。
2.3 贴图导入Unity的参数设置
贴图导入Unity后,在Inspector面板里有一堆参数要调。最重要的几个:
- Texture Type:选Default或者Sprite都行,Projector不挑,但选Default更通用。
- Alpha Source:选From Gray Scale或者Input Texture Alpha,取决于你的贴图是怎么做的。如果Alpha是独立通道,选Input Texture Alpha。
- Alpha Is Transparency:这个要勾上,不然边缘会有黑边。
- Wrap Mode:选Clamp,不要选Repeat。因为Projector投射时如果贴图边缘是Repeat模式,超出范围的部分会重复贴图,导致地面出现莫名其妙的重复阴影。
- Filter Mode:选Bilinear,阴影边缘会柔和一些。Point模式太硬,Trilinear没必要。
- Max Size:512或者1024足够了,再大就是浪费。
这些参数看着琐碎,但每一个都会影响最终效果。我见过有人贴图边缘一直有黑边,查了半天发现是Alpha Is Transparency没勾。也见过地面阴影重复出现,结果是Wrap Mode选成了Repeat。这些坑我都踩过,你直接按上面的设置来就行。
3. Projector组件的配置与材质设置
3.1 Projector组件的核心参数详解
在场景里新建一个空物体,Add Component搜索Projector添加上去。这个组件有几个关键参数:
Orthographic:勾选后投影范围是正交盒子,不勾是透视视锥。做阴影一般勾上,因为正交投影的阴影方向一致,不会出现近大远小的透视变形。但如果你要模拟点光源的阴影,比如灯泡在桌子正上方,那就不勾,用透视模式。
Orthographic Size:正交模式下投影盒子的半高。这个值决定了阴影投射的范围大小。比如你想让阴影覆盖角色周围2米的范围,Orthographic Size就设1左右。具体数值要根据场景比例来调,没有固定标准。
Near Clip Plane / Far Clip Plane:投影的最近和最远距离。Near Clip Plane不要设0,设0.1或者0.2,避免投影器自身位置出现异常。Far Clip Plane根据你需要投射的距离来定,比如从天花板投射到地面,距离3米,那Far Clip Plane至少设3.5。
Aspect Ratio:投影的宽高比。默认是1,也就是正方形。如果你想让阴影在某个方向上拉长,可以调这个值。比如角色阴影想稍微扁一点,Aspect Ratio设1.2。
Ignore Layers:这个非常重要。Projector默认会投射到所有层上,包括角色自身。如果你不想让角色自己身上出现阴影贴图,就要把角色的Layer加到Ignore Layers里。通常我会建一个专门的Layer叫"ShadowReceiver",只让地面和场景模型接收投影,角色和特效都不接收。
3.2 投影材质的选择与自定义Shader
Projector需要一个材质来定义投影的混合方式。Unity自带几个Projector材质,在Effects文件夹里可以找到。常用的有:
- Projector Light:做加法混合,适合做光斑。
- Projector Multiply:做乘法混合,适合做阴影,因为乘法会让底色变暗。
- Projector Shadow:专门做阴影的,但效果比较基础。
我一般不用自带的,而是自己写一个简单的Shader。原因有两个:一是自带Shader的混合模式不够灵活,二是自己写的可以加一些额外控制,比如根据距离淡出、根据角度调整强度。
下面是一个基础的Projector阴影Shader,用的是Multiply混合:
Shader "Custom/ProjectorShadow" { Properties { _ShadowTex ("Cookie", 2D) = "gray" {} _Intensity ("Intensity", Range(0, 1)) = 0.5 } Subshader { Tags { "Queue"="Transparent" } Pass { ZWrite Off Offset -1, -1 Blend DstColor Zero CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 pos : SV_POSITION; float4 uvShadow : TEXCOORD0; }; float4x4 unity_Projector; sampler2D _ShadowTex; float _Intensity; v2f vert (appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uvShadow = mul(unity_Projector, v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 tex = tex2Dproj(_ShadowTex, i.uvShadow); fixed4 col; col.rgb = lerp(fixed3(1,1,1), tex.rgb, _Intensity); col.a = tex.a * _Intensity; return col; } ENDCG } } }这个Shader的核心逻辑是:用unity_Projector矩阵把顶点坐标转换到投影空间,然后用tex2Dproj采样贴图。Blend DstColor Zero是乘法混合,让投影区域变暗。_Intensity控制阴影强度,0就是没阴影,1就是全黑。
提示:
Offset -1, -1是为了避免投影面和接收面在同一深度时产生Z-fighting闪烁。这个偏移值可以根据实际情况微调。
3.3 材质参数的实际调试经验
把Shader挂到材质上,然后把材质拖到Projector组件的Material槽里。这时候场景里应该能看到投影效果了。如果看不到,检查几个地方:
第一,Projector的Far Clip Plane是不是太近了,导致投影范围没覆盖到目标表面。第二,Ignore Layers是不是把接收投影的层给排除了。第三,材质的Shader有没有编译错误,在Console里看一下。
调试阴影强度的时候,_Intensity不要设太高。0.3到0.6之间比较自然,太黑了会显得很假。真实阴影的边缘是有过渡的,不是一刀切的黑色。如果贴图本身边缘羽化做得好,强度低一点反而更真实。
还有一个细节:Projector的旋转角度会影响投影形状。如果Projector是斜着投射的,阴影会被拉长。这是正常的透视效果,但如果你不想要这种拉长,就把Projector的旋转和光源方向对齐,或者用正交模式减少变形。
4. 完整实操流程与场景搭建
4.1 场景准备与层级规划
先规划好场景的层级结构。我一般会建这么几个空物体来组织:
_Projectors:放所有Projector物体。_ShadowReceivers:放需要接收阴影的场景模型。_ShadowCasters:放投射阴影的角色或道具(逻辑上的,不是必须)。
层级规划的好处是,后面用代码控制Projector开关或者批量调整参数时,可以直接遍历父物体下的所有子物体,不用一个个找。
接收阴影的模型,Layer设成"ShadowReceiver"。这个Layer要在Projector的Ignore Layers里排除掉,意思是"不要忽略这一层",也就是要投射到这一层上。Projector的Ignore Layers逻辑是:列表里的层会被忽略,不在列表里的层会接收投影。所以要把不需要接收投影的层(比如角色、UI、特效)加进去。
4.2 创建Projector并配置参数
新建空物体,命名比如"ShadowProjector_Player",位置放在角色正上方。旋转角度根据光源方向来定,如果光源是从左上角打过来的,Projector就朝右下角倾斜。
添加Projector组件,参数按下面的来:
| 参数 | 值 | 说明 |
|---|---|---|
| Orthographic | 勾选 | 正交投影,阴影不变形 |
| Orthographic Size | 1.5 | 覆盖角色周围3米范围 |
| Near Clip Plane | 0.1 | 避免自身位置异常 |
| Far Clip Plane | 10 | 足够投射到地面 |
| Aspect Ratio | 1 | 正方形投影 |
| Ignore Layers | 角色层、UI层、特效层 | 这些层不接收投影 |
材质用上面写的Custom/ProjectorShadow,贴图用做好的圆形阴影贴图。Intensity先设0.5,后面根据效果微调。
把Projector的位置和角色绑定,可以直接把Projector作为角色的子物体,这样角色移动时阴影跟着走。但要注意,如果角色有缩放,Projector也会被缩放,投影范围会跟着变。如果不想这样,就用代码每帧同步Projector的位置,不设为子物体。
4.3 让阴影跟随角色移动的代码实现
如果Projector是角色的子物体,位置同步是自动的,但旋转和缩放可能会受角色影响。更稳妥的做法是用一个独立的Projector,每帧把它的XZ平面位置同步到角色脚下,Y轴保持固定高度。
using UnityEngine; public class ShadowFollower : MonoBehaviour { public Transform target; public float height = 5f; public float followSpeed = 10f; void LateUpdate() { if (target == null) return; Vector3 targetPos = new Vector3(target.position.x, height, target.position.z); transform.position = Vector3.Lerp(transform.position, targetPos, followSpeed * Time.deltaTime); } }把这个脚本挂到Projector物体上,target拖角色进去。height设成Projector离地面的高度,比如5米。followSpeed控制跟随的平滑度,设10左右比较跟手,不会太飘。
用LateUpdate而不是Update,是因为角色的位置可能在Update里被修改,LateUpdate能保证在角色移动之后再同步Projector位置,避免阴影和角色之间出现一帧的延迟。
4.4 多个角色共用Projector的优化方案
如果场景里有几十个角色,每个角色一个Projector,Draw Call会上去。虽然Projector本身开销不大,但数量多了也扛不住。优化方案有两种:
第一种是合并Projector。如果角色比较集中,可以用一个大的Projector覆盖所有角色,贴图用一张包含多个阴影的图。但这样阴影位置就固定了,角色移动时阴影不会跟着动,只适合静态或者小范围移动的场景。
第二种是用Command Buffer或者Graphics.DrawMesh手动绘制。这个方案复杂一些,但性能最好。核心思路是把所有角色的阴影贴图合并到一张大图里,然后用一个Projector投射出去。不过这个方案需要写不少代码,适合对性能有极致要求的项目。
对于大多数项目,我建议先用每个角色一个Projector的方案,跑起来看Profiler。如果Draw Call在可接受范围内,就不用折腾。如果确实高了,再考虑合并方案。过早优化是万恶之源,这句话在Unity性能优化里同样适用。
5. 常见问题与排查技巧实录
5.1 阴影边缘出现硬切或黑边
这是最常见的问题。原因通常有三个:贴图边缘没有做羽化、Alpha Is Transparency没勾、或者Wrap Mode设成了Repeat。
排查顺序:先看贴图本身,在图片查看器里放大边缘,看有没有渐变过渡。如果没有,回绘图工具重新做羽化。如果贴图没问题,看Unity导入设置,确认Alpha Is Transparency勾上了,Wrap Mode是Clamp。这两个都对了,再看材质的Blend模式,Multiply模式对边缘比较敏感,可以换成自定义Shader里的lerp方式,边缘会柔和很多。
还有一个隐藏原因:Projector的Orthographic Size设得太小,导致贴图边缘正好落在投影范围的边界上,被硬切了。解决办法是把Orthographic Size调大一点,让贴图边缘在投影范围内部,不要贴边。
5.2 阴影投射到了不该投射的物体上
比如角色自己身上出现了阴影贴图,或者UI上出现了阴影。这是因为Ignore Layers没配好。
Projector的Ignore Layers逻辑容易搞反。记住:列表里的层会被忽略,不在列表里的层会接收投影。所以要把所有"不想接收投影"的层加进去。角色层、UI层、特效层、水层,这些通常都要加。
如果加了之后还是投射到了角色身上,检查角色的Layer是不是真的设成了你加进去的那个层。有时候角色父物体是一个层,子物体是另一个层,Projector会按子物体的层来判断。确保所有不需要接收投影的物体,其Layer都在Ignore Layers列表里。
5.3 阴影闪烁或Z-fighting
两个面在同一深度时会出现闪烁,这是Z-fighting。Projector的投影面和接收面如果深度太接近,就会闪。
解决办法是在Shader里加Offset。上面给的Shader里有Offset -1, -1,这个就是用来偏移深度的。如果还闪,把值改成-2, -2或者-3, -3。偏移值越大,投影面越"靠前",越不容易和接收面冲突。但偏移太大也会导致投影穿透其他物体,需要平衡。
另一个办法是把Projector的Near Clip Plane调大一点,让投影从离接收面稍远的地方开始。比如Near Clip Plane从0.1改成0.5,投影的起始深度就远了,不容易和接收面重叠。
5.4 阴影在移动端表现异常
移动端对Shader的支持和PC不太一样。有些在PC上正常的Shader,到移动端就出问题。常见的是tex2Dproj在部分移动GPU上不支持,或者精度不够导致阴影边缘有锯齿。
解决办法是用tex2D代替tex2Dproj,手动做透视除法。把o.uvShadow = mul(unity_Projector, v.vertex)改成先算float4 uv = mul(unity_Projector, v.vertex),然后在fragment里用uv.xy / uv.w来采样。这样兼容性更好。
精度问题的话,把Shader里的fixed4改成half4或者float4,提高精度。移动端GPU对fixed精度的支持参差不齐,用half或float更保险。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 |
|---|---|---|
| 阴影边缘硬切 | 贴图未羽化 / Wrap Mode错误 | 检查贴图边缘和导入设置 |
| 阴影投射到角色身上 | Ignore Layers未配置 | 把角色层加入Ignore Layers |
| 阴影闪烁 | Z-fighting | 加Offset或调大Near Clip Plane |
| 移动端阴影异常 | Shader兼容性 / 精度不足 | 换tex2D手动除法 / 提高精度 |
| 阴影位置偏移 | Projector位置未同步 | 用LateUpdate同步位置 |
| 阴影颜色太深 | Intensity过高 | 降低Intensity到0.3-0.6 |
| 投影范围不够 | Far Clip Plane太近 | 调大Far Clip Plane |
6. 进阶技巧与性能优化建议
6.1 用Render Texture动态生成阴影贴图
静态贴图只能做固定形状的阴影。如果想让阴影根据场景实时变化,比如角色举起武器时阴影形状跟着变,可以用Render Texture。
思路是:在角色上方放一个正交相机,朝下拍,只拍角色的阴影投射层。相机的输出给一张Render Texture,这张Render Texture再作为Projector的贴图。这样角色动,阴影贴图就跟着动。
这个方案的开销比静态贴图大,因为多了一个相机渲染。但比实时阴影还是省很多。适合角色动作丰富、对阴影精度要求不高的项目。
配置步骤:新建一个Camera,设为Orthographic,Size和Projector的Orthographic Size一致。相机的Culling Mask只勾选角色层,Clear Flags设为Solid Color,背景色设白色(因为阴影贴图是黑底白透明,白色背景对应透明)。然后新建一张Render Texture,拖到相机的Target Texture上。最后把这张Render Texture拖到Projector材质的_ShadowTex上。
6.2 用Shader Graph做更灵活的阴影效果
如果项目用的是URP或者HDRP,可以用Shader Graph来做Projector的Shader,比手写HLSL直观很多。Shader Graph里有Projector相关的节点,可以直接连。
用Shader Graph的好处是可以很方便地加一些效果,比如根据距离淡出、根据视角角度调整强度、叠加噪声让阴影边缘不规则。这些效果手写HLSL也能做,但Shader Graph可视化编辑,调起来更快。
不过要注意,URP下的Projector和内置管线不太一样。URP没有内置的Projector组件,需要用Decal Projector或者自己写一个。Decal Projector是URP专门做贴花投影的,用法和Projector类似,但参数和Shader写法不同。如果项目是URP,建议直接用Decal Projector,省事。
6.3 性能对比与选型建议
把Projector阴影和实时阴影做个对比:
| 对比项 | Projector阴影 | 实时阴影 |
|---|---|---|
| Draw Call | 每个Projector 1-2个 | 每个光源多个 |
| 内存占用 | 一张贴图 | 阴影贴图RT |
| 移动端性能 | 很好 | 一般到差 |
| 动态遮挡 | 不支持 | 支持 |
| 阴影精度 | 取决于贴图 | 取决于分辨率 |
| 适用场景 | 固定视角、大量角色 | 动态光源、精确遮挡 |
选型建议:如果项目是移动端、视角固定、角色数量多,优先用Projector。如果项目是PC端、光源动态、需要精确遮挡,用实时阴影。如果两者都有,可以混合使用:主角用实时阴影,小怪用Projector阴影。
6.4 一些踩过的坑和实操心得
第一个坑:Projector的贴图不要用带Mipmap的。Mipmap在远处会降低贴图分辨率,导致阴影边缘模糊甚至消失。在贴图导入设置里把Generate Mip Maps关掉。
第二个坑:Projector的旋转角度不要和接收面平行。如果Projector垂直朝下,接收面是水平地面,投影是正常的。但如果接收面是斜面,Projector垂直朝下,投影会被拉长。解决办法是让Projector的朝向和接收面的法线方向有一定角度,或者用多个Projector分别投射不同斜面。
第三个坑:场景切换时Projector的材质会丢。如果Projector是动态加载的,材质要手动赋值,不能依赖Inspector里的引用。用Resources.Load或者Addressables加载材质,然后projector.material = loadedMaterial。
第四个坑:Projector在Scene视图里能看到投影范围线框,但Game视图里看不到阴影。这通常是材质的Shader编译错误,或者贴图的Alpha通道有问题。在Console里看有没有报错,没有的话检查贴图的Alpha通道是不是全白或者全黑。
第五个坑:多个Projector重叠时,阴影会叠加变黑。因为Multiply混合是累乘的,两个0.5的阴影叠在一起就是0.25,更黑。解决办法是控制Projector的投射范围不要重叠,或者用Stencil Buffer做遮罩,让重叠区域只算一次。Stencil方案复杂一些,但效果最好。
7. 不同项目类型下的适配方案
7.1 塔防游戏的阴影方案
塔防游戏视角固定,通常是俯视角或者斜45度。这种场景最适合Projector阴影。所有角色的阴影可以用一张统一的圆形贴图,Projector的旋转角度和光源方向一致,所有角色共用同一个Projector参数。
优化做法:把所有角色的阴影合并到一个Projector里。用一个大的Projector覆盖整个战场,贴图用一张包含所有角色阴影位置的大图。角色移动时,更新贴图上对应位置的阴影。这个方案Draw Call极低,但需要写代码动态更新贴图。如果角色数量不多(20个以内),也可以每个角色一个Projector,性能也扛得住。
7.2 移动端角色阴影的轻量方案
移动端最省事的方案是:每个角色脚下放一个面片(Quad),面片上贴一张圆形阴影贴图,用透明混合。这个方案连Projector都不需要,Draw Call就是一个面片。缺点是阴影只能贴在地面上,如果地面有起伏,面片会穿模。
Projector方案比面片方案好的地方在于,它能贴合地面起伏。如果移动端项目的地面是平坦的,用面片就够了。如果地面有坡度,用Projector。如果地面既有坡度又有高低差,Projector的Far Clip Plane要调好,确保投影能覆盖到低处的地面。
7.3 室内场景的窗户光影效果
室内场景经常需要模拟阳光透过窗户在地面上形成的光斑和阴影。这个效果用Projector做非常合适。贴图做成窗户栅格的形状,Projector放在窗户位置,朝室内投射。
关键点是Projector的旋转角度要和阳光方向一致。如果阳光是从左上角斜射进来,Projector就朝右下角倾斜。倾斜角度根据太阳高度角来算,一般45度左右比较自然。
贴图的颜色不要用纯黑,用深灰色或者带一点蓝色的暗色,模拟环境光的影响。Intensity设0.4到0.5,让光斑和阴影的对比不要太强烈。如果场景里有多个窗户,每个窗户一个Projector,贴图可以共用,但位置和旋转要分别调。
7.4 大世界场景的阴影分级策略
大世界场景里,玩家周围用实时阴影,远处用Projector阴影,再远处直接不显示阴影。这个分级策略能平衡效果和性能。
具体做法:以玩家为中心,半径20米内用实时阴影,20到50米用Projector阴影,50米外关掉阴影。Projector阴影的贴图分辨率可以低一些,因为远处看不清细节。切换距离可以根据设备性能调整,高端机可以拉远,低端机拉近。
实现上,用一个管理器脚本,每帧计算每个角色和玩家的距离,根据距离切换阴影模式。实时阴影的开关在光源组件上控制,Projector的开关在Projector组件上控制。切换时注意不要突然跳变,可以用淡入淡出过渡。
8. 收尾的一些个人体会
Projector做阴影这个方案,我从几年前开始用,中间踩了不少坑,也总结了一些经验。最大的体会是:不要指望Projector能替代实时阴影,它就是一个特定场景下的优化手段。用对了地方,效果和性能都很好;用错了地方,问题一堆。
我一般会在项目前期就确定阴影方案。如果是移动端项目,直接上Projector,不要犹豫。如果是PC端项目,先试实时阴影,性能不够再考虑Projector。如果是主机项目,实时阴影基本够用,Projector主要用来做特殊效果。
还有一个建议:阴影贴图不要只用一张。准备几张不同形状、不同强度的贴图,根据场景切换。比如角色在室内用柔和的圆形阴影,在室外用硬一点的阴影。切换贴图比调参数方便,效果也更可控。
最后分享一个小技巧:Projector的材质可以做成Material Variant,不同场景用不同的Variant,共用同一个Shader。这样改Shader的时候所有Variant一起更新,不用一个个改材质。Unity的Material Variant功能在2021版本之后就有了,用起来很方便。