阴影形成的原理
2.阴影投射原理详解
- 因为三维的示意图不好画,所以这里的讲解都在二维空间中,但是不影响最终结果
- 首先有一个大前提,上图的数值全部在一个坐标空间中,这里选取的是被投影的Plane片自身的模型空间,大家先不要管坐标系转换,先知道一点,他们现在在一个坐标空间下就可以了
- ly和lx是光照方向归一化之后的方向,可以理解为光照方向的单位向量
- 三角形ABC和三角形ADE近似,则有: A C A E = B C D E \frac{AC}{AE}=\frac{BC}{DE} AEAC=DEBC,得到 A E = A C ∗ D E B C = l x ∗ l y y AE=AC*\frac{DE}{BC}=lx * \frac{ly}{y} AE=AC∗BCDE=lx∗yly
- 我们要求的就是A点的横坐标,已知E的横坐标坐标为x,则 A x = x − A E = x − l x ∗ l y y Ax=x-AE=x-lx * \frac{ly}{y} Ax=x−AE=x−lx∗yly
- 同样换算到三维空间中求A的z坐标, A z = z − A E = z − l z ∗ l y y Az=z-AE=z-lz * \frac{ly}{y} Az=z−AE=z−lz∗yly
3.阴影的实现过程
- 先将物体正常渲染一遍,得到正常的模型显示
- 在按第2步中的过程,将顶点投射到响应顶点的面片上,再渲染一遍,即可得到影子
4.Shader详解
- 第一个pass正常渲染
pass {
//使用内置的Diffuse材质进行渲染
Tags { "LightMode" = "ForwardBase" }
Material{Diffuse(1,1,1,1)}
Lighting On
}
- 渲染阴影
pass {
Tags { "LightMode" = "ForwardBase" }
Blend DstColor SrcColor
Offset -1,-1
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
//接收阴影平面的world to local 矩阵
float4x4 _World2Ground;
//接收阴影平面的local to world 矩阵
float4x4 _Ground2World;
float4 vert(float4 vertex: POSITION) : SV_POSITION
{
float3 litDir;
//计算模型顶点在世界空间中的光照方向
litDir=WorldSpaceLightDir(vertex);
//将世界空间中顶点的光照方向转换到接收阴影平面的模型空间中
litDir=mul(_World2Ground,float4(litDir,0)).xyz;
//归一化得到单位向量
litDir=normalize(litDir);
float4 vt;
//将模型的顶点从模型空间转换到世界空间中
vt= mul(unity_ObjectToWorld, vertex);
//将模型顶点从世界空间转换到接收阴影平面的坐标空间中
vt=mul(_World2Ground,vt);
//计算偏移后的顶点x,y轴位置
vt.x=vt.x-(vt.y/litDir.y)*litDir.x;
vt.z=vt.z-(vt.y/litDir.y)*litDir.z;
//阴影应该在接收阴影的平面上,即y轴坐标为0
vt.y=0;
// 将已经转换到接收阴影平面的模型空间中的顶点,转换到世界空间中去
vt=mul(_Ground2World,vt);
//将世界空间中的顶点转换到自己的模型空间下
vt=mul(unity_WorldToObject,vt);
//转换到裁剪空间中,交给片元着色器处理
return UnityObjectToClipPos(vt);
}
float4 frag(void) : COLOR
{
return float4(0.3,0.3,0.3,1);
}
ENDCG
}
5.效果图
6.总结
- 这个例子是为了讲述阴影的形成过程,实用起来会有很多不符合实际的地方, 但是知道这个原理,我们可以真正的作出一些很好看的效果。