1. 引言
在原始Windows端上,我们使用glReadPixels()方法实现OpenGL 纹理到内存图像的转换,其中其支持的色彩类型包括GL_RGBA、GL_RGB、GL_BGRA及GL_BGR等色彩空间,便于我们实现纹理到各个色彩空间的转换,但是在OpenGL es上,其限制条件增加,首先我们没有GL_BGR/GL_BGRA的转换方式了;其次是读取GL_RGB图像时,在跨平台存在黑屏,无法渲染的现象。
2. 问题描述
OpenGL es 3.0环境下调用glreadpixels读取FBO绑定纹理时候,在format为GL_RGBA条件下能够调用,若format为GL_RGB则报GL_INVALID_ENUM的错误。
glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pBuffer);
在IOS上及Windows OpenGL es仿真上均存在上述问题,在Android上则正常回显。
3. 实验测试
1. 在Android 上,如果当前FBO绑定的纹理是RGB的,那么其是可以通过glReadPixels读取GL_RGB和GL_RGBA到CPU的。这里通过glGetIntegerv 查询得到的值为GL_RGB。
在Android上,如果FBO绑定纹理为RGBA类型,那么glReadPixels读取出的GL_RGBA正常,读取GL_RGB报GL_INVALID_OPERATION错误,渲染黑屏。这里通过glGetIntegerv 查询得到的值为GL_RGBA。
2. 在IOS和windows仿真下面,无论FBO绑定的纹理是GL_RGB还是GL_RGBA,使用glGetIntergerv查询值GL_IMPLEMENTATION_COLOR_READ_FORMAT 始终是GL_RGBA。
因此调用glReadPixels读取GL_RGB 会报GL_INVAILD_ENUM的错误。
3. GL_IMPLEMENTATION_COLOR_READ_FORMAT
params returns one value, the format chosen by the implementation in which pixels may be read from the color buffer of the currently bound framebuffer in conjunction with GL_IMPLEMENTATION_COLOR_READ_TYPE. See glReadPixels.
使用GL_IMPLEMENTATION_COLOR_READ_FORMAT 并不能查询所有的支持类型,他返回的是当前FBO绑定的纹理相关,且能够支持的色彩类型。
4. 解决方案
1.相较于OpenGL,OpenGL es其存在功能缩减, 对各项功能支持性存在减弱;其次是各个厂商对OpenGL es 实现有所不一,所有功能全面性上不对齐,若要实现一套代码跨平台,则考虑最低要求支持原则。
2. 移动端考虑仅支持RGB与RGBA色彩空间,如果后续需要使用BGR与BGRA色彩空间,那么在OpenGL 层实现通道R与B的通道颜色交换即可。
3. 统一调用glReadPixels()中读取GL_RGBA的方式,如果输出为RGB,那么直接丢弃alpha通道(这里可使用OpenCV中的split()与merge() API进行通道分离与合并)
5. 参考
https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glReadPixels.xhtml
https://registry.khronos.org/OpenGL-Refpages/es3.0/html/glGet.xhtml
https://blog.csdn.net/ryfdizuo/article/details/45442745