动态阴影在Unity中,默认是使用ShadowMap,原理是通过生成投影深度图来比较,但是由于在移动平台对深度浮点值计算的限制,使得ShadowMap技术在移动平台要么效果不理想,要么需要消耗大量的计算。
所以常见的处理移动平台阴影的方式是使用RenderTexture + 投影,如王者荣耀中的实时阴影。
具体的方式是先渲染一张RenderTexture,这张RenderTexture包含了要显示阴影的对象,可以通过Layer设置,来剔除不显示阴影的对象。然后将这张RenderTexture设置给投影和材质。
第一步:
创建一个GameObject,上面添加Projector组件和Camera组件,两个组件和参数设置成一样,最好是使用正交方式。
Camera的clear flag设置为Solid,显示为白色,Camera的Layer mask只设置要生成阴影的那个层级
Projector 的Ignore layers设置来和 Camera的Layer mask一样,目的是投影不要投到了角色身上。
第二步:
创建一张RenderTexture,当然你也可以通过代码来创建,我这里是直接在unity中创建的,大小256*256,如果不够清晰,可以设置到512,格式设置为RGB565,Warp Mode为Clamp,Filter Mode为Bilinear。
将这张RenderTexture设置给上面的Camera
第三步:
创建一个Shader,用于渲染成RenderTexture,最简单的Shader就行
Shader "Unlit/Shadow"
{
Properties
{
_ShadowColor ("Main Color", COLOR) = (0.5, 0.5, 0.5, 1)
}
SubShader
{
Pass
{
Color [_ShadowColor]
}
}
}
Shader "Projector/Multiply"
{
Properties
{
_ShadowTex ("Cookie", 2D) = "gray" { TexGen ObjectLinear }
_FalloffTex ("FallOff", 2D) = "white" { TexGen ObjectLinear }
}
Subshader
{
Tags { "RenderType"="Transparent-1" }
Pass
{
ZWrite Off
Fog { Color (1, 1, 1) }
AlphaTest Less 1
ColorMask RGB
Blend DstColor Zero
Offset -1, -1
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct v2f
{
float4 pos : SV_POSITION;
float4 uv_Main : TEXCOORD0;
float4 uv_Clip : TEXCOORD1;
};
sampler2D _ShadowTex;
sampler2D _FalloffTex;
float4x4 unity_Projector;
float4x4 unity_ProjectorClip;
v2f vert(appdata_tan v)
{
v2f o;
o.pos = UnityObjectToClipPos (v.vertex);
o.uv_Main = mul (unity_Projector, v.vertex);
o.uv_Clip = mul (unity_ProjectorClip, v.vertex);
return o;
}
half4 frag (v2f i) : COLOR
{
half4 tex = tex2Dproj(_ShadowTex, i.uv_Main);
half4 falloff = tex2D(_FalloffTex, i.uv_Clip.xy);
tex = lerp(float4(1,1,1,1),tex,falloff.a);
return tex;
}
ENDCG
}
}
}
最后一步:
编写脚本,主要用于通过自定义的shader来生成一张RenderTexture
public Projector proj;
public Camera projCam;
public Shader shadowShader;
// Use this for initialization
void Start () {
projCam.enabled = false;
}
// Update is called once per frame
void LateUpdate () {
if(proj && projCam){
projCam.aspect = proj.aspectRatio;
projCam.RenderWithShader(shadowShader,null);
}
}
总结:主要步骤,就是通过一个单独的相机渲染一张RenderTexture给投影,投影再将这张RenderTexture投到地面上。
但是有一定的局限,要投影的物品穿过了地面,投影计算也会计算地面以下的,最后显示出来就有错。