检查模板位,然后写入另一个位
Check a stencil bit, and write to another bit
我正在我的 3D 引擎中渲染反射面。我需要执行两个模板操作。首先,我通过深度测试绘制出反射表面,以找到表面的可见区域。然后我需要将模型绘制到 G-Buffer 中,它也被模板化以找到绘制我的天空盒的区域。
绘制表面:始终绘制,写入位 #1
绘制模型:仅在位 #1 设置时绘制,写入位 #2
我如何使用 OpenGL 执行此操作?我不确定 glStencilFunc ref 和掩码值与 glDepthMask 值之间的关系。
文档非常具体,但如果您只想创建然后激活遮罩,该怎么做并不总是直观或明显。我用这些作为一个简单的起点...
创建蒙版
将模板缓冲区清零并将 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);
}
调用上面的函数,绘制你的东西。您可以设置 glDepthMask and glColorMask,这样这实际上不会影响当前 colour/depth 和您的场景,只会影响模板缓冲区。
绘制,使用遮罩
只绘制到上一步的 1s 像素。
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)
。
读取和写入不同的位
现在专注于您的问题...
这一点有点棘手,因为 glStencilFunc()
的 ref
值在 模板测试和要替换的值中使用。但是,您可以使用面具来解决这个问题:
- 在
glStencilFunc()
中使用mask
来忽略testing/reading时的位。
- 使用
glStencilMask()
来阻止某些位被写入。
在你的情况下,你不需要屏蔽掉第一位,因为它已经设置好了。只需将 useStencilMask()
更新为 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)
和 glStencilFunc(GL_EQUAL, 3, 1);
。因为 mask
是 1
,所以只有第一位用于相等性测试。然而,整个 ref
of 3 (即 0b11
) 被写入。您可以使用glStencilMask(2)
(即0b10
)来阻止写入第一位,但它已经是一个所以没关系。
您也可以使用 GL_INCR
设置第二位并删除第一位。或者用 ones 清除并使用 GL_ZERO
来标记你的两个位。
我正在我的 3D 引擎中渲染反射面。我需要执行两个模板操作。首先,我通过深度测试绘制出反射表面,以找到表面的可见区域。然后我需要将模型绘制到 G-Buffer 中,它也被模板化以找到绘制我的天空盒的区域。
绘制表面:始终绘制,写入位 #1
绘制模型:仅在位 #1 设置时绘制,写入位 #2
我如何使用 OpenGL 执行此操作?我不确定 glStencilFunc ref 和掩码值与 glDepthMask 值之间的关系。
文档非常具体,但如果您只想创建然后激活遮罩,该怎么做并不总是直观或明显。我用这些作为一个简单的起点...
创建蒙版
将模板缓冲区清零并将 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);
}
调用上面的函数,绘制你的东西。您可以设置 glDepthMask and glColorMask,这样这实际上不会影响当前 colour/depth 和您的场景,只会影响模板缓冲区。
绘制,使用遮罩
只绘制到上一步的 1s 像素。
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)
。
读取和写入不同的位
现在专注于您的问题...
这一点有点棘手,因为 glStencilFunc()
的 ref
值在 模板测试和要替换的值中使用。但是,您可以使用面具来解决这个问题:
- 在
glStencilFunc()
中使用mask
来忽略testing/reading时的位。 - 使用
glStencilMask()
来阻止某些位被写入。
在你的情况下,你不需要屏蔽掉第一位,因为它已经设置好了。只需将 useStencilMask()
更新为 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)
和 glStencilFunc(GL_EQUAL, 3, 1);
。因为 mask
是 1
,所以只有第一位用于相等性测试。然而,整个 ref
of 3 (即 0b11
) 被写入。您可以使用glStencilMask(2)
(即0b10
)来阻止写入第一位,但它已经是一个所以没关系。
您也可以使用 GL_INCR
设置第二位并删除第一位。或者用 ones 清除并使用 GL_ZERO
来标记你的两个位。