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

问题描述:

我知道DirectX for Dx9至少有一个纹理对象,你只能得到CPU可访问内存的一小部分纹理。我相信这是一个名为“LockRect”的函数。 OpenGL有glGetTexImage(),但它抓取整个图像,如果格式与纹理不同,那么它将不得不在整个纹理转移之前将整个纹理转换为新的像素格式。这个函数也不在OpenGL ES中。帧缓冲是另一种选择,但我可以绑定一个framebuffer,在其中连接到纹理的颜色附件。然后有从帧缓冲区读取的glReadPixels,所以它应该从纹理读取。 glReadPixels有限的像素格式选项,所以转换将不得不发生,但我可以读取我需要的像素(这是只有1个像素)。我没有使用这种方法,但它似乎是可能的。如果任何人都可以确认framebuffer方法,那么这是一个可行的选择。那么这种方法也适用于OpenGL ES 2+。检索纹理像素数据的最有效方法?

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

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

请注意,我没有太多的ES经验,所以在这种情况下可能有更好的方法来做到这一点。不过,普通的要点适用于普通的OpenGL或ES。


首先,最重要的性能考虑应该是当你在阅读时。如果您在渲染时从视频卡请求数据,则您的程序(CPU端)将不得不暂停,直到视频卡返回数据为止,由于无法发出更多渲染命令,渲染速度会降低。作为一般规则,你应该总是上传,渲染,下载 - 不要混合任何这些进程,它会极大地影响速度,以及依赖驱动程序/硬件/操作系统的程度。

我建议在渲染周期结束时使用glReadPixels()。我怀疑这个函数的格式限制与framebuffer格式的限制有关;此外,你真的应该使用8位无符号或浮点数,both of which are supported。如果您有一些附带条件不允许使用任何支持的格式,则应该说明具体情况,因为可能有专门处理它的方法。

如果在渲染(而不是结束)的特定点需要帧缓冲区的内容,请创建第二个纹理+帧缓冲区(再次使用相同的格式)作为有效的“后缓冲区”,然后从目标framebuffer到该纹理。这发生在视频卡上,因此它不直接读取总线延迟。下面是我写的,做此操作:

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

然后,当你需要的数据,后备缓冲结合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.)您从framebuffer下载的电话(无论是glReadPixels()或其他)。

至于blit/texcopy操作对速度的影响,它在大多数现代硬件上都非常好,我甚至没有发现即使做了10倍以上的帧也有明显的影响,但是如果您正在处理过时的硬件,这可能值得再次思考。

+0

我想获得一个纹理,一旦从磁盘加载到GPU内存中,内容不会发生变化。我不需要特定的格式,但是如果格式不匹配,使用'glReadPixels()'将需要进行某种计算,速度较慢但不确定多少。我只需要检索一个像素,所以它不应该太糟糕,最糟糕的情况是像DXT这样的压缩格式。尽管如此,它会为我节省一些工作。逻辑顺序是获取像素并将该像素用作渲染的输入。还有与它一起进行的计算以及多个样本。 – user3901459

+0

@ user3901459数据量并不会真正影响速度;总线延迟是主要的限制因素,而不是带宽,所以一个像素与整个帧缓冲区大致相同。也就是说,如果你只想要上传的数据,我认为你正在讨论这个错误。只要保留最初传递给OpenGL的图像阵列即可。如果你从光盘加载压缩格式有点困难,我没有任何纹理压缩的经验,但大概你可以用软件做到这一点,并完全切割GPU。 –

+0

在某些时候,带宽会影响它,但对吗?举一个真正大的纹理为例,抓住整个东西会让它变慢?我想这取决于相对于延迟需要多长时间。将它全部保存在内存中并不是最好的,如果其他所有的都失败了,我不得不这样做,但它会使内存使用量增加一倍。特别是在像移动设备这样的共享内存系统上。我猜想可以制作一些混合系统,读取纹理并将其存储在CPU内存中,如果它不被使用一段时间然后将其删除。它仍然需要足够快或者它会stu – user3901459