之前笔者写的Opengl入门教程都是面向Android开发者的Opengl ES方面的,总所周知,Opengl本身就是跨平台的,Opengl ES的程序只需经过稍微改动适配一下即可变成Opengl在 桌面端运行起来。
比如之前笔者写的文章 Opengl ES之YUV数据渲染 最近因为工作需要,笔者想把这部分的渲染代码适配到桌面端, 居然惊奇地发现了在Opengl 3.3中,关于颜色格式的内置变量GL_LUMINANCE
和GL_LUMINANCE_ALPHA
居然被删除了。那咋整?此路不通啊…
兼容性修复
在Opengl ES中GL_LUMINANCE
一般表示的是亮度信息,也就是一个颜色通道,在渲染NV21数据时,就表示的是Y的数据,而GL_LUMINANCE_ALPHA
则表示的是两个通道的信息,在渲染NV21数据时表示的就是UV数据。
明白了这个含义之后,其实就是一个通道和两个通道的事情。既然GL_LUMINANCE
和GL_LUMINANCE_ALPHA
被删除了,那我们可以再查询些资料看看Opengl中还有那些变量时可以表示一个数据通道和两个数据通道的,这样问题应该就能够解决了。
在Opengl中表示一个通道和表示两个通道的变量还是不少的,例如GL_RED
、GL_RED
、GL_GREEN
、GL_BLUE
等这些变量可以表示一个颜色通道的数据,而GL_RG
可以表示两个颜色通道的数据, 同理GL_RGB
则可以表示三个颜色通道的数据,GL_RGBA
则是带透明度的四个颜色通道的数据。
当然,纹理的数据格式与着色器的渲染是息息相关的,我们改动了纹理数据格式的同时,着色器程序也需要作出相应的修改,否则就是白搭,总不能是我们上传的纹理数据是GL_GREEN
,而在着色器中依然使用.r
这样的数据通道吧?
既然原理都清楚了,那下面就是开始适配吧…
首先我们修改下纹理的数据格式,将GL_LUMINANCE
改为GL_RED
,将GL_LUMINANCE_ALPHA
改为GL_RG
,完整代码如下:
void YUVRenderOpengl::setYUVData(void *y_data, void *uv_data, int width, int height, int yuvType) {
// 准备y数据纹理
glGenTextures(1, &y_textureId);
glActiveTexture(GL_TEXTURE2);
glUniform1i(y_textureSampler, 2);
// 绑定纹理
glBindTexture(GL_TEXTURE_2D, y_textureId);
// 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 旧的 opengl ES使用
// glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, y_data);
// 新的 opengl 3.3使用
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, y_data);
// 生成mip贴图
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, y_textureId);
// 解绑定
glBindTexture(GL_TEXTURE_2D, 0);
// 准备uv数据纹理
glGenTextures(1, &uv_textureId);
glActiveTexture(GL_TEXTURE3);
glUniform1i(uv_textureSampler, 3);
// 绑定纹理
glBindTexture(GL_TEXTURE_2D, uv_textureId);
// 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 注意宽高
// 注意要使用 GL_LUMINANCE_ALPHA
// 旧的 opengl ES使用
// glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, width/2, height/2, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, uv_data);
// 新的 opengl 3.3使用
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG, width/2, height/2, 0, GL_RG, GL_UNSIGNED_BYTE, uv_data);
// 生成mip贴图
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, uv_textureId);
// 解绑定
glBindTexture(GL_TEXTURE_2D, 0);
}
相关改动部分笔者已增加注释说明。
然后我们修改下着色器部分,以下是原来的着色器代码:
static const char *fragment = "#version 300 es\n"
"precision mediump float;\n"
"out vec4 FragColor;\n"
"in vec2 TexCoord;\n"
"uniform sampler2D y_texture; \n"
"uniform sampler2D uv_texture;\n"
"void main()\n"
"{\n"
"vec3 yuv;\n"
"yuv.x = texture(y_texture, TexCoord).r;\n"
"yuv.y = texture(uv_texture, TexCoord).a-0.5;\n"
"yuv.z = texture(uv_texture, TexCoord).r-0.5;\n"
"vec3 rgb =mat3( 1.0,1.0,1.0,\n"
"0.0,-0.344,1.770,1.403,-0.714,0.0) * yuv;\n"
"FragColor = vec4(rgb, 1);\n"
"}";
因为我们在纹理上传时修改了Y和UV纹理数据的格式,但是Y数据我们使用的是GL_RED
,因此在着色器中依然使用.r
获取,者无需修改。由于UV纹理我们改为了GL_RG
, 因此我们需要将yuv.y = texture(uv_texture, TexCoord).a-0.5;\n
这一行的a通道改为g通道,也就是改为yuv.y = texture(uv_texture, TexCoord).g-0.5;\n
。
同时#version 300 es
是opengl ES着色器的声明,在桌面端使用Opengl 3.3,我们需要将这个声明改为#version 330 core
。
因此完整的着色器代码是:
static const char *fragment = "#version 330 core\n"
"precision mediump float;\n"
"out vec4 FragColor;\n"
"in vec2 TexCoord;\n"
"uniform sampler2D y_texture; \n"
"uniform sampler2D uv_texture;\n"
"void main()\n"
"{\n"
"vec3 yuv;\n"
"yuv.x = texture(y_texture, TexCoord).r;\n"
"yuv.y = texture(uv_texture, TexCoord).g-0.5;\n"
"yuv.z = texture(uv_texture, TexCoord).r-0.5;\n"
"vec3 rgb =mat3( 1.0,1.0,1.0,\n"
"0.0,-0.344,1.770,1.403,-0.714,0.0) * yuv;\n"
"FragColor = vec4(rgb, 1);\n"
"}";
至此,NV21数据渲染就由Opengl ES向Opengl 3.3兼容适配完成啦…
关注我,一起进步,人生不止coding!!!
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/27415.html