版权声明:本文为博主原创文章,未经博主允许不得转载 https://blog.csdn.net/mobilebbki399/article/details/79491711
平面阴影作为一种最简单的实时阴影实现,尽管其仅能局限于在完全平坦的地面的情况下使用,但由于其性能良好,在许多移动端手游中仍然可以发挥较强的使用价值。
平面阴影的实现原理比较简单,一般由如下几个过程:
1.向shader传入世间到平面的矩阵和平面到世界的矩阵
2.向shader传入灯光方向
3.在平面空间下根据灯光方向计算顶点在平面上的投影坐标,最终得到阴影
但这种方式存在一个问题:我们需要向shader传入至少两个矩阵,而实际上计算顶点沿某方向在平面上的交点要简单的多。
首先考虑以下情况,3D空间中存在某点o,以及平面P,其中P的法线为n,并且存在某点p为平面P上的一点,现在假设从o点沿d方向发射射线,求射线在平面P上的交点h,这时时间上就可以看成计算射线与平面的交点。
根据射线的参数方程:ray = ray.origin+t*ray.direction,即可以知道我们实际上只需求出t,即可确定该射线在平面上的交点:
h = o+t*d
通过计算,得到最终的t:
t = (p – o)·n/(d·n) = (p·n–o·n)/(d·n)
该t表示顶点到平面上的投影位置的距离,且存在如下3中情况:
1.t>0,表示顶点在平面上存在投影点
2.t==0,表示顶点位于平面上
3.t<0,表示顶点在平面上没有投影点, 因此根据t值,我们可以方便的判断是否产生阴影。 而对于已知平面P,我们可以将其以向量方式表达:P=<n,d>,其中d=n·p
通过以上推导,我们只需向shader传入一个平面的向量表达,即可方便求出平面阴影,而不需要向shader传入两个矩阵:
float4 _ShadowColor;
half4 _Plane;
half4 _LightDir;
v2f vert(appdata v)
{
v2f o;
float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
float t = (_Plane.w - dot(worldPos.xyz, _Plane.xyz)) / dot(_LightDir.xyz, _Plane.xyz);
worldPos.xyz = worldPos.xyz + t*_LightDir.xyz;
o.vertex = mul(unity_MatrixVP, worldPos);
o.col = _ShadowColor *step(0, t);
return o;
}
完整实现如下:
Shader "Hidden/Shadow/FakeShadow/PlaneShadow"
{
Properties
{
_ShadowCol("Color", color) = (1,1,1,1)
_StencilID("StencilID", float) = 2
_Plane("Plane", vector) = (1,1,1,1)
_LightDir("LightDir", vector) = (1,1,1,1)
}
SubShader
{
Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" }
//Pass { 渲染非阴影部分 ... }
Pass
{
Stencil{
Ref [_StencilID]
Comp NotEqual
Pass replace
}
zwrite off
blend srcalpha oneminussrcalpha
offset -1,-1
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
UNITY_FOG_COORDS(0)
float4 vertex : SV_POSITION;
float4 col : COLOR;
};
float4 _ShadowCol;
half4 _Plane;
half4 _LightDir;
v2f vert (appdata v)
{
v2f o;
float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
float t = (_Plane.w - dot(worldPos.xyz, _Plane.xyz)) / dot(_LightDir.xyz, _Plane.xyz);
worldPos.xyz = worldPos.xyz + t*_LightDir.xyz;
o.vertex = mul(unity_MatrixVP, worldPos);
o.col = _ShadowCol *step(0, t);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = i.col;
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
更多内容: http://www.lsngo.net