实时阴影渲染(一):PSSM平行分割阴影图

时间:2021-03-01 08:06:13

PSSM(Parallel Split Shadow Map)平行分割阴影图,是一种根据距离远近采用多个深度纹理渲染阴影的方法

适合用于室外大场景中的平行光比如太阳形成的阴影

本系列需要读者了解基本的深度阴影渲染方面的知识

1 视锥划分

如下图,以采用三个划分为例:

实时阴影渲染(一):PSSM平行分割阴影图

这里将视锥体平行划分为3个区域,代号分别为1、2、3

这三个区域在渲染阴影的时候分别采用不同的阴影图sm1、 sm2、sm3

这样将1、2、3对应的距离数据打包为一个float3变量splits.xyz,传入片段shader,然后通过以下的代码实现阴影图的选择:

if (z <= splits.x)
{
shadow = depthShadow(sm1,...);
}
else if (z <= splits.y)
{
shadow = depthShadow(sm2,...);
}
else if (z <= splits.z)
{
shadow = depthShadow(sm3,...);
}

这里z 、depthShadow函数和单个深度纹理阴影的实现类似,区别是相关参数都变成了三份

图上的三个划分并没有包含全部的渲染区域,因为没有必要渲染特别远的阴影

各个阴影区域的大小可以根据需要选择合理的算法

根据实际情况也可以将阴影区域划分为两个或者更多个

为了平滑 ,最后一级阴影(本例的3)可根据z值做线性淡入淡出

2 投影矩阵计算

渲染阴影前需要分别渲染三个深度纹理

对每一个阴影区域都需要计算对应的正投影矩阵

计算正投影矩阵可以理解为计算正投影相机的位置、投影大小和相机的up矢量

下面以单个投影区域为例说明如何计算这三个要素

实时阴影渲染(一):PSSM平行分割阴影图

如上图,彩色阴影部分为视锥体的单个划分区域

根据当前相机的位置、FOV、纵横比和远近截面距离可以算出该视锥体的八个顶点(这里为了简化仅画出四个)

根据光照方向可以用任一垂直矢量作为投影相机up矢量,不过不同的up矢量得到的投影体大小不同,你可以设计专门的算法计算最优的up矢量

然后再通过光照方向和up矢量的叉积计算出right矢量

任选一参照点(此例为左下的O点),通过up\right矢量和光照方向可以计算出其它7个点在投影相机空间相对于O点的最大、最小值也就是最小包围盒

如图所示,相机的位置应在AB的中点D所在的中心线上,其相对于D点的距离为AC的长度加上阴影可能的投影距离(视锥体外的物体投影)

投影体的长为AB的长度,宽为right方向得到的长度(此图没画出来)

也可以将视锥体的八个顶点直接转到相机空间计算轴对称包围盒、相机位置,然后再将相机位置变换回世界空间

不过当场景很大时,正反两次变换造成的误差会非常的大,直接在世界坐标系内计算则不会有这种问题