访问GL_DEPTH_COMPONENT纹理

时间:2018-01-04 08:03:58

标签: c++ glsl

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

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

的方式做了一些事情
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;
    }
}

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

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

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

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

1 个答案:

答案 0 :(得分:1)

基本上,您可以像访问任何其他纹理一样访问深度贴图。 但是,虽然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 - The OpenGL Shading Language - 4.4.1.3 Fragment Shader Inputs, p. 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;
}


此外,如果您设置统一varibale的值,则必须先安装当前程序(glUseProgram,然后才能设置统一变量(glUniformMatrix4fv,因为{{ 1}}为当前程序对象指定统一变量的值。