OpenGL 从当前绑定到帧缓冲区的纹理单元读取
OpenGL Reading from a texture unit currently bound to a framebuffer
我在尝试从当前附加到绘制帧缓冲区的纹理单元读取数据时遇到了问题。只要我在绘制调用之间使用 glTextureBarrier,错误就会被删除。但是我试图消除绘制调用,所以这是一个不理想的解决方案。 OpenGL 4.5 规范(第 9.3 节纹理和帧缓冲区之间的反馈循环)指出
The mechanisms for attaching textures to a framebuffer object do not prevent a
one- or two-dimensional texture level, a face of a cube map texture level, or a
layer of a two-dimensional array or three-dimensional texture from being attached to the draw framebuffer while the same texture is bound to a texture unit. While this condition holds, texturing operations accessing that image will produce unde-fined results, as described at the end of section 8.14.
...
Specifically, the values of rendered fragments are undefined if any shader stage fetches texels and the same texels are written via fragment shader outputs, even if the reads and writes are not in the same draw call, unless any of the following exceptions apply:
The reads and writes are from/to disjoint sets of texels (after accounting for texture filtering rules).
There is only a single read and write of each texel, and the read is in
the fragment shader invocation that writes the same texel (e.g. using texelFetch2D(sampler, ivec2(gl_FragCoord.xy), 0);)
.
If a texel has been written, then in order to safely read the result a texel fetch must be in a subsequent draw call separated by the command void TextureBarrier( void );
TextureBarrier will guarantee that writes have completed and caches have
been invalidated before subsequent draw calls are executed.
我对其他 4 个纹理执行此操作没有问题。对于这些纹理,我只对同一个纹素进行一次读取和一次写入,因此它们包含在第二个例外中。导致问题的纹理需要一些过滤,因此在写入之前我需要多次读取和来自不同的纹素。我认为这可以通过第一个例外被允许,所以我将它们放在一个数组纹理中,交替读取和写入哪个层。这个想法是,这将创建一个设置,其中读取和写入是 from/to 不相交的纹素集。没用。
我还尝试每隔一次绘制调用就执行一次 glTextureBarrier,看看是否是第三次绘制调用导致了问题。当我一直使用障碍和我不使用障碍时,这给出了不同的(仍然是错误的)结果。
绘制调用在 0,0,0 中绘制一个点,该点在几何着色器中分解为全屏四边形。
更新
我正在对体积数据进行光线追踪。
帧缓冲区设置
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rayPositionTexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, rayDirectionTexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, IORTexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, resultTexture, 0);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, rayOffsetTexture, 0, 0);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT5, rayOffsetTexture, 0, 1);
着色器
...
uniform sampler2D RayPositionTexture;
uniform sampler2D RayDirectionTexture;
uniform sampler2DArray RayOffsetTexture;
uniform sampler2D IORTexture;
uniform sampler2D ColorTexture;
...
flat in uint offsetIndex;
layout(location = 0) out vec4 outRayPosition;
layout(location = 1) out vec4 outRayDirection;
layout(location = 2) out float outIOR;
layout(location = 3) out vec4 outColor;
layout(location = 4) out vec4 outRayOffsetA;
layout(location = 5) out vec4 outRayOffsetB;
...
vec3 filteredOffset(ivec2 Pixel)
{
vec3 Offset = vec3(0.0);
float TotalWeight = 0.0;
for(int i = -KernelRadius; i <= KernelRadius; i++)
{
for (int j = -KernelRadius; j <= KernelRadius; j++)
{
ivec2 SampleOffset = ivec2(i,j);
ivec3 SampleLocation = ivec3(Pixel + SampleOffset, offsetIndex);
vec3 Sample = texelFetch(RayOffsetTexture, SampleLocation, 0).xyz;
float Weight = KernelRadius > 0 ? gauss(SampleOffset) : 1.0f;
Offset += Sample * Weight;
TotalWeight += Weight;
}
}
Offset = Offset / TotalWeight;
return Offset;
}
...
//if (offsetIndex == 1)
outRayOffsetA = vec4(RayDirection.xyz - RayDirectionNew, 0.0);
//else
outRayOffsetB = vec4(RayDirection.xyz - RayDirectionNew, 0.0);
outIOR = IOR;
} else {
// if (offsetIndex == 1)
outRayOffsetA = vec4(0.0);
// else
outRayOffsetB = vec4(0.0);
outIOR = PreviousIOR;
//imageStore(VolumeBackTexture, Pixel, vec4(1.0));
}
outColor = Color;
...
绘制调用
GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5 };
glDrawBuffers(6, drawBuffers);
// Run shader
glBindVertexArray(pointVAO);
float distance = 0.0f;
for (int i = 0; distance < 1.73205080757f; i++)
{
glTextureBarrier();
glDrawArrays(GL_POINTS, i, 1);
distance += fUnitStep;
}
glBindVertexArray(0);
上面没有纹理屏障给出了与
相同的结果(错误的结果)
glBindVertexArray(pointVAO);
glDrawArrays(GL_POINTS, 0, int(std::ceil(1.73205080757f / fUnitStep)));
glBindVertexArray(0);
为了回答这个问题,这里需要 TextureBarrier,因为我想从刚刚绘制的纹素中读取。这只有在先前的绘制调用已完成且纹理缓存已失效的情况下才能安全地完成,这正是 TextureBarrier 所保证的。
我在尝试从当前附加到绘制帧缓冲区的纹理单元读取数据时遇到了问题。只要我在绘制调用之间使用 glTextureBarrier,错误就会被删除。但是我试图消除绘制调用,所以这是一个不理想的解决方案。 OpenGL 4.5 规范(第 9.3 节纹理和帧缓冲区之间的反馈循环)指出
The mechanisms for attaching textures to a framebuffer object do not prevent a one- or two-dimensional texture level, a face of a cube map texture level, or a layer of a two-dimensional array or three-dimensional texture from being attached to the draw framebuffer while the same texture is bound to a texture unit. While this condition holds, texturing operations accessing that image will produce unde-fined results, as described at the end of section 8.14. ... Specifically, the values of rendered fragments are undefined if any shader stage fetches texels and the same texels are written via fragment shader outputs, even if the reads and writes are not in the same draw call, unless any of the following exceptions apply:
The reads and writes are from/to disjoint sets of texels (after accounting for texture filtering rules).
There is only a single read and write of each texel, and the read is in the fragment shader invocation that writes the same texel (e.g. using
texelFetch2D(sampler, ivec2(gl_FragCoord.xy), 0);)
.If a texel has been written, then in order to safely read the result a texel fetch must be in a subsequent draw call separated by the command
void TextureBarrier( void );
TextureBarrier will guarantee that writes have completed and caches have been invalidated before subsequent draw calls are executed.
我对其他 4 个纹理执行此操作没有问题。对于这些纹理,我只对同一个纹素进行一次读取和一次写入,因此它们包含在第二个例外中。导致问题的纹理需要一些过滤,因此在写入之前我需要多次读取和来自不同的纹素。我认为这可以通过第一个例外被允许,所以我将它们放在一个数组纹理中,交替读取和写入哪个层。这个想法是,这将创建一个设置,其中读取和写入是 from/to 不相交的纹素集。没用。
我还尝试每隔一次绘制调用就执行一次 glTextureBarrier,看看是否是第三次绘制调用导致了问题。当我一直使用障碍和我不使用障碍时,这给出了不同的(仍然是错误的)结果。
绘制调用在 0,0,0 中绘制一个点,该点在几何着色器中分解为全屏四边形。
更新
我正在对体积数据进行光线追踪。
帧缓冲区设置
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rayPositionTexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, rayDirectionTexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, IORTexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, resultTexture, 0);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, rayOffsetTexture, 0, 0);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT5, rayOffsetTexture, 0, 1);
着色器
...
uniform sampler2D RayPositionTexture;
uniform sampler2D RayDirectionTexture;
uniform sampler2DArray RayOffsetTexture;
uniform sampler2D IORTexture;
uniform sampler2D ColorTexture;
...
flat in uint offsetIndex;
layout(location = 0) out vec4 outRayPosition;
layout(location = 1) out vec4 outRayDirection;
layout(location = 2) out float outIOR;
layout(location = 3) out vec4 outColor;
layout(location = 4) out vec4 outRayOffsetA;
layout(location = 5) out vec4 outRayOffsetB;
...
vec3 filteredOffset(ivec2 Pixel)
{
vec3 Offset = vec3(0.0);
float TotalWeight = 0.0;
for(int i = -KernelRadius; i <= KernelRadius; i++)
{
for (int j = -KernelRadius; j <= KernelRadius; j++)
{
ivec2 SampleOffset = ivec2(i,j);
ivec3 SampleLocation = ivec3(Pixel + SampleOffset, offsetIndex);
vec3 Sample = texelFetch(RayOffsetTexture, SampleLocation, 0).xyz;
float Weight = KernelRadius > 0 ? gauss(SampleOffset) : 1.0f;
Offset += Sample * Weight;
TotalWeight += Weight;
}
}
Offset = Offset / TotalWeight;
return Offset;
}
...
//if (offsetIndex == 1)
outRayOffsetA = vec4(RayDirection.xyz - RayDirectionNew, 0.0);
//else
outRayOffsetB = vec4(RayDirection.xyz - RayDirectionNew, 0.0);
outIOR = IOR;
} else {
// if (offsetIndex == 1)
outRayOffsetA = vec4(0.0);
// else
outRayOffsetB = vec4(0.0);
outIOR = PreviousIOR;
//imageStore(VolumeBackTexture, Pixel, vec4(1.0));
}
outColor = Color;
...
绘制调用
GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5 };
glDrawBuffers(6, drawBuffers);
// Run shader
glBindVertexArray(pointVAO);
float distance = 0.0f;
for (int i = 0; distance < 1.73205080757f; i++)
{
glTextureBarrier();
glDrawArrays(GL_POINTS, i, 1);
distance += fUnitStep;
}
glBindVertexArray(0);
上面没有纹理屏障给出了与
相同的结果(错误的结果)glBindVertexArray(pointVAO);
glDrawArrays(GL_POINTS, 0, int(std::ceil(1.73205080757f / fUnitStep)));
glBindVertexArray(0);
为了回答这个问题,这里需要 TextureBarrier,因为我想从刚刚绘制的纹素中读取。这只有在先前的绘制调用已完成且纹理缓存已失效的情况下才能安全地完成,这正是 TextureBarrier 所保证的。