opengl - 检索纹理像素数据的最有效方法?

标签 opengl opengl-es-2.0 opengl-es-3.0

我至少知道 Dx9 的 Directx 有一个纹理对象,您只能将一小部分纹理获取到 CPU 可访问的内存。我相信这是一个名为“LockRect”的函数。 OpenGL 有 glGetTexImage() 但它会抓取整个图像,如果格式与纹理不同,那么它必须在传输之上将整个纹理转换为新的像素格式整个纹理。 OpenGL ES 中也没有这个函数。帧缓冲区是另一种选择,但我可能会绑定(bind)一个帧缓冲区,其中颜色附件连接到纹理。然后是从帧缓冲区读取的glReadPixels,因此它应该从纹理中读取。 glReadPixels 的像素格式选项有限,因此必须进行转换,但我可以读取所需的像素(只有 1 个像素)。我没有使用过这个方法,但看起来是可行的。如果有人可以确认帧缓冲区方法,那么它是一个可行的替代方案。那么这个方法也适用于 OpenGL ES 2+。

还有其他方法吗?帧缓冲区方法的效率如何(如果有效),它最终是否必须在读取像素之前将整个纹理转换为所需的格式,或者它是否完全由实现定义?

编辑:@Nicol_Bolas 请停止从标签中删除 OpenGL 并添加 OpenGL-ES,OpenGL-ES 不适用,OpenGL 适用。这是专门针对 OpenGL 的,但我希望它尽可能与 Open ES 2+ 兼容,尽管这不是必须的。如果只有 OpenGL 的解决方案可用,那么我会考虑是否值得进行权衡。谢谢。

最佳答案

请注意,我在 ES 方面没有太多经验,因此在这种情况下可能有更好的方法来专门执行此操作。不过,一般要点适用于普通 OpenGL 或 ES。


首先,最重要的性能考虑应该是在阅读时。如果在渲染时向显卡请求数据,您的程序(CPU 端)将不得不暂停,直到显卡返回数据,这会由于您无法发出进一步的渲染命令而减慢渲染速度。作为一般规则,您应该始终上传、渲染、下载 - 不要混合这些过程中的任何一个,这将极大地影响速度,并且影响程度取决于驱动程序/硬件/操作系统。

我建议在渲染周期结束时使用glReadPixels( )。我怀疑该函数的格式限制与帧缓冲区格式的限制有关;此外,您确实应该使用 8 位无符号或浮点,both of which are supported 。如果您遇到一些不允许任何受支持格式的边缘情况,您应该解释那是什么,因为可能有一种方法可以专门处理它。

如果您需要渲染中特定点(而不是结束时)的帧缓冲区内容,请创建第二个纹理+帧缓冲区(同样具有相同的格式)作为有效的“后缓冲区”,然后从目标复制帧缓冲区到该纹理。这发生在视频卡上,因此不会直接读取造成总线延迟。这是我写的执行此操作的代码:

glActiveTexture( GL_TEXTURE0 + unit );
glBindTexture( GL_TEXTURE_2D, backbufferTextureHandle );
glBindFramebuffer( GL_READ_FRAMEBUFFER, framebufferHandle );
glCopyTexSubImage2D(
        GL_TEXTURE_2D,
        0, // level
        0, 0, // offset
        0, 0, // x, y
        screenX, screenY );
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, framebufferHandle );

然后,当您需要数据时,将后台缓冲区绑定(bind)到 GL_READ_FRAMEBUFFER 并对其使用 glReadPixels( )

最后,您应该记住,下载数据仍然会停止 CPU 端。如果在显示帧缓冲区之前下载,则会推迟显示图像,直到可以再次执行命令为止,这可能会导致明显的延迟。因此,即使您只关心最终的缓冲区状态,我建议仍然使用非默认帧缓冲区,并结束渲染周期以达到以下效果:

(1.) Blit 到默认帧缓冲区:

glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 ); // Default framebuffer
glBindFramebuffer( GL_READ_FRAMEBUFFER, framebufferHandle );
glBlitFramebuffer(
        0, 0, screenX, screenY,
        0, 0, screenX, screenY,
        GL_COLOR_BUFFER_BIT,
        GL_NEAREST );

(2.) 在给定情况下调用任何交换缓冲区命令。

(3.) 从帧缓冲区下载调用(无论是 glReadPixels( ) 还是其他)。

至于 blit/texcopy 操作对速度的影响,它在大多数现代硬件上都相当不错,即使一帧执行 10 次以上,我也没有发现它有明显的影响,但如果您正在处理过时的硬件,也许值得再考虑一下。

关于opengl - 检索纹理像素数据的最有效方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35590550/

相关文章:

swift - 如何快速编译着色器,位置总是返回-1

c++ - 如何将这些 OpenGL 着色器转换为适用于 Android NDK 的 GLES3 的 OpenGL ES 着色器?

c++ - 动态迭代 If 语句

c++ - GLEW 只是扩展库还是包含 OpenGL ES 2.0 实现?

android - Galaxy S4 Android 4.4.2 上的 OpenGL 3

java - 防止 OpenGL ES 在方向切换时破坏 Canvas

java - glClearColor 无法正常工作(android opengl)

java - LWJGL 3 : OpenGL Quad is not rendered properly

opengl - 如何从相机内在矩阵计算相机的视野?

OpenGL Vulkan 互操作性