最新文章
泰课在线 | 微信拼团成功后如何获取课程?
08-09 17:57
Unity教程 | 使用ARKit为iOS开发AR应用
07-31 17:23
Unity Pro专业版7折订阅四选一工具包之VR开发与艺术设计
07-28 11:47
网友使用虚幻UE4实现CAVE 多通道立体渲染的沉浸式环境
07-27 11:57
VR晕动症调查:未来5年内大部分VR晕动症将得到解决
07-27 11:26
AMD CEO:未来3-5年最重要 希望5年达1亿VR用户
07-27 10:44
Untiy 3d ShaderLab平面阴影
平面阴影是一种较为特殊的情况。在这种情况下,我们仅考虑物体的阴影投射到平面上的情形,因此有一套相对简单的专用算法。
1. 平行光对平面的投影
1.1 对平行光投影的考虑
首先考虑最简单的情况,即如何计算平行光的投影。在计算中,平行光实际上是一个方向矢量,表示阴影的投射方向,而平面则是阴影要影响的目标物体。我们需要获取目标物体的 Object Space 矩阵,在目标物体的空间内重新计算投影物体的顶点,计算其沿光线方向在阴影接受平面上的位置,这个位置关系可以通过三角形相似原理来计算。
如果我们使用 Unity 自带的 Plane 作为阴影接受平面,那么只需重新计算顶点的 xz 位置。当阴影投射到 Build In 的 Plane 上时,在其 Object Space 中,y 坐标应为 0。但在实际使用时,为了确保阴影始终位于物体上方,我们会对 z 坐标进行偏移。
如图所示,一个精准的阴影已经出现在平面上,下面先展示效果。
1.2 进出阴影接受平面的矩阵
每个投射平面阴影的物体都需要一个脚本来告知其阴影接受物体的信息,具体来说,就是到其 Object Space 空间的矩阵,以及从平面的 Object Space 返回的矩阵。以下是相应的脚本代码:
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
public class PlaneShadowCaster : MonoBehaviour {
public Transform reciever;
void Update () {
GetComponent<Renderer>().sharedMaterial.SetMatrix("_World2Ground", reciever.GetComponent<Renderer>().worldToLocalMatrix);
GetComponent<Renderer>().sharedMaterial.SetMatrix("_Ground2World", reciever.GetComponent<Renderer>().localToWorldMatrix);
}
}
1.3 使用三角形相似计算阴影
在了解了进出阴影接受平面的矩阵后,我们就可以为投射阴影的物体计算阴影了。这一计算过程在 PlanarShadow_1.shader 中完成,其完整代码如下:
Shader "Tut/Shadow/PlanarShadow_1" {
SubShader {
pass { // 对物体本身做一个简单的光照计算
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"
float4x4 _World2Ground;
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(_Object2World, vertex);
vt = mul(_World2Ground, vt); // 将物体顶点转换到接收平面空间
vt.xz = vt.xz - (vt.y / litDir.y) * litDir.xz; // 用三角形相似计算沿光源方向投射后的 xz
vt.y = 0; // 使阴影保持在接收平面上
vt = mul(_Ground2World, vt); // 返回到世界坐标空间
vt = mul(_World2Object, vt); // 计算结果重新表达为 Object Space 坐标
return mul(UNITY_MATRIX_MVP, vt); // 经典的 MVP 变换,输出到 Clip Space
}
float4 frag(void) : COLOR {
return float4(0.3, 0.3, 0.3, 1); // 一个灰色的阴影出来了
}
ENDCG
}
}
}
在此 Shader 中,我们首先使用固定管线对物体进行简单的光照计算。在计算阴影的 ForwardBase 通道中,首先使用一种可以叠加阴影的混合模式,然后使用 z 偏移确保阴影位于接受平面之上。_World2Ground 和 _Ground2World 分别是我们自定义的两个进出阴影接受平面的矩阵。
在具体计算时,首先将光源方向和投影物体的顶点都转换到接受平面的空间。当它们处于同一空间后,通过简单的三角形相似算法,计算投影物体顶点沿光线投射后在接受平面上的新位置。由于这是一个 Build In 的 Unity Plane,所以只需计算其 xz 分量,并将 y 分量设为 0,这样投影出来的阴影就会出现在接受平面上。投影计算完成后,我们先返回到世界空间,再转换到投影物体本身的 Object Space,最后进行经典的 MVP 变换。