问题描述
是否可以实现自定义深度缓冲区值,以便我可以实现非传递排序?例如
Red > Green
Green > Blue
Blue > Red
我想这将在片段着色器或其他东西中实现,仅在像素位于现有像素之上时才写入像素,按照上述模式。请注意,由于它是不可传递的,因此不可能为每种颜色分配一个数值并保持顺序。
换句话说,我想要一个自定义的 z 缓冲区,其中每个元素都是非传递类型。因此 z 缓冲区将是一个充满 Red
、Green
或 Blue
值的大表。为每个分配一个数字,然后根据上面的模式进行比较以确定哪个在最上面。如果所有三种颜色都在同一个片段上,那么我不在乎哪个在上面。
下图说明了我的意思(这是我希望它能够做到的):
(图片来自维基百科)
我想要的东西在 OpenGL 中是可行的,还是我的做法太过分了?如果可能,性能下降的严重程度是多少?
解决方法
您想要做的不仅是可能的,而且相对快速和简单。为了render the penrose triangle。
诀窍是使用 stencil buffer 而不是深度缓冲区。实际上有几种不同的方法可以使用模板缓冲区来实现循环排序效果,具体取决于您的确切需求。我将在这里描述一种可能的算法:
- 在没有模板测试的情况下绘制红色层,同时将
1
写入模板缓冲区。 -
使用模板测试
not equal to 1
绘制绿色层,同时将2
写入模板缓冲区。 这将防止在已经绘制红色的地方绘制它,有效地将绿色“置于”红色之后。 -
使用模板测试
not equal to 2
绘制蓝色图层。这将允许它绘制在红色之上,而不是绿色之上,从而有效地将其放置在它们之间。
同样的技术可以推广到 N 层,如下所示:
- for each layer X going front-to-back,from `N - 1` down to `0`
- draw layer X with stencil test `not equal to X + 1`,while writing `X` to the stencil buffer
此代码可能类似于:
glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);
for (int X = N - 1; X >= 0; --X) {
glStencilFunc(GL_NOTEQUAL,X + 1,0xFF);
// drawLayer(X);
}
该算法仅保证层 X
将与层 (X - 1) % N
和 (X + 1) % N
正确交互。如果您想要更复杂的规则,则需要更复杂的算法。
请注意,使用 8 位模板缓冲区时,您将被限制为 255 层。
另请注意,由于此算法不使用深度缓冲区,因此它也适用于 3D 场景 - 只需像平常一样启用深度测试,您就可以开始比赛了。这就是我能够在 3D 中渲染彭罗斯三角形示例的方式。