1. 使用openGLES绘制三角形Hello_triangle

OpenGLES-triangle

总体流程

  1. 创建EGL 渲染表面
  2. 创建着色器和链接程序
  3. 输入顶点信息
  4. 开始渲染
  5. 开始绘制

源码解析

Android层操作

static void HandleCommand(struct android_app *pApp, int32_t cmd)
{
    MYESContext *myesContext = (MYESContext *)pApp->userData;

    switch (cmd) {
        case APP_CMD_SAVE_STATE:
            break;

        case APP_CMD_INIT_WINDOW:
            myesContext->eglNativeDisplayType = EGL_DEFAULT_DISPLAY;
            myesContext->eglNativeWindowType = pApp->window;

            // 1. 进行init操作
            if (myesMain(myesContext) != GL_TRUE) {
                exit(0);
            }
            break;

        case APP_CMD_TERM_WINDOW:
            // 3. shutdown操作
            if (myesContext->shutdownFunc != NULL) {
                myesContext->shutdownFunc(myesContext);
            }

            if (myesContext->userData != NULL) {
                free(myesContext->userData);
            }

            memset(myesContext, 0, sizeof(MYESContext));
            break;

        case APP_CMD_LOST_FOCUS:
            break;

        case APP_CMD_GAINED_FOCUS:
            break;
    }
}


void android_main(struct android_app *pApp)
{
    MYESContext myesContext;
    float lastTime;

    app_dummy();

    memset(&myesContext, 0, sizeof(myesContext));

    myesContext.platformData = (void *)pApp->activity->assetManager;
    pApp->onAppCmd = HandleCommand;
    pApp->userData = &myesContext;

    lastTime = GetCurrentTime();

    while (1) {
        int ident;
        int events;
        struct android_poll_source *pSource;

        while ((ident = ALooper_pollAll(0, NULL, &events, (void **)&pSource)) >= 0) {
            if (pSource != NULL) {
                pSource->process(pApp, pSource);
            }
            if (pApp->destroyRequested != 0) {
                return;
            }
        }

        if (myesContext.eglNativeWindowType == NULL) {
            continue;
        }

        if (myesContext.updateFunc != NULL) {
            float curTime = GetCurrentTime();
            float deltaTime = (curTime - lastTime);
            lastTime = curTime;
            myesContext.updateFunc(&myesContext, deltaTime);
        }

        if (myesContext.drawFunc != NULL) {
            // 2. 画出三角形
            myesContext.drawFunc(&myesContext);
            eglSwapBuffers(myesContext.eglDisplay, myesContext.eglSurface);
        }
    }
}

1. 进行init操作

int myesMain(MYESContext *myesContext)
{
    // myUserData为GLuint programObject;
    myesContext->userData = malloc(sizeof(myUserData));
    // 1. 使用EGL创建一个屏幕上的渲染表面,surface
    myesCreateWindow(myesContext, "Hello Triangle", 320, 240, MY_ES_WINDOW_RGB);

    // 2. 创建顶点和片段着色器,并编译和加载着色器,创建程序对象并链接着色器
    if (!Init(myesContext)) {
        return GL_FALSE;
    }

    // 3. 和shutdown和draw函数绑定在一起
    esRegisterShutdownFunc(myesContext, Shutdown);
    esRegisterDrawFunc(myesContext, Draw);

    return GL_TRUE;
}

1. 创建渲染表面

GLboolean myesCreateWindow(MYESContext *myesContext, const char* title, GLint width, GLint height, GLuint flags)
{
    EGLConfig config;
    EGLint majorVersion;
    EGLint minorVersion;
    EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE };

    if (myesContext == NULL) {
        return GL_FALSE;
    }

    myesContext->width = ANativeWindow_getWidth(myesContext->eglNativeWindowType);
    myesContext->height = ANativeWindow_getHeight(myesContext->eglNativeWindowType);

    // EGLDisplay EGLAPIENTRY eglGetDisplay (EGLNativeDisplayType display_id)
    // display_id:指定显示连接,默认为EGL_DEFAULT_DISPLAY,默认原生显示的连接
    // 1. 打开与EGL显示服务器的连接
    myesContext->eglDisplay = eglGetDisplay(myesContext->eglNativeDisplayType);
    if (myesContext->eglDisplay == EGL_NO_DISPLAY) {
        return GL_FALSE;
    }

    // EGLBoolean eglInitialize (EGLDisplay dpy, EGLint *major, EGLint *minor)
    // dpy:EGL显示连接,major:EGL实现返回的主版本号,minor:次版本号
    // 2. 初始化EGL
    if (!eglInitialize(myesContext->eglDisplay, &majorVersion, &minorVersion)) {
        return GL_FALSE;
    }

    {
        EGLint numConfigs = 0;
        // 配置为RGB565形式:红色为5 bits
        EGLint attribList[] = {
                EGL_RED_SIZE, 5,
                EGL_GREEN_SIZE, 6,
                EGL_BLUE_SIZE, 5,
                EGL_ALPHA_SIZE, (flags & MY_ES_WINDOW_ALPHA) ? 8 : EGL_DONT_CARE,
                EGL_DEPTH_SIZE, (flags & MY_ES_WINDOW_DEPTH) ? 8 : EGL_DONT_CARE,
                EGL_STENCIL_SIZE, (flags & MY_ES_WINDOW_STENCIL) ? 8 : EGL_DONT_CARE,
                EGL_SAMPLE_BUFFERS, (flags & MY_ES_WINDOW_MULTISAMPLE) ? 1 : 0,
                EGL_RENDERABLE_TYPE, GetContextRenderableType(myesContext->eglDisplay),
                EGL_NONE
        };

        // EGLBoolean  eglChooseConfig (EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
        // 3. 指定一组需求,让EGL推荐最佳匹配
        if (!eglChooseConfig(myesContext->eglDisplay, attribList, &config, 1, &numConfigs)) {
            return GL_FALSE;
        }

        if (numConfigs < 1) {
            return GL_FALSE;
        }
    }

    {
        EGLint format = 0;
        // 4. 获取format,为WINDOW_FORMAT_RGB_565格式,为2
        eglGetConfigAttrib(myesContext->eglDisplay, config, EGL_NATIVE_VISUAL_ID, &format);
        // int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, int32_t height, int32_t format);
        // width和height必须同时为0或者同时不为0,设为0表示使用window的默认值,不进行指定操作
        // 5. 设置原生window的format和buffer的大小
        ANativeWindow_setBuffersGeometry(myesContext->eglNativeWindowType, 0, 0, format);
    }

    // EGLSurface eglCreateWindowSurface (EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);
    // config为前面的配置;attrib_list为窗口属性列表
    // 6. 创建EGL窗口
    myesContext->eglSurface = eglCreateWindowSurface(myesContext->eglDisplay, config,
                                                    myesContext->eglNativeWindowType, NULL);
    if (myesContext->eglSurface == EGL_NO_SURFACE) {
        return GL_FALSE;
    }
    
    // EGLContext eglCreateContext (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
    // share_context允许多个EGLContext共享特定类型的数据;attrib_list:只接受EGL_CONTEXT_CLIENT_VERSION,为版本号
    // 7. 创建渲染上下文
    myesContext->eglContext = eglCreateContext(myesContext->eglDisplay, config,
            EGL_NO_CONTEXT, contextAttribs);
    if (myesContext->eglContext == EGL_NO_CONTEXT) {
        return GL_FALSE;
    }

    // EGLBoolean eglMakeCurrent (EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
    // draw为绘图表面;read为读取表面
    // 8. 指定某个EGLContext为当前上下文
    if (!eglMakeCurrent(myesContext->eglDisplay, myesContext->eglSurface,
            myesContext->eglSurface, myesContext->eglContext)) {
        return GL_FALSE;
    }

    return GL_TRUE;
}

(1)EGL的作用:

  • 与设备的原生窗口系统通信
  • 查询绘图表面的可用类型和配置
  • 创建绘图表面
  • 在OpenGL ES 3.0和其他图形渲染API如OpenVG等之间同步渲染
  • 管理纹理贴图等渲染资源

(2)ANativeWindow是C/C++中定义的一个结构体,等同于Java中的Surface

2. 创建着色器和链接程序

int Init(MYESContext *myesContext)
{
    myUserData *userData = (myUserData *)myesContext->userData;
    char vShaderStr[] =
            "#version 300 es                            \n"
            "layout(location = 0) in vec4 vPosition;    \n"
            "void main()                                \n"
            "{                                          \n"
            "   gl_Position = vPosition;                \n"
            "}                                          \n";
    char fShaderStr[] =
            "#version 300 es                            \n"
            "precision mediump float;                   \n"
            "out vec4 fragColor;                        \n"
            "void main()                                \n"
            "{                                          \n"
            "   fragColor = vec4(1.0, 0.0, 0.0, 1.0);   \n"
            "}                                          \n";
    GLuint vertexShader;
    GLuint fragmentShader;
    GLuint programObject;
    GLint linked;

    // 1. 创建着色器
    vertexShader = LoadShader(GL_VERTEX_SHADER, vShaderStr);
    fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);
    // 程序对象是一个容器对象,可以将着色器与之连接,并链接一个最终的可执行程序
    // 2. 创建一个程序对象
    programObject = glCreateProgram();

    if (programObject == 0) {
        return 0;
    }

    // 3. 连接着色器
    glAttachShader(programObject, vertexShader);
    glAttachShader(programObject, fragmentShader);
    // 4. 链接程序
    glLinkProgram(programObject);
    // 获取链接程序状态
    glGetProgramiv(programObject, GL_LINK_STATUS, &linked);

    if (!linked) {
        GLint infoLen = 0;
        // 获取日志信息长度
        glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 1) {
            char *infoLog = (char *)malloc(sizeof(char) *infoLen);
            glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
            esLogMessage("Error linking program:\n%s\n", infoLog);
            free(infoLog);
        }

        glDeleteProgram(programObject);
        return 0;
    }

    userData->programObject = programObject;
    glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    return 1;
}
1. 创建着色器
GLuint LoadShader(GLenum type, const char *shaderSrc)
{
    GLuint shader;
    GLint compiled;

    // GL_VERTEX_SHADER为顶点类型的,GL_FRAGMENT_SHADER为片段类型的
    // 1. 创建着色器
    shader = glCreateShader(type);
    if (shader == 0) {
        return 0;
    }

    // void glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
    // count为着色器字符串数量;length为多个字符串的时候,指定各个字符串长度的一个数组,当count为1时其为NULL
    // 2. 提供着色器源代码
    glShaderSource(shader, 1, &shaderSrc, NULL);
    // 3. 编译着色器
    glCompileShader(shader);
    // 4. 查询信息,这里查询编译信息
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);

    if (!compiled) {
        GLint infoLen = 0;
        // 查询信息日志长度
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 1) {
            char *infoLog = (char *)malloc(sizeof(char) *infoLen);
            glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
            esLogMessage("Error compiling shader:\n%s\n", infoLog);
            free(infoLog);
        }
        // 删除着色器
        glDeleteShader(shader);
        return 0;
    }

    return shader;
}
2. OpenGL ES 着色器语言代码解析

(1)顶点着色器

    char vShaderStr[] =
            "#version 300 es                            \n"     // 着色器版本规范
            "layout(location = 0) in vec4 vPosition;    \n"     // layout用来指定顶点属性的索引,这里为0,表示位置;in表示顶点着色器中,每个顶点的输入;
            "void main()                                \n"
            "{                                          \n"
            "   gl_Position = vPosition;                \n"     // gl_Position为顶点着色器的输出向量(内建变量)
            "}                                          \n";

    0层属性,也就是位置输入(vPosition) -> gl_Position
    
    顶点着色器输出 out vec3 v_color 颜色  ->  片段着色器输入 in vec3 v_color,就可以使用顶点着色器输出的颜色作为片段着色器的输入了

(2)片段着色器

    char fShaderStr[] =
            "#version 300 es                            \n"
            "precision mediump float;                   \n"     // 设置float精度为mediump
            "out vec4 fragColor;                        \n"     // out表示片段着色器的输出变量
            "void main()                                \n"
            "{                                          \n"
            "   fragColor = vec4(1.0, 0.0, 0.0, 1.0);   \n"     // 设置为红色
            "}                                          \n";

3. 进行draw操作

void Draw(MYESContext *myesContext)
{
    myUserData *userData = (myUserData *)myesContext->userData;
    // 三个顶点信息
    GLfloat vVertices[] = {
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f
    };
    // 1. 通知OpenGL ES用于绘制的2D渲染表面的原点(x,y)坐标,宽度和高度
    glViewport(0, 0, myesContext->width, myesContext->height);
    // 2. 清除颜色缓冲区;有颜色、深度和模板缓冲区
    glClear(GL_COLOR_BUFFER_BIT);
    // 3. 将程序设为活动程序
    glUseProgram(userData->programObject);

    // void glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
    // index为第几个属性,属性有顶点的位置为0/纹理为1/法线为3;size为一个顶点所有数据的个数,这里XYZ为3个;
    // type为顶点描述数据的类型,这里为FLOAT;normalized为是否需要显卡把数据归一化到-1到+1区间,这里不需要为FALSE
    // stride连续顶点属性之间的偏移量,0表示他们为紧密排列在一起的。pointer为顶点数组
    // 4. 加载顶点位置到GL中
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
    // 5. 使能顶点数组顶点位置属性,也就是0属性;如果什么都不使能的话,就使用常量顶点属性0
    glEnableVertexAttribArray(0);
    // 6. 绘制三角形
    glDrawArrays(GL_TRIANGLES, 0, 3);
}

4. 进行显示

        if (myesContext.drawFunc != NULL) {
            myesContext.drawFunc(&myesContext);
            // 显示在屏幕上
            eglSwapBuffers(myesContext.eglDisplay, myesContext.eglSurface);
        }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容