在开发AR应用中如果虚拟物体能"投射阴影到现实环境中",沉浸感会大大增加.而且大多数AR应用都会这么做。
在网上查找相关的实现,社区文章不少,但是几乎都是转载的AR 中的阴影与浮现效果 (Unity实现)这篇文章。这种解决方案虽然可以实现AR阴影,但是在开启HDR后,其承接阴影的平面就清晰可见,露出破绽(请大神解释一下原因)。
于是受其启发,使用Unlit Shader实现了另一种方案,分享一下:
先演示一下效果:
虚拟的小人向桌面投出了影子,以假乱真
经常写Shader的同学应该知道其实编写一个能接受影子的Shader并不是一件难事,只需要添加以下影子三剑客——SHADOW_COORDS、TRANSFER_SHADOW、SHADOW_ATTENUATION三个宏。关键就是最后一个宏SHADOW_ATTENUATION,这个宏返回一个fixed的值,表示了光照衰减。该值在越没有影子的时候,越接近1,在影子越浓烈的地方越接近0。因此,这个光照衰减就是我们解决问题的关键,可以让我们写出一个只渲染阴影的Shader。
直接上代码:
Shader "ChuckLee/ARShadow"
{
Properties
{
_ShadowColor ("Shadow Color", Color) = (0.1, 0.1, 0.1, 0.53)
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Geometry+1"}
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "AutoLight.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 pos : SV_POSITION;
SHADOW_COORDS(2)
};
fixed4 _ShadowColor;
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed atten = SHADOW_ATTENUATION(i);
return fixed4(_ShadowColor.rgb,saturate(1-atten)*_ShadowColor.a);
}
ENDCG
}
}
FallBack "Diffuse"
}
关键在于片元着色器的这段
fixed atten = SHADOW_ATTENUATION(i);
return fixed4(_ShadowColor.rgb,saturate(1-shadow)*_ShadowColor.a);
我们取1和atten的差,归到(0,1)区间,然后乘以_ShadowColor的alpha值,最终作为输出片元的alpha值。saturate(1-shadow)*_ShadowColor.a在非阴影区域内为0,开启透明混合以后,此处是透明的,所以非阴影部分不会得到提现,而阴影区域得到大于零的值,并根据_ShadowColor 的值被渲染出想要的颜色和柔和度。
用这个Shader建立一个Materia放在Qaud上,把Qaud放在"地面"上就可以了,它其他物体投来的接受阴影,并且只渲染这个阴影。
(宏要联系上下文,比如宏TRANSFER_SHADOW的定义中有"ComputeScreenPos(o.pos)"(把顶点从裁剪空间向屏幕空间变换),所以在结构v2f你只能给顶点坐标变量起名pos,写成float4 blabla : SV_POSITION是不行。)