图像上的原子移动平均线

时间:2017-03-28 16:37:15

标签: c++ opengl glsl

我试图计算图像上的原子移动平均值,但平均值是错误的,当我用相同的数据多次运行它时,平均值每次都不同。 我有一个RGBA8纹理,但我将它绑定为R32UI,因为我需要原子操作和纹理过滤。

我为平均做了一个测试应用程序,因为实际的应用程序非常复杂。在测试应用程序中,我在单个纹素上的片段着色器中进行平均,所以我有一个1x1 RGBA8纹理,绑定为R32UI。我正在渲染一个大小为500x500的四边形,并且每个片段着色器调用都会在纹素中为平均值添加蓝色或红色值,具体取决于片段坐标。

片段着色器:

#version 450 core

layout (binding = 0, r32ui) volatile uniform uimage2D img;

layout (location = 0) uniform int width;

vec4 convRGBA8ToVec4( uint val)
{
    return vec4(float((val & 0x000000FF)), float((val & 0x0000FF00) >> 8U),
                float((val & 0x00FF0000) >> 16U), float((val & 0xFF000000) >> 24U));
}

uint convVec4ToRGBA8( vec4 val)
{
    return (uint(val.w) & 0x000000FF) << 24U | (uint(val.z) & 0x000000FF) << 16U |
           (uint(val.y) & 0x000000FF) << 8U  | (uint(val.x) & 0x000000FF);
}

void imageAtomicRGBA8Avg(layout (r32ui) volatile uimage2D imgUI, ivec2 coords , vec4 val)
{
    val.rgb *= 255.0f;
    uint newVal = convVec4ToRGBA8(val);
    uint prevStoredVal = 0;
    uint curStoredVal;

    // Loop as long as destination value gets changed by other threads
    while((curStoredVal = imageAtomicCompSwap(imgUI, coords, prevStoredVal, newVal))
          != prevStoredVal)
    {
        prevStoredVal = curStoredVal;
        vec4 unpacked = convRGBA8ToVec4(curStoredVal);
        vec3 x = unpacked.xyz;
        float n = unpacked.w;
        vec4 curValF = vec4(vec3(((n * x) + val.xyz) / n), n + 1);
        newVal = convVec4ToRGBA8(curValF);
    }
}

void main()
{
    int idx = int(gl_FragCoord.y * width) + int(gl_FragCoord.x);

    if(idx % 2 == 0)
    {
        // Add red
        imageAtomicRGBA8Avg(img, ivec2(0, 0), vec4(1.0f, 0.0f, 0.0f, 1.0f));
    }
    else
    {
        // Add blue
        imageAtomicRGBA8Avg(img, ivec2(0, 0), vec4(0.0f, 0.0f, 1.0f, 1.0f));
    }
}

平均颜色介于红色和蓝色之间,有时甚至是纯红色或蓝色。我想知道为什么会这样,我在一篇论文中找到了平均功能,所以它实际上应该有用。

这里是初始代码:

glCreateTextures(GL_TEXTURE_2D, 1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);

glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);

glBindTextureUnit(0, texture);
glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32UI);

和渲染代码:

glUseProgram(atomic_avg_prog);
glUniform1i(0, window_width);
glDrawArrays(GL_TRIANGLES, 0, 6);

glUseProgram(read_prog);
glDrawArrays(GL_TRIANGLES, 0, 6);

读取程序只是在四边形上绘制纹理元素

1 个答案:

答案 0 :(得分:0)

我通过将convRGBA8ToVec4convVec4ToRGBA8替换为packUnorm4x8unpackUnorm4x8来解决问题,这要归功于这篇文章:https://rauwendaal.net/2013/02/07/glslrunningaverage/

该解决方案不允许平均超过255个值,因为计数器只存储在8位的alpha中,但在我的情况下足够

void imageAtomicAvgRGBA8(layout (r32ui) volatile uimage2D imgUI, ivec2 coords , vec4 val)
{
    uint newVal = packUnorm4x8(vec4(val.xyz, 1.0f / 255.0f));
    uint prevStoredVal = 0;
    uint curStoredVal;

    // Loop as long as destination value gets changed by other threads
    while((curStoredVal = imageAtomicCompSwap(imgUI, coords, prevStoredVal, newVal))
          != prevStoredVal)
    {
        prevStoredVal = curStoredVal;
        vec4 unpacked = unpackUnorm4x8(curStoredVal);
        vec3 x = unpacked.xyz;

        float n = unpacked.w * 255.0f;

        x = (x * n + val.xyz) / (n + 1);
        newVal = packUnorm4x8(vec4(x, (n + 1) / 255.0f));
    }
}