如何在渲染 3D 场景之前在 3D 场景之上渲染 2D UI?

How to render a 2D UI on top of a 3D scene before rendering the 3D scene?

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

我目前所做的是渲染整个 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 叠加四边形 ,并且 3D 对象将仅在 2D 叠加为空白的地方绘制。

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

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

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

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

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

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