OpenGL 计算着色器中的样本深度缓冲区

Posted

技术标签:

【中文标题】OpenGL 计算着色器中的样本深度缓冲区【英文标题】:Sample depth buffer in OpenGL compute shader 【发布时间】:2014-02-18 12:49:25 【问题描述】:

我正在尝试将深度纹理采样到计算着色器中并将其复制到其他纹理中。

问题是我从深度纹理中读取时没有得到正确的值:

我尝试检查深度纹理的初始值是否正确(使用 GDebugger),并且确实如此。因此,检索错误值的是 imageLoad GLSL 函数。

这是我的 GLSL 计算着色器:

layout (binding=0, r32f) readonly uniform image2D depthBuffer;
layout (binding=1, rgba8) writeonly uniform image2D colorBuffer;

// we use 16 * 16 threads groups
layout (local_size_x = 16, local_size_y = 16) in;

void    main()

    ivec2       position = ivec2(gl_GlobalInvocationID.xy);
    // Sampling from the depth texture
    vec4        depthSample = imageLoad(depthBuffer, position);
    // We linearize the depth value
    float       f = 1000.0;
    float       n = 0.1;
    float       z = (2 * n) / (f + n - depthSample.r * (f - n));
    // even if i try to call memoryBarrier(), barrier() or memoryBarrierShared() here, i still have the same bug
    // and finally, we try to create a grayscale image of the depth values
    imageStore(colorBuffer, position, vec4(z, z, z, 1));

这就是我创建深度纹理和颜色纹理的方式:

// generate the deth texture
glGenTextures(1, &_depthTexture);
glBindTexture(GL_TEXTURE_2D, _depthTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, wDimensions.x, wDimensions.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);

// generate the color texture
glGenTextures(1, &_colorTexture);
glBindTexture(GL_TEXTURE_2D, _colorTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, wDimensions.x, wDimensions.y, 0, GL_RGBA, GL_FLOAT, NULL);

我用深度值填充深度纹理(将其绑定到帧缓冲区并渲染场景),然后以这种方式调用我的计算着色器:

_computeShader.use();

// try to synchronize with the previous pass
glMemoryBarrier(GL_ALL_BARRIER_BITS);
// even if i call glFinish() here, the result is the same

glBindImageTexture(0, _depthTexture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32F);
glBindImageTexture(1, _colorTexture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA8);

glDispatchCompute((wDimensions.x + WORK_GROUP_SIZE - 1) / WORK_GROUP_SIZE,
                  (wDimensions.y + WORK_GROUP_SIZE - 1) / WORK_GROUP_SIZE, 1); // we divide the compute into groups of 16 threads

// try to synchronize with the next pass
glMemoryBarrier(GL_ALL_BARRIER_BITS);

与:

    wDimensions = 上下文(和帧缓冲区)的大小 WORK_GROUP_SIZE = 16

你知道为什么我没有得到有效的深度值吗?

编辑:

这是我渲染球体时颜色纹理的样子:

而且 glClear(GL_DEPTH_BUFFER_BIT) 似乎什么也没做: 即使我在 glDispatchCompute() 之前调用它,我仍然有相同的图像...... 这怎么可能?

【问题讨论】:

一方面:GL_DEPTH_COMPONENT16 不是浮点图像格式。它是定点的(例如GL_R16)。只有 1 种浮点深度图像格式(如果计算压缩深度 + 模板,则为 2),即 32 位:GL_DEPTH_COMPONENT32F 你是对的,现在我得到了价值,但它们似乎并不好。 这怎么可能? 如果您不将内存屏障与计算着色器一起使用,就会发生很多非常奇怪的事情。由于它们以非常通用的方式访问内存,因此 GL 很难正确调度和同步需要在计算着色器之前/之后完成的操作。普通的图形管道对哪些操作是依赖的等等有一个明确的定义。计算着色器完全打破了这一点,因为它们不是图形管道中的实际阶段,而是完全独立的。 好吧,我尝试在 glDispactchCompute() 之前和之后添加glMemoryBarrier(GL_ALL_BARRIER_BITS);,但它不会改变任何东西。我想我真的不明白 glMemoryBarrier 是如何工作的...... 我认为您还需要在计算着色器本身中使用内存屏障以 100% 确保一切正常。但是,如果您还没有这样做,您肯定可以尝试在调试模式下运行 OpenGL,这可能会暴露额外的错误。 【参考方案1】:

实际上,我发现即使使用 readonly 关键字,您也无法将深度纹理作为图像发送到计算着色器。

所以我换了:

glBindImageTexture(0, _depthTexture, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32F);

作者:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _depthTexture);

在我的计算着色器中:

layout (binding=0, r32f) readonly uniform image2D depthBuffer;

作者:

layout (binding = 0) uniform sampler2D depthBuffer;

为了取样,我只写:

ivec2       position = ivec2(gl_GlobalInvocationID.xy);
vec2        screenNormalized = vec2(position) / vec2(ctxSize); // ctxSize is the size of the depth and color textures
vec4        depthSample = texture2D(depthBuffer, screenNormalized);

而且它像这样工作得很好

【讨论】:

您能告诉我您在哪里发现无法将深度纹理作为 image2D 发送吗?我处于类似情况,但我想写入深度纹理,所以 sampler2D 不适合我。 我也注意到了这种奇怪的行为,即无法将深度缓冲区用作 image2D。但我找不到你不应该这样做的官方说明-.- @Makx 没有明确说明。但是,GL_DEPTH_COMPONENT* 格式不在列出的 glBindImageTexture khronos.org/registry/OpenGL-Refpages/gl4/html/… 支持的格式中

以上是关于OpenGL 计算着色器中的样本深度缓冲区的主要内容,如果未能解决你的问题,请参考以下文章

从固定管线到可编程管线:十段代码入门OpenGL

从固定管线到可编程管线:十段代码入门OpenGL

OpenGL 帧缓冲区颜色附件到计算着色器

打开 gl 计算着色器和帧缓冲区

着色器存储缓冲区中的 OpenGL 顶点

从顶点着色器中修改着色器存储缓冲区对象