正在读取绑定到帧缓冲区的纹理单元的OpenGL。

4
我遇到了一个问题,尝试从当前附加到绘制帧缓冲区的纹理单元中读取数据。只要在绘制调用之间使用glTextureBarrier,就可以消除错误。然而,我正在尝试消除绘制调用,所以这是一种不太理想的解决方案。OpenGL 4.5规范(第9.3节纹理和帧缓冲之间的反馈循环)指出:
附加纹理到帧缓冲对象的机制不会防止将一维或二维纹理级别、立方体贴图纹理级别的面或二维数组或三维纹理的层附加到绘制帧缓冲区,同时相同的纹理绑定到纹理单元。在这种情况下,访问该图像的纹理操作将产生未定义的结果,如第8.14节末尾所述。...
特别地,如果任何着色器阶段获取了纹素并且相同的纹素通过片段着色器输出被写入,则渲染片段的值未定义,即使读取和写入不在同一绘制调用中,除非满足以下任何一种情况:
- 读取和写入来自/去往互不相交的纹素集(在考虑纹理过滤规则之后)。 - 只有每个纹素的单一读取和写入,并且读取在写入相同纹素的片段着色器调用中进行(例如,使用texelFetch2D(sampler, ivec2(gl_FragCoord.xy), 0);)。 - 如果已经写入纹素,则为了安全地读取结果,必须将纹素获取置于通过命令void TextureBarrier( void );分隔的后续绘制调用中。TextureBarrier将确保写入完成并在执行后续绘制调用之前使缓存失效。
我对其他4个纹理做了同样的操作而没有问题。对于这些纹理,我只从相同的纹素处读取和写入一次,因此它们符合第二个例外。导致问题的纹理需要一些过滤,因此我需要从不同的纹素中多次读取和写入。我想到这可能允许通过第一个例外,因此我将它们放在交替读取和写入哪个图层的数组纹理中。我的想法是这将创建一个从/到互不相交的纹素集的设置。但这没有帮助。
我还尝试仅在每个其他绘制调用中使用glTextureBarrier,以查看是否第三个绘制调用会导致问题。这与我一直使用屏障和不使用屏障时产生了不同(仍然错误)的结果。
绘制调用会在0,0,0处绘制一个点,然后在几何着色器中扩展为全屏四边形。
更新:
我正在通过卷积数据进行光线追踪。
帧缓冲区设置
    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rayPositionTexture, 0);
    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, rayDirectionTexture, 0);
    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, IORTexture, 0);
    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, resultTexture, 0);
    glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, rayOffsetTexture, 0, 0);
    glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT5, rayOffsetTexture, 0, 1);

着色器

...
uniform sampler2D RayPositionTexture;
uniform sampler2D RayDirectionTexture;
uniform sampler2DArray RayOffsetTexture;
uniform sampler2D IORTexture;
uniform sampler2D ColorTexture;
...
flat in uint offsetIndex;
layout(location = 0) out vec4  outRayPosition;
layout(location = 1) out vec4  outRayDirection;
layout(location = 2) out float outIOR;
layout(location = 3) out vec4  outColor;
layout(location = 4) out vec4  outRayOffsetA;
layout(location = 5) out vec4  outRayOffsetB;
...
vec3 filteredOffset(ivec2 Pixel)
{
    vec3 Offset = vec3(0.0);
    float TotalWeight = 0.0;
    for(int i = -KernelRadius; i <= KernelRadius; i++)
    {
        for (int j = -KernelRadius; j <= KernelRadius; j++)
        {
            ivec2 SampleOffset = ivec2(i,j);
            ivec3 SampleLocation = ivec3(Pixel + SampleOffset, offsetIndex);
            vec3  Sample = texelFetch(RayOffsetTexture, SampleLocation, 0).xyz;
            float Weight = KernelRadius > 0 ? gauss(SampleOffset) : 1.0f;

            Offset += Sample * Weight;
            TotalWeight += Weight;
        }
    }
    Offset = Offset / TotalWeight;
    return Offset;
}
...
        //if (offsetIndex == 1)
        outRayOffsetA = vec4(RayDirection.xyz - RayDirectionNew, 0.0);
        //else
        outRayOffsetB = vec4(RayDirection.xyz - RayDirectionNew, 0.0);
        outIOR = IOR;
    } else {
        // if (offsetIndex == 1)
        outRayOffsetA = vec4(0.0);
        // else
        outRayOffsetB = vec4(0.0);
        outIOR = PreviousIOR;
        //imageStore(VolumeBackTexture, Pixel, vec4(1.0));
    }
    outColor = Color;
...

绘制调用

GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5 };
glDrawBuffers(6, drawBuffers);

// Run shader
glBindVertexArray(pointVAO);
float distance = 0.0f;
for (int i = 0; distance < 1.73205080757f; i++)
{
    glTextureBarrier();
    glDrawArrays(GL_POINTS, i, 1); 
    distance += fUnitStep;
}
glBindVertexArray(0);

没有纹理屏障的情况下,与以下结果相同(错误结果)
glBindVertexArray(pointVAO);
glDrawArrays(GL_POINTS, 0, int(std::ceil(1.73205080757f / fUnitStep)));
glBindVertexArray(0); 

除非您提供一些代码,否则很难说这里出了什么问题... - peppe
@peppe 嘿,这里有很多代码,哪些部分会对你有帮助呢?我在帖子中没有提到的一点是,数组纹理的层附加到不同的颜色附件上。但据我所知,那是唯一的方法。 - Jens
至少提供您FBO设置的草图,着色器的相关部分,以及您要渲染的数据类型。 - peppe
@peppe 我已经更新了问题并附上了代码。 - Jens
1个回答

0
为了回答这个问题,这里需要使用TextureBarrier,因为我想要从刚刚绘制的纹素中读取。只有在前一个绘制调用完成并且纹理缓存已被清空时,才能安全地执行此操作,而这正是TextureBarrier所保证的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接