如何在OpenGL ES上高效地将深度缓冲区复制到纹理?

4
我正在尝试通过从标准GL移植某些代码,在iOS上使用OpenGL ES 2.0实现一些阴影效果。示例的一部分涉及将深度缓冲区复制到纹理中:
glBindTexture(GL_TEXTURE_2D, g_uiDepthBuffer);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 0, 0, 800, 600, 0);

然而,似乎ES不支持glCopyTexImage2D。阅读相关主题时,我发现可以使用帧缓冲区和片段着色器提取深度数据。因此,我正在尝试将深度组件写入颜色缓冲区,然后复制它。
// clear everything
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 

// turn on depth rendering
glUseProgram(m_BaseShader.uiId);

// this is a switch to cause the fragment shader to just dump out the depth component
glUniform1i(uiBaseShaderRenderDepth, true);

// and for this, the color buffer needs to be on
glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);

// and clear it to 1.0, like how the depth buffer starts
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

// draw the scene
DrawScene();

// bind our texture 
glBindTexture(GL_TEXTURE_2D, g_uiDepthBuffer);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, width, height, 0);

这是片元着色器:

uniform sampler2D sTexture;
uniform bool bRenderDepth;

varying lowp    float LightIntensity;
varying mediump vec2  TexCoord;

void main()
{    
    if(bRenderDepth) {
        gl_FragColor = vec4(vec3(gl_FragCoord.z), 1.0);
    } else {
        gl_FragColor = vec4(texture2D(sTexture, TexCoord).rgb * LightIntensity, 1.0);
    }
}

我已经尝试过没有“bRenderDepth”分支,但它并没有明显加快速度。
现在仅仅完成这一步骤它的帧率为14fps,显然是不可接受的。如果我去掉复制,它的帧率就会超过30fps。Xcode OpenGLES分析器在复制命令上给出了两个建议:
  1. file://localhost/Users/xxxx/Documents/Development/xxxx.mm: error: Validation Error: glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 960, 640, 0) : Height<640>不是2的幂

  2. file://localhost/Users/xxxx/Documents/Development/xxxx.mm: warning: GPU Wait on Texture: Your app updated a texture that is currently used for rendering. This caused the CPU to wait for the GPU to finish rendering.

我将努力解决以上两个问题(也许它们是关键所在)。与此同时,有谁能建议更有效的方法将深度数据拉入纹理?
提前感谢!

我还没有尝试过这个方法,但是你不可以使用片段着色器将深度信息写入渲染纹理的颜色缓冲区中,然后将其作为另一个用于后期处理的着色器的输入?OpenGL应该只在GPU上进行同步,而不会因执行基于CPU的读取/复制而阻塞管道。此外,还可以参考相关讨论此处 - starbugs
1个回答

7
iOS设备通常支持OES_depth_texture,因此在该扩展存在的设备上,您可以设置一个帧缓冲对象,并使用深度纹理作为其唯一附件:
GLuint g_uiDepthBuffer;
glGenTextures(1, &g_uiDepthBuffer);
glBindTexture(GL_TEXTURE_2D, g_uiDepthBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
// glTexParameteri calls omitted for brevity

GLuint g_uiDepthFramebuffer;
glGenFramebuffers(1, &g_uiDepthFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, g_uiDepthFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, g_uiDepthBuffer, 0);

当您绘制场景时(可以使用简单的片段着色器),您的纹理将接收写入深度缓冲区的所有值,然后您可以直接从中进行纹理,而不需要调用glCopyTexImage2D


你是100%正确的。我基于OpenGL 2.0 ES规范来理解支持的内容,但该规范不包括任何扩展。所有iOS ES 2.0设备都支持OES_depth_texture,所以这确实是解决方案。谢谢! - mobob
那么,仅具有深度附件的帧缓冲区是可以的吗? - Viktor Sehr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接