我要对输出的每个像素进行计算,并且它使用从着色器管道中的早期步骤传递的一些数据 - 因此在片段着色器中执行此计算是最有意义的。为了看看它是否可能,我从最简单的例子开始 - 只计算每个基元的像素。这只需要两个着色器 - 顶点着色器:
#version 430
in vec3 position;
void main() {{
gl_Position = vec4(position, 1);
}}
和片段着色器:
#version 430
layout(early_fragment_tests) in;
out vec4 out_color;
layout(std430, binding = 3) buffer out_data {
int data[];
};
void main() {{
atomicAdd(data[gl_PrimitiveID], 1);
out_color = vec4(1, gl_PrimitiveID, 0, 1);
}}
正如您所看到的,它只是增加着色器存储缓冲区对象的元素。
然后我用两个三角形(6分)喂它:[-1, -1, 0], [-1, 1, 0], [1, 1, 0], [-1, -1, 0], [1, -1, 0], [-1, 1, -1]
。它正确显示一个红色三角形和一个绿色三角形,每个三角形占据窗口的一半,但绿色三角形位于顶部 - 所以红色三角形只有一半可见(窗口的1/4)。
当然我预计红色三角形的窗口大小约为1/4,绿色的大约为1/2 - 但它们都等于1/2!顺便说一句,如果我将所有输入Z坐标设置为零,则红色三角形位于顶部,绿色半隐藏 - 在这种情况下,计数是正确的。
在阅读OpenGL文档(我找到选项early_fragment_tests
)时,我理解因任何原因丢弃的片段(例如我的情况下的深度测试)不会影响原子计数器和SSBO - 请参阅{{3} }。但正如我的例子所示,他们显然会影响他们!还有什么能解决这个问题吗?
如果这很重要,我使用intel skylake iGPU,OpenGL 4.3在linux下运行它。
答案 0 :(得分:3)
处理来自单独三角形的片段的顺序很大程度上是未定义的。早期的片段测试不会强制以光栅化器顺序处理每个三角形的片段。虽然OpenGL主要在" as-if"规则(即:一切正常" as-if"它按顺序处理),图像加载/存储和SSBO内存访问的不连贯性意味着您不能依赖于有序处理。
如果您想发布一批工作并且FS只为每个片段执行一次,那么您将不得不进行深度预传。首先,渲染场景,但根本没有FS;这意味着唯一能写的是深度值。接下来,您正常渲染场景。
在这两者之间应该是某种形式的同步,以确保第一遍中的所有三角形在第二遍开始之前完成。不幸的是,OpenGL没有一个体面的方式来要求它。您可以use a fence sync object with glWaitSync
,但这需要明确的glFlush
来电,这并不是很便宜。