配置Depth-Stencil功能

本节介绍了设置深度模板缓冲区的步骤以及输出合并阶段的深度模板状态。

了解如何使用深度模具缓冲区和相应的深度模具状态,请参阅高级模具技术

创建Depth-Stencil资源

使用纹理资源创建深度模具缓冲区。

ID3D11Texture2D* pDepthStencil = NULL;
D3D11_TEXTURE2D_DESC descDepth;
descDepth.Width = backBufferSurfaceDesc.Width;
descDepth.Height = backBufferSurfaceDesc.Height;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = pDeviceSettings->d3d11.AutoDepthStencilFormat;
descDepth.SampleDesc.Count = 1;
descDepth.SampleDesc.Quality = 0;
descDepth.Usage = D3D11_USAGE_DEFAULT;
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;
hr = pd3dDevice->CreateTexture2D( &descDepth, NULL, &pDepthStencil );

创建深度模具状态

输出合并器阶段通过深度模具状态来判断如何执行深度模具测试。 深度模具测试确定是否应该绘制给定像素。

D3D11_DEPTH_STENCIL_DESC dsDesc;

// Depth test parameters
dsDesc.DepthEnable = true;
dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
dsDesc.DepthFunc = D3D11_COMPARISON_LESS;

// Stencil test parameters
dsDesc.StencilEnable = true;
dsDesc.StencilReadMask = 0xFF;
dsDesc.StencilWriteMask = 0xFF;

// Stencil operations if pixel is front-facing
dsDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
dsDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

// Stencil operations if pixel is back-facing
dsDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
dsDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
dsDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

// Create depth stencil state
ID3D11DepthStencilState * pDSState;
pd3dDevice->CreateDepthStencilState(&dsDesc, &pDSState);

DepthEnable 和 StencilEnable 启用 (并禁用) 深度和模具测试。 将 DepthEnable 设置为 FALSE 可禁用深度测试并阻止写入深度缓冲区。 将 StencilEnable 设置为 FALSE 可禁用模具测试并阻止写入模具缓冲区 (当 DepthEnable 为 FALSE 且 StencilEnable 为 TRUE 时,深度测试始终通过模具操作) 。

DepthEnable 仅影响输出合并阶段 - 它不会影响在将数据输入到像素着色器之前对值的剪裁、深度偏差或值固定。

将深度模具数据绑定到 OM 阶段

绑定深度模具状态。

// Bind depth stencil state
pDevice->OMSetDepthStencilState(pDSState, 1);

使用视图绑定深度模具资源。

D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
descDSV.Format = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
descDSV.Texture2D.MipSlice = 0;

// Create the depth stencil view
ID3D11DepthStencilView* pDSV;
hr = pd3dDevice->CreateDepthStencilView( pDepthStencil, // Depth stencil texture
                                         &descDSV, // Depth stencil desc
                                         &pDSV );  // [out] Depth stencil view

// Bind the depth stencil view
pd3dDeviceContext->OMSetRenderTargets( 1,          // One rendertarget view
                                &pRTV,      // Render target view, created earlier
                                pDSV );     // Depth stencil view for the render target

呈现目标视图数组可以传递到 ID3D11DeviceContext::OMSetRenderTargets,但这些呈现目标视图都将对应于单个深度模具视图。 Direct3D 11 中的呈现目标数组是一项功能,使应用程序能够在基元级别同时呈现到多个呈现目标上。 与通过多次调用 ID3D11DeviceContext::OMSetRenderTargets 的单独设置呈现目标 (实质上 Direct3D 9) 中使用的方法,呈现目标数组提供更高的性能。

呈现器目标必须全部是相同类型的资源。 如果使用多重采样反锯齿,则所有绑定的呈现器目标和深度缓冲区必须拥有相同的采样数量。

当使用缓冲区作为呈现器目标时,深度模具测试和多个呈现器目标不受支持。

  • 可以同时绑定多达 8 个呈现器目标。
  • 所有呈现器目标在所有维度上的尺寸必须相同(宽度和高度,3D 深度或 *Array 类型的数组大小)。
  • 每个呈现器目标都可能有不同的数据格式。
  • 写掩码控制将哪些数据写入呈现器目标。 输出写掩码控制在每个呈现器目标、每个组件级别上将哪些数据写入呈现器目标。

高级模具技术

深度模具缓冲区的模具部分可用于创建合成、贴纸和轮廓等呈现效果。

合成

你的应用程序可以使用模具缓冲区将 2D 或 3D 图像合成到 3D 场景中。 模具缓冲区中的掩码用于遮挡渲染目标图面的区域。 然后,可以将已存储 2D 信息(如文本或位图)写入遮挡区域。 或者,你的应用程序也可以将额外的 3D 基元渲染到渲染目标图面的已屏蔽模具区域。 它甚至可以渲染整个场景。

游戏通常会合成多个 3D 场景。 例如,赛车游戏通常会显示后视镜。 镜中呈现驾驶员身后的 3D 场景视图。 它本质上是与驾驶员前方视图合成的第二个 3D 场景。

贴纸

Direct3D 应用程序使用贴纸来控制将特定基元图像中的哪些像素绘制到渲染目标图面。 应用程序将贴纸用于基元图像以使共面多边形正确渲染。

例如,在道路上应用轮胎压痕和黄线时,标记应显示直接在路面上显示。 但是,标记和道路的 z 值完全相同。 因此,深度缓冲区可能无法明确分离两者。 后部基元中的部分像素可能会渲染到前部基元上,反之亦然。 最后生成的图像看起来每一帧之间闪烁微光。 此效果称为 z-fighting 或 flimmering。

若要解决此问题,可使用模具屏蔽后部基元上显示贴纸的部分。 关闭 z 缓冲,并将前部基元的图像渲染至渲染目标曲面的已屏蔽区域。

可以使用多个纹理混合来解决此问题。

轮廓与剪影

你可以使用模具缓冲区生成更抽象的效果,例如轮廓与剪影。

如果你的应用程序具有两个呈现器通道,一个用于生成模具掩码,另一个用于将模具掩码应用于图像,但是第二个通道上的基元稍微小些,那么生成的图像将仅包含基元的轮廓。 然后,应用程序可以使用纯色填充图像的模具屏蔽区域,使基元呈现浮雕般的外观。

如果模具掩码与你正在渲染的基元的大小和形状相同,则在生成的图像中,基元应在的位置有一个洞。 然后,应用程序可以用黑色填充这个洞,以生成基元剪影。

双面模具

阴影卷用于在模具缓冲区中绘制阴影。 应用程序通过遮挡几何图形计算阴影卷大小,方法是计算剪影边缘并将它们从光线中挤压到一组 3D 卷中。 然后,这些卷将两次渲染到模具缓冲区中。

第一次渲染绘制前向多边形,并增加模具缓冲区值。 第二次渲染绘制阴影卷的后向多边形,并减少模具缓冲区值。 通常,所有增加和减少值会相互抵消。但是,由于阴影卷已被渲染,使用常规几何图形渲染的场景会导致部分像素无法通过 z 缓冲测试。 留在模具缓冲区的值域阴影中像素相对应。 使用这些保留的模具缓冲区内容作为掩码,以在场景中 alpha 混合大面积、全环绕的黑色间隙。 将模具缓冲区作为掩码,结果是将位于阴影处的像素变暗。

这意味着每个光源的阴影几何图形将绘制两次,因此为 GPU 的顶点吞吐量带来了压力。 双面模具功能旨在缓解此问题。 这种方法使用两组模具状态(如下文所列),一组用于前向三角形,另一组用于后向三角形。 这样,每个阴影卷每个光源只需绘制一次。

可以在 ShadowVolume10 示例中找到双面模具实现的示例。

将深度模具缓冲区读作纹理

着色器可以将非活动深度模具缓冲区读作纹理。 将深度模具缓冲区读作纹理的应用程序在两个通道中呈现,第一个通道写入深度模具缓冲区,第二个通道从缓冲区读取。 这样着色器可以将之前写入缓冲区的深度或模具值与当前正在呈现的像素值进行比较。 比较结果可用于创建效果,例如阴影贴图或粒子系统中的软粒子。

若要创建可用作深度模具资源和着色器资源的深度模具缓冲区,需要对 创建Depth-Stencil资源 部分中的示例代码进行一些更改。

  • 深度模具资源必须采用无类型格式,例如DXGI_FORMAT_R32_TYPELESS。

    descDepth.Format = DXGI_FORMAT_R32_TYPELESS;
    
  • 深度模具资源必须使用D3D10_BIND_DEPTH_STENCIL和D3D10_BIND_SHADER_RESOURCE绑定标志。

    descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL | D3D10_BIND_SHADER_RESOURCE;
    

此外,还需要使用 D3D11_SHADER_RESOURCE_VIEW_DESC 结构和 ID3D11Device::CreateShaderResourceView 为深度缓冲区创建着色器资源视图。 着色器资源视图将使用类型化格式(例如 DXGI_FORMAT_R32_FLOAT ,该格式等效于创建深度模具资源时指定的无类型格式。

在第一次呈现传递中,深度缓冲区会按照将 Depth-Stencil数据绑定到 OM 阶段 部分中所述进行绑定。 请注意,传递给 的格式D3D11_DEPTH_STENCIL_VIEW_DESC。格式将使用类型化格式,例如 DXGI_FORMAT_D32_FLOAT。 第一个呈现器通过后,深度缓冲区将包含场景的深度值。

第二次呈现传递中 ,ID3D11DeviceContext::OMSetRenderTargets 函数用于将深度模具视图设置为 NULL 或其他深度模具资源,并使用 ID3D11EffectShaderResourceVariable::SetResource 将着色器资源视图传递给着色器。 这允许着色器查找在第一次呈现传递中计算的深度值。 请注意,如果第一个呈现通道的视图点与第二个呈现阶段不同,则需要应用转换来检索深度值。 例如,如果使用阴影映射技术,则第一个呈现通道将从光源的角度进行,而第二个呈现通道将从查看器的角度进行。

输出合并器阶段

管道阶段 (Direct3D 10)