2

我正在我的 3D 引擎中渲染反射表面。我需要执行两个模板操作。首先,我通过深度测试画出反射面,以找到表面的可见区域。然后我需要将模型绘制到 G-Buffer 中,该 G-Buffer 也被模板用来寻找绘制我的天空盒的区域。

  • 绘制表面:始终绘制,写入位 #1

  • 绘制模型:仅在设置位 #1 时绘制,写入位 #2

我将如何使用 OpenGL 来做到这一点?我不确定 glStencilFunc ref 和 mask 值以及 glDepthMask 值之间的关系。

4

1 回答 1

5

文档非常具体,但如果您只是想创建然后激活蒙版,则并不总是直观或显而易见的。我用这些作为一个简单的起点......

创建蒙版

将模板缓冲区清零并将 1 写入您绘制的所有像素。

void createStencilMask()
{
    // Clear the stencil buffer with zeroes.
    // This assumes glClearStencil() is unchanged.
    glClear(GL_STENCIL_BUFFER_BIT);

    // Enable stencil raster ops
    // 1. Enables writing to the stencil buffer (glStencilOp)
    // 2. If stencil test (glStencilFunc) fails, no frags are written
    glEnable(GL_STENCIL_TEST);

    // sfail GL_KEEP - keep original value if stencil test fails
    // dpfail GL_KEEP - also keep if the stencil passes but depth test fails
    // dppass GL_REPLACE - write only if both pass and you'd normally see it
    //                     this writes the value of 'ref' to the buffer
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);

    // func GL_ALWAYS - the stencil test always passes
    // ref 1 - replace with ones instead of zeroes
    // mask 1 - only operate on the first bit (don't need any more)
    // Assumes glStencilMask() is unchanged
    glStencilFunc(GL_ALWAYS, 1, 1);
}

调用上面的函数,然后画出你的东西。您可以设置glDepthMaskglColorMask这样它实际上不会影响当前颜色/深度和您的场景,只会影响模板缓冲区。

绘制,使用蒙版

仅绘制上一步中为 1 的像素。

void useStencilMask()
{
    // Enable stencil raster ops
    glEnable(GL_STENCIL_TEST);

    // Just using the test, don't need to replace anything.
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

    // Only render if the current pixel stencil value is one.
    glStencilFunc(GL_EQUAL, 1, 1);
}

完成后绘制并禁用测试glDisable(GL_STENCIL_TEST)

读取和写入不同的位

现在专注于你的问题......

这一点有点棘手,因为在模板测试和要替换的ref值中glStencilFunc()使用了相同的值。但是,您可以使用遮罩解决此问题:

  1. 在测试/读取时使用maskinglStencilFunc()忽略位。
  2. 用于glStencilMask()阻止某些位被写入。

在您的情况下,您不需要屏蔽写入的第一位,因为它已经设置好了。只需更新useStencilMask()glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)glStencilFunc(GL_EQUAL, 3, 1);。因为maskis 1,所以在相等性测试中只使用第一位。然而,整个ref3(即0b11)都被写入。您可以使用glStencilMask(2)(which is 0b10) 来停止写入第一位,但它已经是一个,所以没关系。

您还可以利用GL_INCRwhich 设置第二个位并删除第一个位。或者也许用一个清除并GL_ZERO用来标记你的位。

于 2015-05-13T13:19:27.297 回答