访问GL_DEPTH_COMPONENT纹理

Accessing a GL_DEPTH_COMPONENT texture

本文关键字:纹理 COMPONENT DEPTH 访问 GL      更新时间:2023-10-16

>我目前正在尝试实现屏幕空间环境光遮蔽。我的想法是按照标准方式使用多通道渲染,其中我使用前一个通道的深度信息来计算遮挡因子。因此,在我的"设置"代码中,我创建了一个帧缓冲和纹理对象并将它们绑定在一起。纹理是使用 GL_DEPTH_COMPONENT24 作为内部格式创建的,我正在发出 glDrawBuffer(GL_NONE) 调用,因为我不想要任何颜色输出。

此外,我有 3 个名为 AnimatedObject 的类实例,它们有自己的绘制方法,还有一个渲染器类在绘制时包装所有这些方法。目前,我的渲染器做了一些类似于

drawIntoDepthBuffer(); 
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
drawOntoScreen();

在上面的代码片段中,drawIntoDepthBuffer()使用我的深度缓冲区程序,drawOntoScreen()使用主顶点和片段着色器。我现在面临的问题如下:我根本无法访问在第二次绘制调用中渲染到的纹理。着色器都可以很好地编译和链接。使用深度缓冲区程序绘制到屏幕上会产生完美的输出,也不在主着色器中使用渲染的纹理会产生预期的结果。

我得到的错误是通用的OpenGL错误

错误: OpenGL 错误: 无效操作 (1282)

我也尝试在主着色器中使用sampler2Dshadow制服,但在这种情况下,我真的不知道如何访问它,因为您需要一个vec3来采样它,这在我的情况下根本没有意义。

以下是不同的代码片段:

"主"片段着色器:

#version 430 core
layout (binding = 0) uniform sampler2D texture_main;
layout (binding = 1) uniform sampler2D depth_map;
in vec3 normal;
in vec2 uv;
out vec4 outColor;
void main()
{
float test = texture(depth_map, gl_FragCoord.xy / textureSize(depth_map, 0)).r; //<---- this fails
float lambert = clamp(dot(normalize(normal), normalize(vec3(-0.5,1,1))), 0.2f, 1);
outColor = test * lambert * texture(texture_main, uv);
}

深度片段着色器:

#version 430 core
layout(location = 0) out float fragment_depth;
void main() {
fragment_depth = gl_FragCoord.z;
}

两个阶段的顶点着色器相同,如下所示:

#version 430 core
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec3 inNormal;
layout (location = 2) in vec2 inUv;
uniform mat4 mvp;
out vec3 normal;
out vec2 uv;
void main()
{
gl_Position = mvp * inPos;
normal = inverse(transpose(mvp)) * inNormal;
uv = inUv;
}

相应的绘制调用看起来像这些行:

void draw_screen(GLuint depth_texture)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(main_program);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, depth_texture);
for (const auto& surface : surfaces)
{
glActiveTexture(GL_TEXTURE0));
glBindTexture(GL_TEXTURE_2D, surface);
glDrawArrays(GL_TRIANGLES, pos, count);
pos += pos_increment;
}
}
void draw_depth(GLuint framebuffer_id)
{
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
glUseProgram(depth_program);
for (const auto& surface : surfaces)
{
glBindTexture(GL_TEXTURE_2D, surface);
glDrawArrays(GL_TRIANGLES, pos, count);
pos += pos_increment;
}
}

下面是帧缓冲对象的设置:

glGenFramebuffers(1, &depth_framebuffer_id);
glBindFramebuffer(GL_FRAMEBUFFER, depth_framebuffer_id);
glGenTextures(1, &depth_texture_id);
glBindTexture(GL_TEXTURE_2D, depth_texture_id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, viewport_width, viewport_height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_texture_id, 0);
glDrawBuffer(GL_NONE);

我很抱歉有这么多代码,但这就是将所有这些联系在一起所需要的。任何帮助将不胜感激。


编辑:在摆弄了几个小时后,我发现错误是由调用glUniformMatrix4fv(location_mvp, 1, GL_TRUE, mvp._m)引发的,顾名思义,它将MVP矩阵设置为统一。当我注释掉这一行时,depth_map采样器可以从纹理中读取(但它显然只包含垃圾值)。另一方面,如果我把这一行放进去,代码只有在我不访问depth_map采样器时才有效。这可能是什么原因?


好吧,似乎在第一次运行着色程序后,mvp 矩阵的位置会发生变化。它可以很好地渲染第一个对象,但是当第二个对象要渲染时,会弹出错误。我只是通过查找glGetUniformLocation的位置来修复它,但是我仍然不明白为什么这个问题以如此奇怪的方式表现出来。首先,完全相同的代码适用于主着色程序(顶点着色器和调用代码完全相同),其次,当我不使用深度纹理时,我不明白程序是如何运行的。当后续传递中未使用整个渲染通道时,驱动程序是否能够优化它们?

基本上,您可以像访问任何其他纹理一样访问深度图。 但是,虽然gl_FragCoord.xy提供窗口坐标,但texture需要范围 [0.0, 1.0] 的浮点坐标。

如果深度贴图的大小和视口的大小相等(在您的情况下应该是这种情况),则使用texelFetch

float test = texelFetch(depth_map, ivec2(gl_FragCoord.xy), 0).r;

或者你必须除以纹理的大小(textureSize):

float test = texture(depth_map, gl_FragCoord.xy / textureSize(depth_map, 0)).r;

注意,如果深度图的大小不等于视口大小,则必须提供视口大小作为统一变量:

uniform vec2 vp_size;
float test = texture(depth_map, gl_FragCoord.xy / vp_size).r;


参见 GLSL - OpenGL 着色语言 - 4.4.1.3 片段着色器输入,第 65 页

4.4.1.3 片段着色器输入

默认情况下,gl_FragCoord假定窗口坐标的原点左下角,并假定像素中心位于半像素坐标处。


答案的扩展:

第一遍是"仅深度"刀路。您不会将任何颜色平面附加到帧缓冲区,因此也不应写入片段着色器中的任何颜色平面。您必须使用空main

void main( void )
{
}

注意,如果要显式设置片段深度,则必须写入gl_FragDepth。默认情况下,gl_FragCoordz组件分配给gl_FragDepth

void main( void )
{
gl_FragDepth = gl_FragCoord.z;
}


此外,如果设置了统一变量的值,则必须在设置统一变量(glUniformMatrix4fv)之前安装当前程序(glUseProgram),因为glUniform*当前程序对象指定统一变量的值。