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

问题描述:

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

#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 - 请参阅here。但正如我的例子所显示的,他们显然影响他们!还有什么可以解决这个问题吗?

如果这很重要,我使用intel skylake iGPU,OpenGL 4.3在linux下运行它。

+0

您确定深度测试正常吗? (已启用深度测试,没有写入蒙版,深度func可以...)您可以通过更改三角形的Z值并查看顶部三角形是否更改来检查此问题。你使用哪个Z值?对两个三角形使用完全相同的值可能会导致您的问题。你是否尝试手动丢弃一些碎片以查看这是否改变了原子计数器? – dv1729

+1

你以哪种顺序绘制三角形?当您绘制红色然后绿色时,结果并不令人惊讶,因为红色三角形在渲染时完全可见。 – BDL

+0

我提到我用两个三角形(6点)提供着色器 - 我只需一次调用DrawArrays即可完成此操作。 – aplavin

处理来自不同三角形的碎片的顺序在很大程度上是未定义的。早期的片段测试不会强制每个三角形的片段按照光栅化程序进行处理。虽然OpenGL主要是在“as-if”规则下工作的(即:一切正常,“按原样”按顺序处理),但图像加载/存储和SSBO存储器访问的不连贯特性意味着您不能依赖in-订单处理。

如果你想发出一批作品,并且FS只对每个片段执行一次,那么你将不得不进行深度预传。首先,你渲染场景,但没有FS;这意味着唯一被写入的东西是深度值。接下来,您正常渲染场景。

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