EGL和OpenGL简介

OpenGL(英语:Open Graphics Library,译名:开放图形库或者“开放式图形库”)是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API)。这个接口由用来绘制从简单的图形比特到复杂的三维景象。而另一种程序接口系统是仅用于Microsoft Windows上的Direct3D。OpenGL常用于CAD、虚拟现实、科学可视化程序和电子游戏开发。

EGL是OpenGL渲染和本地窗口系统(Windows系统的Window,Android中的SurfaceView等)之间的一个中间接口层。引入EGL就是为了屏蔽不同平台上的区别。

比如要实现一个Android播放器为,创建OpenGL环境大致分为如下几部:

一、EGL的部分

  1. 获取当前平台窗口
ANativeWindow *nativewindow = ANativeWindow_fromSurface(env, jSurface);
  1. 创建EGL display并初始化
EGLDisplay egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
  1. 创建EGLSurface
// 3-1 display的配置
// 输出配置
EGLConfig config;
EGLint configNum;
EGLint configSpec[] = {
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE
};
if (EGL_TRUE != eglChooseConfig(egl_display, configSpec, &config, 1, &configNum))
{
    LOGD("eglChooseConfig failed!");
    return;
}

// 3-2 创建surface, 这里根据之前创建并配置的egl_display跟nativewindow生成egl_surface
EGLSurface egl_surface = eglCreateWindowSurface(egl_display, config, nativewindow, 0);
  1. 创建EGL上下文
const EGLint ctxAttr[] = {
        EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
};
EGLContext egl_context = eglCreateContext(egl_display, config, EGL_NO_CONTEXT, ctxAttr);
if (EGL_TRUE != eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context))
{
    LOGD("eglMakeCurrent failed!");
    return;
}

二、OpenGL的部分

  1. 着色器代码,跟C语言相近,要以字符串的形式传给OpenGL API
// 顶点着色器
#define GET_STR(x) #x
static const char *vertexShader = GET_STR(
    attribute vec4 aPosition; // 顶点坐标
    attribute vec2 aTexCoord; // 纹理坐标
    varying vec2 vTexCoord;   // 输出的纹理坐标
    void main(){
        vTexCoord = vec2(aTexCoord.x, 1.0-aTexCoord.y);
        gl_Position = aPosition;
    }
);

// 片元着色器, 软解码和部分x86硬解码
// 不同YUV格式的fragment shader不一样,这里提供YUV420P,NV12和NV21
static const char *fragYUV420P = GET_STR(
    precision mediump float;    // 精度
    varying vec2 vTexCoord;     // 顶点着色器传递的坐标
    uniform sampler2D yTexture; // 输入的纹理y、u、v
    uniform sampler2D uTexture;
    uniform sampler2D vTexture;
    void main(){
        vec3 yuv;
        vec3 rgb;
        yuv.r = texture2D(yTexture,vTexCoord).r;
        yuv.g = texture2D(uTexture,vTexCoord).r-0.5;
        yuv.b = texture2D(vTexture,vTexCoord).r-0.5;
        rgb = mat3(1.0,     1.0,    1.0,
                   0.0,-0.39465,2.03211,
                   1.13983,-0.58060,0.0)*yuv;
        //输出像素颜色
        gl_FragColor = vec4(rgb,1.0);
    }
);

static const char *fragNV12 = GET_STR(
        precision mediump float;    // 精度
        varying vec2 vTexCoord;     // 顶点着色器传递的坐标
        uniform sampler2D yTexture; // 输入的纹理y、u、v
        uniform sampler2D uvTexture;
        void main(){
            vec3 yuv;
            vec3 rgb;
            yuv.r = texture2D(yTexture,vTexCoord).r;
            yuv.g = texture2D(uvTexture,vTexCoord).r - 0.5;
            yuv.b = texture2D(uvTexture,vTexCoord).a - 0.5;
            rgb = mat3(1.0,     1.0,    1.0,
                       0.0,-0.39465,2.03211,
                       1.13983,-0.58060,0.0)*yuv;
            //输出像素颜色
            gl_FragColor = vec4(rgb,1.0);
        }
);

static const char *fragNV21 = GET_STR(
        precision mediump float;    // 精度
        varying vec2 vTexCoord;     // 顶点着色器传递的坐标
        uniform sampler2D yTexture; // 输入的纹理y、u、v
        uniform sampler2D uvTexture;
        void main(){
            vec3 yuv;
            vec3 rgb;
            yuv.r = texture2D(yTexture,vTexCoord).r;
            yuv.g = texture2D(uvTexture,vTexCoord).a - 0.5;
            yuv.b = texture2D(uvTexture,vTexCoord).r - 0.5;
            rgb = mat3(1.0,     1.0,    1.0,
                       0.0,-0.39465,2.03211,
                       1.13983,-0.58060,0.0)*yuv;
            //输出像素颜色
            gl_FragColor = vec4(rgb,1.0);
        }
);

  1. 初始化shader,加载并编译
// 2-1 创建shader,type传入GL_VERTEX_SHADER(顶点)或GL_FRAGMENT_SHADER(片元)
GLint gl_shader = glCreateShader(type);
// 2-2 加载shader
glShaderSource(gl_shader,
               1,           //shader数量
               &shader_str, //shader代码
               0);          //代码长度
// 2-3 编译shader并获取编译情况
glCompileShader(gl_shader);
//获取编译情况
GLint compile_status;
glGetShaderiv(gl_shader, GL_COMPILE_STATUS, &compile_status);
if (compile_status == 0)
{
    LOGD("glCompileShader failed!");
    return 0;
}
LOGD("glCompileShader success!");
  1. 创建渲染程序,并在渲染程序中加入vertex与fragment shader,这个program用作后面绘图使用
// 3-1 create program
GLint gl_program = glCreateProgram();
// 3-2 渲染程序中加入着色器代码, shader是glCreateShader返回的顶点与片元shader
glAttachShader(gl_program, vertex_shader);
glAttachShader(gl_program, fragment_shader);
// 3-3 链接程序并使用program
glLinkProgram(program);
GLint link_status = 0;
glGetProgramiv(program, GL_LINK_STATUS, &link_status);
if (link_status != GL_TRUE)
{
    LOGD("glLinkProgram failed!");
    return;
}
glUseProgram(gl_program);
  1. 加入三维顶点数据和纹理坐标数据
    顶点坐标的由来,我们是Android播放器播放视频是2D画面,所以OpenGL的三维坐标Z轴都是0。我们需要用矩形区域来显示视频,通过绘制2个三角形形成一个矩形,走向为A->B->C->D(有△ABC和△BCD形成矩形)。这样可以得出如下的三维顶点数据:
    在这里插入图片描述
    纹理坐标张这样,纹理坐标的取点要跟三维顶点坐标的取点方向保证一致(A->B->C->D),这样既可得出如下的纹理坐标:
    在这里插入图片描述
// 加入三维顶点数据,两个三角形组成正方形
static float vers[] = {
        1.0f,-1.0f,0.0f,  // A
        -1.0f,-1.0f,0.0f, // B
        1.0f,1.0f,0.0f,   // C
        -1.0f,1.0f,0.0f,  // D
};
// 这里的aPosition跟我们vertexShader中的要保证一致
GLuint apos = (GLuint)glGetAttribLocation(program, "aPosition");
glEnableVertexAttribArray(apos);
// 把我们上面定义好的顶点数据传递给vertexShader中的aPosition
glVertexAttribPointer(apos, 3, GL_FLOAT, GL_FALSE,12, vers);
// 加入纹理坐标数据
static float txts[] = {
        1.0f,0.0f , // A
        0.0f,0.0f,  // B
        1.0f,1.0f,  // C
        0.0,1.0     // D
};
// 与顶点坐标一样,给给纹理坐标赋值
GLuint atex = (GLuint)glGetAttribLocation(program,"aTexCoord");
glEnableVertexAttribArray(atex);
glVertexAttribPointer(atex, 2, GL_FLOAT, GL_FALSE, 8, txts);
  1. 创建设置纹理层并做纹理初始化
// 设置纹理层
glUniform1i( glGetUniformLocation(program,"yTexture"),0); //对于纹理第1层
glUniform1i( glGetUniformLocation(program,"uTexture"),1); //对于纹理第2层
glUniform1i( glGetUniformLocation(program,"vTexture"),2); //对于纹理第3层

// 创建opengl纹理
GLuint gl_texture[3] = {0};
// 创建三个纹理
glGenTextures(3, gl_texture);

// 设置“y”层纹理属性
glBindTexture(GL_TEXTURE_2D, gl_texture[0]);
// 缩小的过滤器
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 设置纹理的格式和大小
glTexImage2D(GL_TEXTURE_2D,
             0,                //细节基本  0默认
             GL_LUMINANCE,     //gpu内部格式 亮度,灰度图
             width, height,    //拉升到全屏
             0,                //边框
             GL_LUMINANCE,     //数据的像素格式 亮度,灰度图 要与上面一致
             GL_UNSIGNED_BYTE, //像素的数据类型
             NULL              //纹理的数据
);

// 设置“u”纹理属性
glBindTexture(GL_TEXTURE_2D, gl_texture[1]);
// 缩小的过滤器
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 设置纹理的格式和大小
glTexImage2D(GL_TEXTURE_2D,
             0,                //细节基本 0默认
             GL_LUMINANCE,     //gpu内部格式 亮度,灰度图
             width/2,height/2, //拉升到全屏
             0,                //边框
             GL_LUMINANCE,     //数据的像素格式 亮度,灰度图 要与上面一致
             GL_UNSIGNED_BYTE, //像素的数据类型
             NULL              //纹理的数据
);

// 设置“v”纹理属性
glBindTexture(GL_TEXTURE_2D, gl_texture[2]);
// 缩小的过滤器
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 设置纹理的格式和大小
glTexImage2D(GL_TEXTURE_2D,
             0,                //细节基本 0默认
             GL_LUMINANCE,     //gpu内部格式 亮度,灰度图
             width/2,height/2, //拉升到全屏
             0,                //边框
             GL_LUMINANCE,     //数据的像素格式 亮度,灰度图 要与上面一致
             GL_UNSIGNED_BYTE, //像素的数据类型
             NULL              //纹理的数据
);
  1. 激活纹理,替换纹理内容并显示
    若buf[0]存y,buf[1]存u,buf[2]存v,则buf[0]分配的空间是uv buffer空间的4倍(YUV420P:YYYY U V)
// 激活第1层纹理y, 绑定到创建的opengl纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, gl_texture[0]);
// 替换纹理内容
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf[0]);

// 激活第2层纹理u, 绑定到创建的opengl纹理
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, gl_texture[1]);
// 替换纹理内容
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width/2, height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf[1]);

// 激活第2层纹理, 绑定到创建的opengl纹理
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, gl_texture[2]);
// 替换纹理内容
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width/2, height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf[2]);

// 绘制图形
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// 窗口显示(参数请参考EGL部分的最后一步eglMakeCurrent)
eglSwapBuffers(egl_display, egl_surface);

至此Android平台的OpenGL环境已经搭建完成,其中为了阅读方便,用了很多平行代码堆叠,大家在做的时候可以进行适当封装。

参考文献:
1、百度百科
2、FFmpeg安卓流媒体播放器开发实战视频课程 -基于NDK、C++和 FFmpeg Android(夏曹俊)

发布了2 篇原创文章 · 获赞 0 · 访问量 454

猜你喜欢

转载自blog.csdn.net/qq_18349021/article/details/104737832