OpenGL ES 渲染 NV21、NV12、I420、YV12、YUYV、UYVY、I444
本文主要讲解常见的几类 YUV 格式图像渲染方式,如果对 YUV 格式不是很熟悉的同学可以翻看旧文一文掌握 YUV 图像的基本处理,YUV 格式的介绍这里不再展开。
渲染 NV21、NV12 格式图像
NV21、NV12 可以看成同一种结构,区别只是 uv 的交错排列顺序不同。
渲染 NV21/NV12 格式图像需要使用 2 个纹理,分别用于保存 Y plane 和 UV plane 的数据,然后在片段着色器中分别对 2 个纹理进行采样,转换成 RGB 数据。
需要用到 GL_LUMINANCE 和 GL_LUMINANCE_ALPHA 格式的纹理,其中 GL_LUMINANCE 纹理用来加载 NV21/NV12 Y Plane 的数据,GL_LUMINANCE_ALPHA 纹理用来加载 UV Plane 的数据。
加载 NV21/NV12 的 2 个 Plane 数据到纹理,ppPlane[0] 表示 Y Plane 的指针,ppPlane[1] 表示 UV Plane 的指针,注意 2 个纹理的格式和宽高。
//upload Y plane data
glBindTexture(GL_TEXTURE_2D, m_yTextureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_RenderImage.width, m_RenderImage.height, 0, GL_LUMINANCE, 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_LUMINANCE_ALPHA, m_RenderImage.width >> 1, m_RenderImage.height >> 1, 0, GL_LUMINANCE_ALPHA, 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);
使用 2 个纹理渲染 NV21 格式图像的片段着色器:
#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D y_texture;
uniform sampler2D uv_texture;
void main()
{
vec3 yuv;
yuv.x = texture(y_texture, v_texCoord).r -0.063;
yuv.y = texture(uv_texture, v_texCoord).a-0.502;
yuv.z = texture(uv_texture, v_texCoord).r-0.502;
vec3 rgb = mat3(1.164, 1.164, 1.164,
0, -0.392, 2.017,
1.596, -0.813, 0.0) * yuv;
outColor = vec4(rgb, 1.0);
}
使用 2 个纹理渲染 NV12 格式图像的片段着色器(只是交换了一下 uv 分量):
#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D y_texture;
uniform sampler2D uv_texture;
void main()
{
vec3 yuv;
yuv.x = texture(y_texture, v_texCoord).r -0.063;
yuv.z = texture(uv_texture, v_texCoord).a-0.502;
yuv.y = texture(uv_texture, v_texCoord).r-0.502;
vec3 rgb = mat3(1.164, 1.164, 1.164,
0, -0.392, 2.017,
1.596, -0.813, 0.0) * yuv;
outColor = vec4(rgb, 1.0);
}
当然也可以使用一张纹理实现 NV21/NV12 图像渲染,参考文章OpenGL ES 渲染 NV21/NV12 格式图像有哪些“姿势”?
渲染 I420、YV12 格式图像
I420 也是 YUV4:2:0 的采样方式,有 3 个 plane , 跟 YV12 格式的区别就是 uv plane 的位置不同。
4×4 的 I420 图像内存分布:
(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 ~ 17) U00 U01
(18 ~ 19) U10 U11
(20 ~ 21) V00 V01
(22 ~ 23) V10 V11
类比 NV21 渲染 ,可以将 I420 格式的三个 plane 数据加载到 3 个纹理,然后再分别采样。
上传 I420 三个 plane 的数据到纹理:
//upload Y plane data
glBindTexture(GL_TEXTURE_2D, m_yTextureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_RenderImage.width, m_RenderImage.height, 0, GL_LUMINANCE, 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 U plane data
glBindTexture(GL_TEXTURE_2D, m_uTextureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_RenderImage.width >> 1, m_RenderImage.height >> 1, 0, GL_LUMINANCE, 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);
//update V plane data
glBindTexture(GL_TEXTURE_2D, m_vTextureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_RenderImage.width >> 1, m_RenderImage.height >> 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_RenderImage.ppPlane[2]);
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);
使用 3 个纹理渲染 I420 格式图像的片段着色器:
#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D y_texture;
uniform sampler2D u_texture;
uniform sampler2D v_texture;
void main()
{
vec3 yuv;
yuv.x = texture(y_texture, v_texCoord).r -0.063;
yuv.y = texture(u_texture, v_texCoord).r-0.502;
yuv.z = texture(v_texture, v_texCoord).r-0.502;
vec3 rgb = mat3(1.164, 1.164, 1.164,
0, -0.392, 2.017,
1.596, -0.813, 0.0) * yuv;
outColor = vec4(rgb, 1.0);
}
同上一节,渲染 YV12 格式只需要在 shader 中交换下 uv 分量,或者上传数据到纹理之前交换下 uv plane 的数据,代码就不贴出来了,显得太啰嗦。
那么 I420/YV12 的渲染能不能只使用一张纹理就可以实现?
答案是可以的。类似前文提到的,需要使用 OpenGL ES 3.0 texlFetch 来替代 texture 采样函数,因为 yuv 3 个 plane 都在同一张纹理上,所以不能执行任何形式的过滤和插值操,必须要精确获取像素的内容。
我们把整个 I420 数据上传到一张 GL_LUMINANCE 纹理,尺寸 [width,height * 3 / 2] 。
glBindTexture(GL_TEXTURE_2D, m_TextureId);
glTexImage2D ( GL_TEXTURE_2D, 0, GL_LUMINANCE, m_RenderImage.width, m_RenderImage.height * 3 / 2, 0, GL_LUMINANCE, 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);
只使用一张纹理渲染 I420 格式图像的片段着色器,shader 中需要传入图像的分辨率来确保采样的准确性,inputSize 为图像分辨率:
#version 300 es
precision highp float;
in vec2 v_texCoord;
uniform sampler2D y_texture;
uniform vec2 inputSize;
out vec4 outColor;
void main() {
vec2 uv = v_texCoord;
uv.y *= 2.0 / 3.0;
float y = texture(y_texture, uv).r - 0.063;
vec2 pixelUV = v_texCoord * inputSize;
pixelUV.x = mod(pixelUV.y/2.0, 2.0) > 0.001 ? pixelUV.x / 2.0 + inputSize.x / 2.0 : pixelUV.x / 2.0;
pixelUV.y = floor(pixelUV.y / 4.0);
pixelUV.y += inputSize.y;
float u = texelFetch(y_texture, ivec2(int(pixelUV.x), int(pixelUV.y)), 0).r - 0.502;
pixelUV = v_texCoord * inputSize;
pixelUV.x = mod(pixelUV.y/2.0, 2.0) > 0.001 ? pixelUV.x / 2.0 + inputSize.x / 2.0 : pixelUV.x / 2.0;
pixelUV.y = floor(pixelUV.y / 4.0);
pixelUV.y += inputSize.y * 5.0 / 4.0;
float v = texelFetch(y_texture, ivec2(int(pixelUV.x), int(pixelUV.y)), 0).r - 0.502;
vec3 yuv = vec3(y,u,v);
highp vec3 rgb = mat3(1.164, 1.164, 1.164,
0, -0.392, 2.017,
1.596, -0.813, 0.0) * yuv;
outColor = vec4(rgb, 1.0);
}
代码其实不太好理解的,可以先点赞收藏,后面慢慢琢磨,可以加我微信进技术群交流。
渲染 YUYV、UYVY 格式图像
YUYV 和 UYVY 格式用的是 YUV 4:2:2 的采样方式,2 个 Y 分量共用一对 UV 分量,一个像素占用 2 个字节。
YUYV 格式的存储格式:
(0 ~ 7) Y00 U00 Y01 V00 Y02 U01 Y03 V01
(8 ~ 15) Y10 U10 Y11 V10 Y12 U11 Y13 V11
(16 ~ 23) Y20 U20 Y21 V20 Y22 U21 Y23 V21
(24 ~ 31) Y30 U30 Y31 V30 Y32 U31 Y33 V31
参考 I420 的渲染方式,可以使用 2 个纹理来实现,由于 YUYV、UYVY 格式是每 2 个字节才有一个 Y 分量,可以使用 GL_LUMINANCE_ALPHA 格式纹理来采样 Y 分量;每 4 个字节才出现一对 UV 分量,可以使用 GL_RGBA 格式纹理来采样 UV 分量。
上传 YUYV、UYVY 数据到纹理:
//upload YUYV、UYVY 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 YUYV、UYVY data
glBindTexture(GL_TEXTURE_2D, m_uvTextureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_RenderImage.width >> 1, m_RenderImage.height, 0, GL_RGBA, 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);
使用 2 个纹理渲染 YUYV 格式图像的片段着色器:
#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D y_texture;
uniform sampler2D uv_texture;
void main()
{
vec3 yuv;
yuv.x = texture(y_texture, v_texCoord).r -0.063;
vec4 yuyv = texture(uv_texture, v_texCoord);
yuv.y = yuyv.g - 0.502;
yuv.z = yuyv.a - 0.502;
vec3 rgb = mat3(1.164, 1.164, 1.164,
0, -0.392, 2.017,
1.596, -0.813, 0.0) * yuv;
outColor = vec4(rgb, 1.0);
}
使用 2 个纹理渲染 UYVY 格式图像的片段着色器:
#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D y_texture;
uniform sampler2D uv_texture;
void main()
{
vec3 yuv;
yuv.x = texture(y_texture, v_texCoord).a -0.063;
vec4 uyvy = texture(uv_texture, v_texCoord);
yuv.y = yuyv.r - 0.502;
yuv.z = yuyv.b - 0.502;
vec3 rgb = mat3(1.164, 1.164, 1.164,
0, -0.392, 2.017,
1.596, -0.813, 0.0) * yuv;
outColor = vec4(rgb, 1.0);
}
另外,借助于 OpenGL ES 3.0 的 texlFetch 也可以实现只需要一张纹理就可以实现 YUYV、UYVY 格式图像渲染。
直接把整个 YUYV、UYVY 图像数据上传到一张 GL_LUMINANCE_ALPHA 格式的纹理,在 shader 中通过偏移采样的方式获取 uv 值,同样 shader 中需要传入图像的分辨率 inputSize 来确保采样的准确性。
glBindTexture(GL_TEXTURE_2D, m_TextureId);
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);
只使用一张纹理渲染 YUYV 格式图像的片段着色器:
#version 300 es
precision highp float;
in vec2 v_texCoord;
uniform sampler2D y_texture;
uniform vec2 inputSize;
out vec4 outColor;
void main() {
//YUYV YUYV
vec2 uv = v_texCoord;
vec2 pixelUV = v_texCoord * inputSize;
pixelUV = floor(pixelUV);
vec4 col = texelFetch(y_texture, ivec2(int(pixelUV.x), int(pixelUV.y)), 0);
float y = col.r - 0.063;
float u,v;
if(mod(pixelUV.x, 2.0) > 0.01) {
v = col.a - 0.502;
pixelUV.x -= 1.0;
u = texelFetch(y_texture, ivec2(int(pixelUV.x), int(pixelUV.y)), 0).a - 0.502;
} else {
u = col.a - 0.502;
pixelUV.x += 1.0;
v = texelFetch(y_texture, ivec2(int(pixelUV.x), int(pixelUV.y)), 0).a - 0.502;
}
vec3 yuv = vec3(y,u,v);
vec3 rgb = mat3(1.164, 1.164, 1.164,
0, -0.392, 2.017,
1.596, -0.813, 0.0) * yuv;
outColor = vec4(rgb, 1.0);
}
只使用一张纹理渲染 UYVY 格式图像的片段着色器:
#version 300 es
precision highp float;
in vec2 v_texCoord;
uniform sampler2D y_texture;
uniform vec2 inputSize;
out vec4 outColor;
void main() {
//UYVY UYVY
vec2 uv = v_texCoord;
vec2 pixelUV = v_texCoord * inputSize;
pixelUV = floor(pixelUV);
vec4 col = texelFetch(y_texture, ivec2(int(pixelUV.x), int(pixelUV.y)), 0);
float y = col.a - 0.063;
float u,v;
if(mod(pixelUV.x, 2.0) > 0.01) {
v = col.r - 0.502;
pixelUV.x -= 1.0;
u = texelFetch(y_texture, ivec2(int(pixelUV.x), int(pixelUV.y)), 0).r - 0.502;
} else {
u = col.r - 0.502;
pixelUV.x += 1.0;
v = texelFetch(y_texture, ivec2(int(pixelUV.x), int(pixelUV.y)), 0).r - 0.502;
}
vec3 yuv = vec3(y,u,v);
vec3 rgb = mat3(1.164, 1.164, 1.164,
0, -0.392, 2.017,
1.596, -0.813, 0.0) * yuv;
outColor = vec4(rgb, 1.0);
}
渲染 I444 格式图像
I444 使用的是 YUV 4:4:4 的采样方式,每个像素占用 3 个字节,yuv 各占一个字节,I444 有 3 个 plane 。
首先能想到是使用 3 个 GL_LUMINANCE 格式纹理来装载 3 个 plane 的数据,然后在 shader 分别采样出 yuv 值。
上传 I444 三个 plane 的数据到纹理:
//upload Y plane data
glBindTexture(GL_TEXTURE_2D, m_yTextureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_RenderImage.width, m_RenderImage.height, 0, GL_LUMINANCE, 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 U plane data
glBindTexture(GL_TEXTURE_2D, m_uTextureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_RenderImage.width, m_RenderImage.height, 0, GL_LUMINANCE, 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);
//update V plane data
glBindTexture(GL_TEXTURE_2D, m_vTextureId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_RenderImage.width, m_RenderImage.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_RenderImage.ppPlane[2]);
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);
使用 3 个纹理渲染 I444 格式图像的片段着色器(是不是跟 I420 使用的一样?):
#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D y_texture;
uniform sampler2D u_texture;
uniform sampler2D v_texture;
void main()
{
vec3 yuv;
yuv.x = texture(y_texture, v_texCoord).r -0.063;
yuv.y = texture(u_texture, v_texCoord).r-0.502;
yuv.z = texture(v_texture, v_texCoord).r-0.502;
vec3 rgb = mat3(1.164, 1.164, 1.164,
0, -0.392, 2.017,
1.596, -0.813, 0.0) * yuv;
outColor = vec4(rgb, 1.0);
}
I444 的渲染推荐使用一张纹理,纹理格式 GL_LUMINANCE ,纹理尺寸[width, height * 3],然后 yuv 三个 plane 各占纹理的 1/3 .
上传 I444 数据到一张纹理:
glBindTexture(GL_TEXTURE_2D, m_TextureId);
glTexImage2D ( GL_TEXTURE_2D, 0, GL_LUMINANCE, m_RenderImage.width, m_RenderImage.height * 3, 0, GL_LUMINANCE, 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);
只使用一张纹理渲染 I444 格式图像的片段着色器:
#version 300 es
precision highp float;
in vec2 v_texCoord;
uniform sampler2D y_texture;
uniform vec2 inputSize;
out vec4 outColor;
void main() {
vec2 uv = v_texCoord;
uv.y *= 1.0 / 3.0;
float y = texture(y_texture, uv).r - 0.063;
uv.y += 1.0 / 3.0;
float u = texture(y_texture, uv).r - 0.502;
uv.y += 1.0 / 3.0;
float v = texture(y_texture, uv).r - 0.502;
vec3 yuv = vec3(y,u,v);
highp vec3 rgb = mat3(1.164, 1.164, 1.164,
0, -0.392, 2.017,
1.596, -0.813, 0.0) * yuv;
outColor = vec4(rgb, 1.0);
}
由于 I444 yuv 三个 plane 各占纹理的 1/3 ,需要在 shader 中控制每个分量的采样范围,这样既可以节省 2 个纹理也提升了渲染效率。
需要完整代码的同学,可以扫码添加微信获取。
进技术交流群,扫码添加我的微信:Byte-Flow
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/42761.html