YUV回顾
YUV数据量相比RGB较小,因此YUV适用于传输,但是YUV图不能直接用于显示,需要转换为RGB格式才能显示,因而YUV数据渲染实际上就是使用Opengl ES将YUV数据转换程RGB数据,然后显示出来的过程。
也就是说Opengl ES之所以能渲染YUV数据其实就是使用了Opengl强大的并行计算能力,快速地将YUV数据转换程了RGB数据。
YUV的格式比较多,我们今天就以YUV420SP为例,而YUV420SP又分为和两种,因此今天我们的主题就是如何使用Opengl ES对和数据进行渲染显示。
在着色器中使用对YUV数据进行归一化处理后Y数据的映射范围是0到1,而U和V的数据映射范围是-0.5到0.5。
因为YUV格式图像 UV 分量的默认值分别是 127 ,Y 分量默认值是 0 ,8 个 bit 位的取值范围是 0 ~ 255,由于在 shader 中纹理采样值需要进行归一化,所以 UV 分量的采样值需要分别减去 0.5 ,确保 YUV 到 RGB 正确转换。
YUV数据准备
首先我们可以使用命令行将一张png图片转换成YUV格式的图片:
通过上面这个命令行我们就可以将一张图片转换成yuv格式的图片,此时我们可以使用软件看下你转换的图片对不对,如果本身转换出来的图片就是错的,那么后面的程序就白搭了...
注意:转换图片的宽高最好是2的幂次方,笔者测试了下发现如果宽高不是2的幂次方的话虽然能正常转换,但是查看的时候要么有色差,要么有缺陷,也有可能正常。
又或者你可以极客一点,直接使用ffmpeg代码解码视频的方式获得YUV数据并保存,这个可以参考笔者之前的文章:
同时在上面的文章中笔者也介绍了通过命令行的方式查看YUV数据的方法。
YUV数据渲染
YUV 渲染步骤:
生成 2 个纹理,分别用于承载Y数据和UV数据,编译链接着色器程序;
NV21和NV12格式的YUV数据是只有两个平面的,它们的排列顺序是或者因此我们的片元着色器需要两个纹理采样。
确定纹理坐标及对应的顶点坐标;
分别加载 NV21 的两个 Plane 数据到 2 个纹理,加载纹理坐标和顶点坐标数据到着色器程序;
绘制。
YUV与RGB的转换格式图:
YUV与RGB的转换公式
在OpenGLES的内置矩阵实际上是一列一列地构建的,比如YUV和RGB的转换矩阵的构建是:
OpenGLES 实现 YUV 渲染需要用到 GL_LUMINANCE 和 GL_LUMINANCE_ALPHA 格式的纹理,其中 GL_LUMINANCE 纹理用来加载 NV21 Y Plane 的数据,GL_LUMINANCE_ALPHA 纹理用来加载 UV Plane 的数据。
废话少说,show me the code
YUVRenderOpengl.h
YUVRenderOpengl.cpp
注意看着色器代码的注释,NV12和NV21的渲染仅仅是着色器代码有细小差别而已。
YUVRenderActivity.java
这个主要看懂方法,对于YUV数据的读取即可。
思考
专栏系列
领取专属 10元无门槛券
私享最新 技术干货