OpenGL ES 如何直接渲染 P010、P016 格式图像?

前面文章已经介绍过如何渲染 16bit 灰度图,本篇基于同样的思路来实现 P010、P016 格式图像的渲染。

P010 最早是微软定义的格式,表示的是 YUV 4:2:0 的采样方式,也就是说 P010 表示的是一类 YUV 格式,它的内存排布方式可能是 NV21、NV12、YU12、YV12 。

P016 也表示的是 YUV 4:2:0 的采样方式,与 P010 的差异只是有效位不同,P010 的有效位是 10bit , P016 有效位为 16bit 。

微软定义的其他 10bit 和 16bit YUV 格式:

OpenGL ES 如何直接渲染 P010、P016 格式图像?

下面我们讨论的 P010、P016 格式的内存排布方式跟 NV21 格式类似,只是每个 Y、U、V 分量分别占用 2 个字节。

(0  ~  3) Y00  Y01  Y02  Y03  
(4  ~  7) Y10  Y11  Y12  Y13  
(8  ~ 11) Y20  Y21  Y22  Y23  
(12 ~ 15) Y30  Y31  Y32  Y33  

(16 ~ 19) V00  U00  V01  U01 
(20 ~ 23) V10  U10  V11  U11

读者可以看之前 NV21 格式图像渲染的文章:在面试中,被反复提及的 OpenGL NV21 图像渲染

我们渲染 P010 格式图像也是分为 2 个 plane , 分别上传到 2 个纹理,然后采样,最后做 yuv2rgb 转换。

P010 格式图像的 Y plane 数据可以直接当成 16bit 的灰度图处理,利用 2 个通道 8bit 格式如 GL_LUMINANCE_ALPHA 或者 GL_RG8 完成加载 16bit 图像数据到纹理,然后采样的时候再将 2 个 8bit 数据转换成 16bit 。

P010 格式图像的 UV plane 数据处理还是基于同样的思路,可以利用 4 个通道 GL_RGBA 纹理格式,然后加载 32bit (uv 各占用 16bit)图像数据到 RGBA 纹理,在 Shader 中采样的时候,把 RG 通道当成 v 分量数据,把 BA 通道当成 u 分量数据,分别转成 16bit 数据,然后在进行归一化。

对应的片段着色器如下,这里默认 16bit 为小端序。

#version 300 es
precision highp float;
in vec2 v_texCoord;
uniform sampler2D y_texture;
uniform sampler2D uv_texture;
out vec4 outColor;
void main() {
    vec4 yCol = texture(y_texture, v_texCoord);
    vec4 uvCol = texture(uv_texture, v_texCoord);

    float val = 255.0 * yCol.r + yCol.a * 255.0 * pow(2.0, 8.0);
    float yVal = val / 65535.0 - 0.063;

    val = 255.0 * uvCol.r + uvCol.g * 255.0 * pow(2.0, 8.0);
    float vVal = val / 65535.0 - 0.502;

    val = 255.0 * uvCol.b + uvCol.a * 255.0 * pow(2.0, 8.0);
    float uVal = val / 65535.0 - 0.502;

    highp vec3 rgb = mat3(1.164, 1.164, 1.164,
                              0, -0.392, 2.017,
                          1.596, -0.813,  0.0) * vec3(yVal, uVal, vVal);
    outColor = vec4(rgb, 1.0);
}

加载 Y 和 UV plane 数据到纹理:

    //upload Y plane data
    glBindTexture(GL_TEXTURE_2D, m_yTextureId);
    glTexImage2D ( GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, m_RenderImage.width, m_RenderImage.height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, m_RenderImage.ppPlane[0]);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, GL_NONE);

    //update UV plane data
    glBindTexture(GL_TEXTURE_2D, m_uvTextureId);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_RenderImage.width >> 1, m_RenderImage.height >> 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_RenderImage.ppPlane[1]);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, GL_NONE);

渲染效果与原图一致:

OpenGL ES 如何直接渲染 P010、P016 格式图像?

需要源码的同学可以,可以下方扫码添加我的微信:Byte-Flow 获取。

字节流动

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/40558.html

(0)

相关推荐

发表回复

登录后才能评论