我试图计算图像上的原子移动平均值,但平均值是错误的,当我用相同的数据多次运行它时,平均值每次都不同。 我有一个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);
读取程序只是在四边形上绘制纹理元素
答案 0 :(得分:0)
我通过将convRGBA8ToVec4
和convVec4ToRGBA8
替换为packUnorm4x8
和unpackUnorm4x8
来解决问题,这要归功于这篇文章: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));
}
}