图像上的原子移动平均线

Atomic moving average on image

本文关键字:移动 图像      更新时间:2023-10-16

我正在尝试计算图像上的原子移动平均值,但是平均值是错误的,当我多次使用相同的数据运行它时,平均值每次都不同。 我有一个 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);

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

我通过将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));
}
}