仅对可见像素执行片段着色器

时间:2016-10-25 10:09:30

标签: opengl shader fragment-shader

我要对输出的每个像素进行计算,并且它使用从着色器管道中的早期步骤传递的一些数据 - 因此在片段着色器中执行此计算是最有意义的。为了看看它是否可能,我从最简单的例子开始 - 只计算每个基元的像素。这只需要两个着色器 - 顶点着色器:

#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下运行它。

1 个答案:

答案 0 :(得分:3)

处理来自单独三角形的片段的顺序很大程度上是未定义的。早期的片段测试不会强制以光栅化器顺序处理每个三角形的片段。虽然OpenGL主要在" as-if"规则(即:一切正常" as-if"它按顺序处理),图像加载/存储和SSBO内存访问的不连贯性意味着您不能依赖于有序处理。

如果您想发布一批工作并且FS只为每个片段执行一次,那么您将不得不进行深度预传。首先,渲染场景,但根本没有FS;这意味着唯一能写的是深度值。接下来,您正常渲染场景。

在这两者之间应该是某种形式的同步,以确保第一遍中的所有三角形在第二遍开始之前完成。不幸的是,OpenGL没有一个体面的方式来要求它。您可以use a fence sync object with glWaitSync,但这需要明确的glFlush来电,这并不是很便宜。