美文网首页
1. 使用openGLES绘制三角形Hello_triangle

1. 使用openGLES绘制三角形Hello_triangle

作者: _jetson_ | 来源:发表于2021-04-02 08:28 被阅读0次

    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);
            }
    

    相关文章

      网友评论

          本文标题:1. 使用openGLES绘制三角形Hello_triangle

          本文链接:https://www.haomeiwen.com/subject/qmiphltx.html