Direct X 12 – Stenciling

stencil buffer也是一个“屏幕后”的buffer,我们可以使用该buffer来实现某些特殊效果。stencil buffer和back buffer以及depth buffer有着相同的分辨率,也就是说stencil buffer中的第ij个像素对应back buffer和depth buffer中的第ij个像素。在4.1.5小节中我们了解到,当stencil buffer确定时,其将被附加到depth buffer。正如stencil的字面意义,stencil buffer犹如一个模板,其可以禁止某些像素被绘制到back buffer中。

例如,当实现镜面效果时,我们需要把镜子前的物体进行反射;然而我们希望反射的图像只在镜面中显示。我们可以使用stencil buffer来禁用反射后物体的像素,除非其位于镜面内(如下图所示)。

上图中,左边的骷髅在镜面中的显示是正确的。而骷髅没有显示在墙面上是因为墙面部分的像素无法通过depth test。然而,墙面外的部分,我们还是能看到骷髅。右边的图像中,我们使用了stencil buffer,禁止其在被绘制在非镜面的区域。

stencil buffer和depth buffer的设置都由D3D12_DEPTH_STENCIL_DESC表示,我需要将其设为D3D12_GRAPHICS_PIPELINE_STATE_DESC::DepthStencilState。最有效的学习stencil buffer的方法就是研究现有的demo范例。

目标:

1. 学习如何通过D3D12_DEPTH_STENCIL_DESC来设置depth buffer和stencil buffer。

2. 学会如何使用stencil buffer来模拟镜子反射的效果。

3. 能够辨别双重blend,并学会通过stencil buffer来防止其发生。

4. 了解depth complexity(深度复杂度)并了解两种检测depth complexity的方法。

11.1 Depth / Stencil 格式与清空

之前我们有提到过,depth/stencil buffer实为一张贴图,我们需要以确定的数据格式来创建它。而depth/stencil buffer所使用的数据格式如下:

1. DXGI_FORMAT_D32_FLOAT_S8X24_UINT:depth buffer由一个32位浮点数组成,stencil buffer由一个8位无符号整型数组成,且范围为[0,255],还有24位的空间用于padding。

2. DXGI_FORMAT_D24_UNORM_S8_UINT:depth buffer由一个24位无符号浮点数组成且范围为[0,1],stencil buffer为1个8位无符号整型数,且范围为[0,255]。

在我们的D3DApp框架中,当我们创建depth/stencil buffer时,我们将depth/stencil buffer的格式设为:

DXGI_FORMAT mDepthStencilFormat = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilDesc.Format = mDepthStencilFormat;

此外,在每一帧开始时,stencil buffer的值需要被重置为某个值。我们通过以下函数实现,其也会清空depth buffer:

void ID3D12GraphicsCommandList::ClearDepthStencilView(
    D3D12_CPU_DESCRIPTOR_HANDLE DepthStencilView,
    D3D12_CLEAR_FLAGS ClearFlags,
    FLOAT Depth,
    UINT8 Stencil,
    UINT NumRects,
    const D3D12_RECT *pRects);

1. DepthStencilView:我们需要清空的depth/stencil buffer的view/descriptor。

2. ClearFlags:若传入D3D12_CLEAR_FLAG_DEPTH,那只会清空depth buffer;若传入D3D12_CLEAR_FLAG_STENCIL,那只会清空stencil buffer;传入D3D12_CLEAR_FLAG_DEPTH|D3D12_CLEAR_FLAG_STENCIL则同时清空depth/stencil buffer。

3. Depth:将depth buffer中每个像素的depth设为该参数;其必须是范围[0,1]之内的浮点数。

4. Stencil:将stencil buffer中每个像素的stencil值设为该参数;其必须是范围[0,255]之内的整型数。

5. NumRects:数组pRects的元素数量。

6. pRects:D3D12_RECT的数组,其确定了depth/stencil buffer中需要清空的矩形区域;传入nullptr则表示清空整个depth/stencil buffer。

在demo中,我们每一帧都会调用以下函数。例如:

mCommandList->ClearDepthStencilView(DepthStencilView(),
    D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL,
    1.f, 0, 0, nullptr);

留下评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据