Unity3D引擎中投影式纹理映射应用
投影式纹理映射(Projective texturing)常用于在低端平台实现实时阴影效果,或实现类似幻灯片投射的效果。从其名称可以看出,这种技术与普通的网格渲染流程存在关联。
常规网格渲染流程
常规的网格渲染流程如下:首先,将网格从物体本地坐标转换到世界坐标;然后,通过反转的摄像机世界坐标变换矩阵,将其转换到摄像机的本地坐标(Eye Space);接着,将其乘以投影矩阵,使其进入裁剪空间(Clip Space);之后,通过将裁剪空间的四维向量除以 w 进入NDC空间(范围在 (-1, 1));最后,通过视口(ViewPort)和深度的范围进行剔除操作。
投影式纹理映射原理
将投影器看作摄像机,将投影目标(接收投影结果的网格)看作摄像机渲染的屏幕,就会发现投影式纹理映射的原理与网格渲染的流程极为相似。具体步骤如下:
- 定义投影器的方向及投影矩阵。
- 将本地坐标的网格通过相同的转换,转换到投影器的NDC空间。
- 进行一次重映射(将
(-1, 1)映射到(0, 1)),从而得到投影目标网格在投影器的屏幕空间中正确的UV值。 - 使用这个UV对投影器产生的贴图进行采样,即可得到正确的投影像素。
以特定场景为例,红色的炸弹是产生投影的网格,地面和地上的汽车是接收投影的网格。具体操作流程为:首先,设置投影的方向(左手系沿X轴转90度)与投影的矩阵(平行投影);接着,将一定范围的网格渲染到一张RenderTexture上;然后,给接受投影的网格设置自定义的着色器,并对着色器传入用于将世界坐标的投影接收网格转换到投影器屏幕坐标的矩阵:
其中,OffsetMatrix 是一个用于重映射的矩阵,可将NDC空间的坐标 (-1, 1) 映射到 (0, 1) 的范围。ProjectionMatrix 代表投影器的投影矩阵,InvertWorldMatrix 代表投影器的世界到本地坐标的转换矩阵。
在着色器中,通过将世界坐标的顶点位置与 ProjectorMatrix 相乘,传入片段着色器,再将传入的四维向量除以 w,就可以得到正确的、可用于采样投影器 RenderTexture 的UV坐标。利用这些坐标,我们可以实现幻灯片投影效果、硬阴影,甚至是假的软阴影(即对 RenderTexture 做一次模糊)。
部分着色器代码示例
以下是部分着色器代码,仅供参考:
float4x4 ProjectorMatrix;
struct v2f {
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0;
};
v2f vert(float4 vertex : POSITION)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, vertex);
float4 worldPos = mul(_Object2World, vertex);
o.uv = mul(ProjectorMatrix, worldPos);
return o;
}
sampler2D Maou_ProjectorTexture;
fixed4 frag(v2f i) : SV_Target
{
float2 texCroodProj = i.uv.xy / i.uv.w;
fixed4 color = tex2D(Maou_ProjectorTexture, texCroodProj);
// clip掉(0,1)范围之外的像素
float2 uvmasks = min(texCroodProj, 1.0 - texCroodProj);
float mask = min(uvmasks.x, uvmasks.y);
color = mask < 0.0 ? fixed4(1, 1, 1, 0) : color;
return color;
}
通过上述步骤和代码,我们可以在Unity3D引擎中实现投影式纹理映射,进而实现各种相关的视觉效果。