在片段着色器中,我从纹理(由另一个片段着色器创建)中读取(使用texelFetch)多次相同的texel,并将其写入输出渲染缓冲区。在texel上进行几次读取操作后,会发生故障。
纹理和渲染图像的大小为(n * 256)x(m * 192)像素/ texel。在第一个实例中(texel [0,...,255] x [0,...,191])纹理包含一个人类。
这是片段着色器的最小代码,应该复制人类:
#version 450
// output render buffer
layout(location = 5) out float depth;
in vec4 gl_FragCoord;
uniform sampler2DRect GradientsTexture;
void main(){
ivec2 pos = ivec2(mod(gl_FragCoord.x,256), mod(gl_FragCoord.y,192));
vec4 gradient = texelFetch(GradientsTexture, pos);
depth = gradient.x;
}
执行后输出如下:
看到,在第三个实例之后有一个问题。另外我注意到黑框/条纹的位置随机变化。
您是否知道如何解决此问题?
长VERSION
基本上,我的问题涉及OpenGL的Fragment Shader中的纹理访问。首先,我将描述问题的背景以及我想要实现的目标。然后,我将解释问题的出处。最后,我将介绍几行代码,然后是我的实际问题。如果您对详细信息不感兴趣,可以直接跳到问题所在。但是也许详细的解释对其他人还是有用的。
背景基本上我想根据等式(15)计算渲染深度图像的偏导数。 ),本文中的魏,小林,张培钊和柴锦祥。 “使用单深度相机进行实时准确的全身运动捕捉。” ACM Transactions on Graphics(TOG)31.6(2012):188。
导数
本文中有两个重要的导数需要计算:
1。参数梯度
深度值$ D $相对于移动骨架的参数$ q_k $的变化:
$$ \ frac {\ partial D_c} {\ partial \ vec {q}} = \ left [\ frac {\ partial D_c} {\ partial q_1},\; ...,\; \ frac {\ partial D_c} {\ partial q_k},\; ... \ right] $$
但是由于我使用的是多个摄像头,因此我得到了同一模型的不同深度图像。在此示例中,使用了两个摄像头($ c = 1,2 $),因此我们有$ D_1 $和$ D_2 $。
下图可以看到此渐变。我将此图像称为“渐变纹理$ G $”。在这里,我们看到了同一模型的多个实例。左上角的第一个实例(0)与其他实例相反,不是原始值,而是原始深度值$ D_1 $。第二个实例是相对于整个模型绕x轴旋转的深度变化(这是参数$ q_1 $)($ q_7 $是大腿的角度)。在计算完所有$ \ frac {\ partial D_1} {\ partial q_k} $之后,我们继续$ c = 2 $并看到$ D_2 $,依此类推。
图1 :有关参数的原始深度图像和深度梯度。 (称为Gradient-Texture $ G $)
2。图像梯度
图像梯度是众所周知的,必须通过将卷积运算符$ \ left [-1/2,0,+1/2 \ right] $应用于$ D_1 $来计算和$ D_2 $。通过在x和y方向上执行此操作,我们得到:
$$ \ nabla D_c = \ left [\ frac {\ partial D_c} {\ partial x},\ frac {\ partial D_c} {\部分y} \ right]
$$
求和
最后我必须总结两个导数:
$ $ \ nabla D_c \ frac {\ partial \ vec {x}} {\ partial \ vec {q}} + \ frac {\ partial D_c} {\ partial \ vec {q}} \;。 \; \; (1)$$
其中$ \ frac {\ partial \ vec {x}} {\ partial \ vec {q}} $是模型上投影点的图像坐标的变化关于参数。在这里应用正交投影时,这成为常数。
问题
为了计算等式(1)中的总和,我需要将梯度添加到每个$ \ frac {\ partial D_c} {\ partial q_k} $。因此,我需要在每个实例中访问$ D_c $,计算导数并将其添加到实例$ \ frac {\ partial D_c} {\ partial q_k} $中。
这是在片段着色器中完成的,该片段着色器将图1作为输入(如GL_TEXTURE_RECTANGLE)。由于每个实例$ i $不知道在纹理中的原始深度图像($ D_c $)的位置,因此需要额外输入x和y偏移从当前像素/纹理位置到相应的像素/纹理位置提供了原始深度图像的:我们将其称为$ O_x $和$ O_y $。因此,要获取渐变纹理$ G $中原始深度图像$ D_c $的坐标,请执行以下操作:
$$
x_D = x + O_x(x,y)\; \,\ ; \; \; \; \; \; \; \; \\
y_D = y + O_y(x,y)\ ;, \; \; \; (2)$$
,其中$ x $,$ y $是片段着色器中的当前像素坐标(gl_fragCoord),$ x_D $是分配给渐变的原始深度图像$ D_c $的x坐标-$(x,y)$处的纹理。
下图显示了偏移纹理$ O_x $。
图2:偏移纹理,将坐标从“渐变纹理”中每个实例的点转换为“渐变纹理”中原始深度图像的坐标。
进行等式(2)中的计算时)我得到了我称之为的基本坐标$ x_D $。它们显示在下面的图像中。
图3:每个实例的原始深度图像的基本位置$ x_D $。
现在当我使用计算出的基坐标访问Gradient-Texture的值时,出现错误。结果显示在下面的图4中。我希望每个实例都能显示出它分配的原始深度$ D_c $。但正如您看到的那样,这里有黑色的人工制品。之后,我将不得不计算渐变图像$ \ nabla D_c $,但由于明显存在故障,我在这里停了下来。
图4:使用计算出的基本坐标从渐变纹理$ G $中提取的原始深度$ D_c $
代码
为了提供有用的反馈,我假设您需要该代码。在下面的内容中,您将找到Fragment Shader代码。
变量
GradientsTexture
包含$ G $和
GradientsOffset
包含$ O_x $和$ O_y $。
#version 450
// locations of the output data
layout(location = 5) out float depth2;
in vec4 gl_FragCoord;
uniform sampler2DRect GradientsTexture;
uniform sampler2DRect GradientsOffset;
void main(){
ivec2 pos = ivec2(gl_FragCoord.x-0.5, gl_FragCoord.y-0.5);
ivec2 offset = ivec2(texelFetch(GradientsOffset, pos).xy);
ivec2 basePos = pos+offset;
vec4 gradient = texelFetch(GradientsTexture, basePos);
depth2 = gradient.x;
}
问题
最后是我的问题:有人知道这些人工制品来自何处吗?如您在上图中所看到的,之前的步骤给出了正确的结果。仅使用
vec4 gradient = texelFetch(GradientsTexture, basePos);
访问渐变纹理会导致意外结果,如图4所示。预先感谢,对于详细说明。
干杯,
克里斯蒂安·布洛赫。
评论
您的CPU代码是否在多个线程上调用GL?我的第一个猜测是着色器没有问题,这是由于某些非线程安全使用中间纹理而导致的缓存失效问题。谢谢,不,没有多线程执行CPU代码。顺便说一句:现在,我尝试了以下操作:vec2 basePos = vec2(mod(pos.x,256),mod(pos.y,192));其中一个实例是256x192,我仍然遇到此错误。可以在texel上执行的读取次数似乎受到限制吗?
另外我发现,使用mod(...)版本时黑匣子的位置随机变化
在您的简短版本中,着色器看起来不错。深度缓冲区或模板缓冲区中的垃圾可以很容易地做到这一点(请确保深度测试已禁用)。在GL错误方面,您是否尝试过强制使用cpu渲染器或其他供应商的GPU?
只需一个问题,您的渐变纹理就是矩形纹理。只是想知道您的应用程序代码中您没有意外启用纹理过滤模式。仅将最小过滤器GL_TEXTURE_MIN_FILTER设置为GL_NEAREST或GL_LINEAR。每个坐标的换行模式也必须为GL_CLAMP_TO_EDGE或GL_CLAMP_TO_BORDER。另请参见mipmap未打开。着色器中的另一件事是确保texelFetch pos未标准化。