Shadow Volume是一种可以使实现阴影任意投射的高级方法。主要借助模板缓存(stencil buffer )实现对阴影区域的标识。最后借助标识进行alpha混合绘制阴影区域。
大致思路:
在Shadow Volume中最重要的就是找出物体相对于灯光的轮廓。然后为所有的轮廓边构造四边形。四边形的构造是这样的,将四边形的一边与轮廓边对齐,然后将另一边(相对于轮廓边的边)沿着灯光照射的方向移动到足够远,这样就形成一个很大的四边形。所有轮廓边生成的四边形将组成一个阴影体积,然后用这些四边形采用某种方式渲染到stencil buffer里面以实现阴影区域的标识。
重点细节:
- 轮廓边的识别
在轮廓识别中我目前掌握的两种方法。轮廓边是这样一种边。与边相邻的一个面朝向光源,另一个面背向光源。
- 一种是构建一个边与两个与边相邻的面法线的结构体,然后分别用两个面的法线与灯光方向做点积,如果两个点积的值的符号不同,便可以证明此边是轮廓边。因为点积的几何含义是:如果点积大于0表明面朝向灯光,如果点积小于0表明背向灯光,
- 另一种是将所有面朝光源的面的边加入到一个边列表,如何将边加入边列表,是这样,每个面有3条边,每个边有两个点,每个点都有索引,我们用两个点的索引表示边,加入到边列表。这样一般一个面会加入3条边,也就是6个点索引。我们在加入边列表的时候还需做这样的校验,如果列表中存在即将加入的边,就将已经存在的边去掉(即将加入的边也就不加入了)。这样最后在边列表存在就是边轮廓了。这是因为所有面向光源的面在非轮廓边上都有一次重复。所以所有非轮廓边肯定会去除。
2. 模板缓存对阴影区域进行标识
首先场景正常渲染到color buffer和zbuffer中,然后在zbuffer开启的状态下,开启stencil buffer,将stencil buffer比较函数改成总能通过,将通过zbuffer的操作改为增加值。这样先渲染阴影体积的正面。然后将通过zbuffer的操作改为减一,修改剔除操作为剔除正面,然后渲染阴影体积的背面。最后stencil buffer中所有是1的区域就是阴影区域(stencil buffer初始化为0)。如果stencil buffer是两面的那么,可以将阴影体积正面和背面同时渲染,当然也就需要将剔除操作去除(否则背面会被剔除)。
最后就是制作一个和窗口客户区同大小的黑色四边形与stencil buffer中表示的阴影区域进行alpha混合,实现将阴影绘画出来。四边形的肯定需要采用XYZRHW格式(即已经经过变换的坐标)。
具体案例参照早期DirectX9的Demo --- Shadow Volume。
具体的解说还是要参照详细教材。