首页 > 解决方案 > 如何在渲染 3D 场景之前在 3D 场景之上渲染 2D UI?

问题描述

我有一个包含 2D 叠加层的 2D 纹理。纹理本身大部分是空白的(透明的),有一些部分包含一些数据。

我目前所做的是渲染整个 3D 场景,禁用深度缓冲区并在其顶部渲染 2D 四边形:

 // render 3D scene
 context->OMSetDepthStencilState(_noDepthTestState.Get(), 1); // disable depth test
 // render 2D quad on top of the whole viewport
 context->OMSetDepthStencilState(nullptr, 1); // restore default

_noDepthTestState变量是ID3D11DepthStencilState使用以下描述符创建的:

   D3D11_DEPTH_STENCIL_DESC desc{};
   desc.DepthEnable = false;
   desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
   desc.DepthFunc = D3D11_COMPARISON_LESS;
   desc.StencilEnable = true;
   desc.StencilReadMask = 0xFF; 
   desc.StencilWriteMask = 0xFF;
   desc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
   desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
   desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
   desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
   desc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
   desc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
   desc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
   desc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

我想在渲染 3D 场景之前渲染 2D 叠加四边形,并且仅在 2D 叠加为空白的地方绘制 3D 对象。

有没有一种有效的方法来实现这种行为?深度/模板状态的正确配置是什么?

标签: directxdirectx-11direct3ddirect3d11

解决方案


这取决于渲染主 3D 场景的方式。

假设您不使用模板缓冲区,并且使用 D3D11_COMPARISON_LESS 深度比较,请按以下方式绘制四边形。

您的顶点着色器应该输出四边形顶点 [ ±1, ±1, 0, 1 ]。这使得 GUI 最接近相机,早期 Z 拒绝应该在 PS 阶段之前剪辑 3D 场景中的被遮挡像素,节省一些 GPU 资源(我假设这就是为什么你要先渲染 2D)。

您的像素着色器应该从您的 GUI 纹理中读取,将 alpha 与某个阈值进行比较,如果它足够小,请调用discard. 丢弃的像素不会改变任何缓冲区,无论是颜色还是深度/模板。

如果您的 GUI 由轴对齐的矩形组成,或者您不使用 MSAA,这将可以正常工作。但是,如果您确实有 GUI 的弯曲/对角边缘,并且使用 MSAA,您将不会对结果感到满意。解决这个问题是可能的,但要复杂得多。SV_Coverage在渲染 GUI 纹理时,您需要以某种方式生成每像素值。


推荐阅读