Shadow Mapping
要点
- 整体而言,先从光源的视角渲染一个深度贴图,然后在第二个pass里,用这个深度贴图作为对比,来决定渲染阴影的地方。
- 这个以光源为视角的view matrix来选择合适的proj matrix
- 正交投影(orthographic projection):可用于平行光
- 透视投影:可用于点光源
- 这个中间的深度贴图的 Format 设置为 R24G8_TYPELESS,因为会先后绑定为D24S8(渲染深度图)和R24X8(在PS中作为source)。
- 在处理多个RenderTarget的时候,总是可以通过申请多个RenderTarget,然后通过绑定新的来解除旧的
- 只需设置depthstencil target,无需设置 render target。这会有优化。
- 手动对视锥之外的物体进行剔除
- 这个以光源为视角的view matrix来选择合适的proj matrix
- 投影纹理坐标:如何为每个像素指定一个纹理坐标,从而使他看上去就像是被投影在一个物体上一样?
- 先把点p投影到光线的投影窗口,转换到NDC空间
- 然后把投影后的坐标从NDC空间转换到纹理空间,从而变换为纹理坐标
- 偏置(biasing)和走样(aliasing)
- 阴影斑(shadow acne)
- 由于shadowmap只是光源视角的离散的深度采样,无法精确满足摄像机视角的所有像素的深度比较。会导致非阴影位置出现阶梯条纹。
- 根据法线角度可知深度图和渲染时的最大误差,设置误差上限,可以减轻这种走样。 (peter-panning)但是误差上限如果太大,阴影就会明显偏移根部。但是在法线和光源几乎垂直的时候,误差上限需要比较大。现在图形卡对这件事有了专门的支持——根据坡度缩放偏置(slope-scaled-bias)的光栅阶段的属性:
- D3D11_RASTERIZER_DESC
- DepthBias:一个固定的偏移
- DepthBiasClamp:最大偏移钳位值
- SlopScaledDepthBias:根据光源透视下的斜率,控制深度偏移
- 注:如果深度缓存是UNORM格式或者没有深度缓存,那么偏移值为:(float)DepthBias * r + SlopeScaledDepthBias * MaxDepthSlope,其中r为深度缓存的格式的误差限。
问题:上边的MaxDepthSlope在哪里定义的?
- PCF滤波(percentage closer filtering)
- shadow map 的采样,并非是双线性插值,对四个点的深度值进行平均显然不对,同样,mipmap也会产生错误的结果。应当对结果进行插值,而非深度值——亦即PCF滤波:对分辨率下的相邻四个点进行采样并计算阴影,然后将结果进行插值。
- DX11新增:SampleCmpLevelZero
- 只读取第一级mipmap,且自动做一个4-tap的双线性插值的PCF
- Filter = COMPARISON_MIN_MAG_LINEAR_MIP_POINT
- Format = R32_FLOAT_X8X24_TYPELESS/R32_FLOAT/R24_UNORM_X8_TYPELESS/R16_UNORM
- 使用多个PCF可以将4-tap提升到更大,但太大的话会重新造成阴影漂移
- 采样是比较贵的,可以通过只在阴影边缘采样来提高效率。
- 不需要一定是方形的滤波
- 注意对于前边介绍的曲面细分阶段,生成shadowmap的时候,在light的视角所做的细分要和摄像机视角时的一样。不过如果不是偏移很大的话也可以省略。精确度换速度。
- PCF滤波在采样增多时的问题
- 采样的边长增大时,因为采样点未必是在同一直线上,所以会引起错误的结果——导致本来在阴影里的点,结果判定只有部分在阴影里,亦即阴影漂移
- 通过计算x和y方向的导数(斜率),从而计算在 z+delta_z 处的深度值,来解决上述漂移问题
- 阴影斑(shadow acne)