访问一个GL_DEPTH_COMPONENT纹理

时间:2022-04-26 04:02:10

I am currently trying to implement screen space ambient occlusion. My idea is to do it, as is standard, with multipass rendering, where I use the depth information of the previous pass to calculate the occlusion factor. Therefore, in my "setup" code i created a framebuffer and texture object and bound those together. The texture was created using GL_DEPTH_COMPONENT24 as internal format and i am issuing a glDrawBuffer(GL_NONE) call since i do not want any color output.

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

Furthermore I have 3 instances of classes called AnimatedObject which have their own draw methods, and a Renderer Class to wrap all of them when drawing. At the moment my Renderer does something along the lines of

此外,我还有3个名为AnimatedObject的类实例,它们有自己的绘制方法,以及一个渲染器类来在绘制时将它们全部包装起来。此时,我的渲染器会按照

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

Where in the above snippet drawIntoDepthBuffer() uses my depth buffer program and drawOntoScreen() uses the main vertex and fragment shaders. The problem i am now facing is the following: I simply can not access the texture I rendered to in the second draw call. The shaders all compile and link fine. Using the depth buffer program to draw to the screen produces perfectly fine output, also not using the rendered texture in the main shader produces the expected results.

在上面的代码片段中,drawIntoDepthBuffer()使用我的深度缓冲程序,而drawOntoScreen()使用主顶点和片段着色器。我现在面临的问题是:我无法访问在第二次绘制调用中呈现的纹理。着色所有编译和链接罚款。使用深度缓冲程序绘制到屏幕上会产生完美的输出,也不会在主着色器中使用渲染纹理产生预期的结果。

The error I get is the generic OpenGL error

我得到的错误是一般的OpenGL错误

error: OpenGL error: Invalid operation (1282)

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

I have also tried using a sampler2Dshadow uniform in the main shader, but in that case I really would not know how to access it, since you need a vec3 to sample it and this simply does not make sense in my case.

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

Here are the different code pieces:

以下是不同的代码片段:

"Main" fragment shader:

“主要”片段着色器:

#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);
}

Depth fragment shader:

深度片段着色器:

#version 430 core

layout(location = 0) out float fragment_depth;

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

The vertex shaders for both stages are identical and look like the following:

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

#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;
}

The corresponding draw calls looks something along those lines:

相应的绘制调用看起来是这样的:

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;
    }
}

And here is the setup for the framebuffer object:

这是framebuffer对象的设置:

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);

Im sorry for so much code, but thats what it needs to tie all this together. Any help would be greatly appreciated.

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


EDIT: After fiddling around for a few hours i found that the error gets raised by a call to glUniformMatrix4fv(location_mvp, 1, GL_TRUE, mvp._m), which as the name suggests, sets the MVP matrix uniform. When I comment this line out, the depth_map sampler can read from the texture (but it obviously only contains garbage values). On the other hand, if i leae this line in, the code only works whe i do not access the depth_map sampler. What could be the cause of this?

编辑:在摆弄了几个小时之后,我发现这个错误是通过调用gl变均矩阵4fv(location_mvp, 1, GL_TRUE, MVP ._m)来引起的,正如名字所示,它设置了MVP矩阵的统一。当我注释这一行时,depth_map采样器可以从纹理中读取(但它显然只包含垃圾值)。另一方面,如果我插入这一行,代码只在我不访问depth_map采样器时才工作。这可能是什么原因造成的呢?


Well, it seems like the location of the mvp matrix changes after the first run of the shading program. It can render the first object fine but when the second object is to be rendered the error pops up. I simply fixed it by looking up the location with glGetUniformLocation, however i still do not understand why this problem manifested itself in such a weird way. First of all, the exact same code works for the main shading program (vertex shader and calling code is exactly the same), and second of all i do not get how the program was able to run when i did not use the depth texture. Is the driver able to optimize away entire rendering passes when they are not used in subsequent passes?

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

1 个解决方案

#1


1  

Basically, you can access a depth map in the same way as any other texture. But, while gl_FragCoord.xy provides window coordinates, texture requires floating point coordinates in range [0.0, 1.0].

基本上,您可以使用与其他纹理相同的方式访问深度映射。不过,尽管gl_FragCoord。xy提供窗口坐标,纹理要求浮动点坐标在范围内[0.0,1.0]。

If the size of the depth map and the size of the viewport are equal (that should be the case in your case), then either use texelFetch:

如果深度映射的大小和viewport的大小相等(在您的例子中应该是这样),那么使用texelFetch:

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

Or you have to divide by the size of the texture (textureSize):

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

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

Note, If the size of the depth map is not equal the viewport size, then you have to provide the viewport size as a uniform variable:

注意,如果深度映射的大小不等于viewport大小,则必须将viewport大小作为统一变量提供:

uniform vec2 vp_size;

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


See GLSL - The OpenGL Shading Language - 4.4.1.3 Fragment Shader Inputs, p. 65

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

4.4.1.3 Fragment Shader Inputs

4.4.1.3片段着色器输入

By default, gl_FragCoord assumes a lower-left origin for window coordinates and assumes pixel centers are located at half-pixel coordinates.

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


Extension of the answer:

扩展的回答:

The first pass is a "depth only" pass. You do not attach any color plane to the frame buffer, so you should not write to any color plane, in the fragment shader, too. You have to use an empty main:

第一个传球是“只有深度”的传球。您不应该将任何颜色平面附加到框架缓冲区,所以您不应该写入任何颜色平面,在碎片着色器中也是如此。你必须使用一个空的主:

void main( void )
{
}

Note, if you want to set the fragment depth explicite, then you have to write to gl_FragDepth. By default the z component of gl_FragCoord is assigned to gl_FragDepth:

注意,如果您想设置片段深度显式,那么您必须写入gl_FragDepth。默认情况下,gl_FragCoord的z分量被分配给gl_FragDepth:

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


Further, if you set the value for a uniform varibale, you have to install the current program (glUseProgram) before you set the uniform varible (glUniformMatrix4fv), because glUniform*specify the value of a uniform variable for the current program object.

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

#1


1  

Basically, you can access a depth map in the same way as any other texture. But, while gl_FragCoord.xy provides window coordinates, texture requires floating point coordinates in range [0.0, 1.0].

基本上,您可以使用与其他纹理相同的方式访问深度映射。不过,尽管gl_FragCoord。xy提供窗口坐标,纹理要求浮动点坐标在范围内[0.0,1.0]。

If the size of the depth map and the size of the viewport are equal (that should be the case in your case), then either use texelFetch:

如果深度映射的大小和viewport的大小相等(在您的例子中应该是这样),那么使用texelFetch:

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

Or you have to divide by the size of the texture (textureSize):

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

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

Note, If the size of the depth map is not equal the viewport size, then you have to provide the viewport size as a uniform variable:

注意,如果深度映射的大小不等于viewport大小,则必须将viewport大小作为统一变量提供:

uniform vec2 vp_size;

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


See GLSL - The OpenGL Shading Language - 4.4.1.3 Fragment Shader Inputs, p. 65

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

4.4.1.3 Fragment Shader Inputs

4.4.1.3片段着色器输入

By default, gl_FragCoord assumes a lower-left origin for window coordinates and assumes pixel centers are located at half-pixel coordinates.

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


Extension of the answer:

扩展的回答:

The first pass is a "depth only" pass. You do not attach any color plane to the frame buffer, so you should not write to any color plane, in the fragment shader, too. You have to use an empty main:

第一个传球是“只有深度”的传球。您不应该将任何颜色平面附加到框架缓冲区,所以您不应该写入任何颜色平面,在碎片着色器中也是如此。你必须使用一个空的主:

void main( void )
{
}

Note, if you want to set the fragment depth explicite, then you have to write to gl_FragDepth. By default the z component of gl_FragCoord is assigned to gl_FragDepth:

注意,如果您想设置片段深度显式,那么您必须写入gl_FragDepth。默认情况下,gl_FragCoord的z分量被分配给gl_FragDepth:

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


Further, if you set the value for a uniform varibale, you have to install the current program (glUseProgram) before you set the uniform varible (glUniformMatrix4fv), because glUniform*specify the value of a uniform variable for the current program object.

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