IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    OpenGL 学习笔记

    四十九C-137发表于 2023-12-08 19:24:00
    love 0

    可以使用 eglGetConfig() 获取指定 display 支持的所有config

    dsp = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    
    EGLConfig xconfig[100];
        EGLint xsize;
        if (!eglGetConfigs(dsp, &xconfig, 100, &xsize)){
                printf("  eglGetError() = 0x%x \n", eglGetError());
                assert(0 && "eglGetConfigs failad");
        }
        printf(" xsize = %d \n", xsize);
        for (int i = 0; i < xsize; ++i) {
                    EGLint value;
                    eglGetConfigAttrib(dsp, xconfig[i], EGL_RED_SIZE, &value);
                    printf("Config %d: Red Size = %d\n", i, value);
                    eglGetConfigAttrib(dsp, xconfig[i], EGL_GREEN_SIZE,&value);
                    printf("Config %d: Green Size = %d\n", i, value);
                    eglGetConfigAttrib(dsp, xconfig[i], EGL_BLUE_SIZE, &value);
                    printf("Config %d: Blue Size = %d\n", i, value);
                    eglGetConfigAttrib(dsp, xconfig[i], EGL_RENDERABLE_TYPE, &value);
                    printf("Config %d: EGL_RENDERABLE_TYPE = 0x%x\n", i, value);
                    eglGetConfigAttrib(dsp, xconfig[i], EGL_SURFACE_TYPE, &value);
                    printf("Config %d: EGL_SURFACE_TYPE = 0x%x\n", i, value);
    
        }

    非常好用的函数:eglGetError()/glGetError()
    用来排错,只调用了 egl/gl 接口后,可以使用该函数来诊断调用有没有出错,用法:

    eglInitialize(dsp, &majorVersion, &minorVersion);
    printf("  eglGetError() = 0x%x \n", eglGetError());
    
    position_loc = glGetAttribLocation((GLuint)program, "position");
    printf("  eglGetError() = 0x%x \n", glGetError());

    使用 EGL 创建上下文:

    EGLDisplay display;
        EGLContext context;
        EGLSurface surface;
        // 2. 配置 EGL 属性
        const EGLint attribs[] = {
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_DEPTH_SIZE, 24,
            EGL_SAMPLES, 0,
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT,
            EGL_SURFACE_TYPE,   EGL_PBUFFER_BIT,
            EGL_ALPHA_SIZE,         0,
            EGL_STENCIL_SIZE,       8,
            EGL_NONE
        };
        // 1. 初始化 EGL 显示连接
        display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        if (display == EGL_NO_DISPLAY)
        {
            assert(0 && "EGL get display failed!");
        }
        if (!eglInitialize(display, NULL, NULL))
        {
            assert(0 && "EGL initialize falledl");
        }
        if (!eglBindAPI(EGL_OPENGL_API))
        {
            assert(0 && "EGL bind API failed!");
        }
        
        EGLConfig config;
        EGLint numConfigs;
        if(!eglChooseConfig(display, attribs, &config, 1, &numConfigs)){
            assert(0 && "eglchooseConfig failad");
        }
        if (numConfigs < 1)
        {
            assert(numConfigs < 1);
            exit(0);
        }
        // 3. 创建 EGL 上下文
        const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
        context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
        if (context == EGL_NO_CONTEXT)
        {
            assert(0 && "eglCreateContext failed");
        }
        // 4. 创建表面
        const EGLint pbufferAttribs[] = {
            EGL_WIDTH, SCREEN_WIDTH,
            EGL_HEIGHT, SCREEN_HEIGHT,
            EGL_NONE,
        };
        surface = eglCreatePbufferSurface(display, config, pbufferAttribs);
        if ( surface == EGL_NO_SURFACE)
        {
            printf(" EGL_NO_SURFACE \n");
            assert(0 && "glCreateWindowsurface failed");
        }
        // 5. 激活上下文
        if (!eglMakeCurrent(display, surface, surface, context)){
            assert(0 && "eglMakeCurrent failed");
        }
    

    创建完上下文之后,可以使用 gl 接口获取硬件&驱动信息:

    printf(" GPU vendor : %s\n", glGetString(GL_VENDOR));
    printf(" GL version : %s\n", glGetString(GL_VERSION));
    

    使用 shader 创建着色器程序:

    static const char vertex_shader[] =
    "#version 300 es\n"
    "in vec3 position;\n"
    "in vec3 color;\n"
    "out vec3 vertexColor;\n"
    "void main()\n"
    "{\n"
    "    gl_Position = vec4(position, 1.0);\n"
    "    vertexColor = color;\n"
    "}\0";
    static const char fragment_shader[] =
    "#version 300 es\n"
    "precision mediump float;\n"
    "in vec3 vertexColor;\n"
    "out vec4 fragmentColor;\n"
    "void main()\n"
    "{\n"
    "    fragmentColor = vec4(vertexColor, 1.0);\n"
    "}\0";
    
    
    GLuint gles2_api_create_program(const char *vs_src, const char *fs_src)
    {
        GLuint prog, vs, fs;
        GLint compiled, linked, pos;
        char *buffer;
        if ((NULL == vs_src) || (NULL == fs_src))
        {
            printf("gles2_api_create_program: Invalid input parameters\n");
            return 0;
        }
        buffer = (char *)malloc(GLES2_API_BUFFERSIZE);
        if (NULL == buffer)
        {
            printf("gles2_api_create_program: Unable to allocate %i bytes.\n", GLES2_API_BUFFERSIZE);
            return 0;
        }
        vs = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vs, 1, &vs_src, NULL);
        glCompileShader(vs);
        glGetShaderiv(vs, GL_COMPILE_STATUS, &compiled);
        if (GL_FALSE == compiled)
        {
            glGetShaderInfoLog(vs, GLES2_API_BUFFERSIZE - 1, NULL, buffer);
            printf("gles2_api_create_program: Unable to compile vertex shader - reason: \n%s\n", buffer);
            free(buffer);
            return 0;
        }
        fs = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fs, 1, &fs_src, NULL);
        glCompileShader(fs);
        glGetShaderiv(fs, GL_COMPILE_STATUS, &compiled);
        if (GL_FALSE == compiled)
        {
            glGetShaderInfoLog(fs, GLES2_API_BUFFERSIZE - 1, NULL, buffer);
            printf("gles2_api_create_program: Unable to compile fragment shader - reason: \n%s\n", buffer);
            free(buffer);
            return 0;
        }
        prog = glCreateProgram();
        glAttachShader(prog, vs);
        glAttachShader(prog, fs);
        glLinkProgram(prog);
        glGetProgramiv(prog, GL_LINK_STATUS, &linked);
        if (GL_FALSE == linked)
        {
            glGetProgramInfoLog(prog, GLES2_API_BUFFERSIZE - 1, NULL, buffer);
            printf("gles2_api_create_program: Unable to link program - reason: \n%s\n\n", buffer);
            free(buffer);
            return 0;
        }
        glUseProgram(prog);
        free(buffer);
        return prog;
    }

    如果 glLinkProgram() 没有报错,则说明着色器程序创建成功。
    使用创建的着色器程序绘制矩形:

    {
            GLint j, position_loc, color_loc, progs;
            progs = create_program();
            glGetIntegerv(GL_CURRENT_PROGRAM, &progs);
            glUseProgram(progs);
            GLfloat baseColors[] = 
                {(rand() % 256) / 255.0f, (rand() % 256) / 255.0f, (rand() % 256) / 255.0f};
            // float baseVertices[] = {
            //  // 第一个三角形
            //  0.5f,  0.5f, 0.0f,  // 右上角
            //  0.5f, -0.5f, 0.0f,  // 右下角
            //  -0.5f,  0.5f, 0.0f, // 左上角
            //  // 第二个三角形
            //  0.5f, -0.5f, 0.0f,  // 右下角
            //  -0.5f, -0.5f, 0.0f, // 左下角
            //  -0.5f,  0.5f, 0.0f  // 左上角
            // };
            float vertices[12];
            GLfloat colors[12];
            for (int i = 0; i < 12; i += 3) {
                colors[i] = baseColors[0];
                colors[i + 1] = baseColors[1];
                colors[i + 2] = baseColors[2];
            }
            float ndcX1, ndcY1, ndcX2, ndcY2;
            // glClear(GL_COLOR_BUFFER_BIT);
            for (j = 0; j < PRIMSPERLIST; j++) {
                // 更新顶点数据
                // 生成随机位置
                GLfloat cx = 0, cy = 0;
                if (WIDTH < g_nScreenW) {
                    cx = rand() % (g_nScreenW - (GLint)WIDTH);
                }
                if (HEIGHT < g_nScreenH) {
                    cy = rand() % (g_nScreenH - (GLint)HEIGHT);
                }
                pixelToNDC(cx, cy, g_nScreenW, g_nScreenH, &ndcX1, &ndcY1);
                pixelToNDC(cx + WIDTH, cy + HEIGHT, g_nScreenW, g_nScreenH, &ndcX2, &ndcY2);
                
                float vertices[] = {
                    ndcX1, ndcY1, 0.0f,  // 左下角
                    ndcX2, ndcY1, 0.0f,  // 右下角
                    ndcX2, ndcY2, 0.0f,   // 右上角
                    ndcX1, ndcY2, 0.0f  // 左上角
                };
                position_loc = glGetAttribLocation((GLuint)progs, "position");
                glEnableVertexAttribArray(position_loc);
                glVertexAttribPointer((GLuint)position_loc, 3, GL_FLOAT, GL_FALSE, 0, vertices);
                color_loc = glGetAttribLocation((GLuint)progs, "color");
                glEnableVertexAttribArray(color_loc);
                glVertexAttribPointer((GLuint)color_loc, 3, GL_FLOAT, GL_FALSE, 0, colors);
                glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
                
                glDisableVertexAttribArray((GLuint)position_loc);
                glDisableVertexAttribArray((GLuint)color_loc);
            }
            // glFinish();
            glDeleteProgram(progs);
        }

    在使用着色器程序绘制顶点时,使用glVertexAttribPointer() 函数传入的 vertices[] 数组代表顶点的坐标,该坐标为标准化设备坐标(NDC),x, y, z 轴的坐标范围是 [-1.0, 1.0]。而不是像素坐标。使用标准化设备坐标的好处是提供一个与设备无关的坐标系统。在这个系统中,任何在屏幕上显示的点的坐标都需要在 -1.0 到 1.0 的范围内。

    如果目标是绘制像素坐标,这需要进行 NDC坐标转换,将窗口中的像素坐标转换为 NDC 坐标:
    参考函数:

    void pixelToNDC(float pixelX, float pixelY, int windowWidth, int windowHeight, float *ndcX, float *ndcY) {
        *ndcX = (pixelX / (float)windowWidth) * 2.0f - 1.0f;
        *ndcY = (pixelY / (float)windowHeight) * 2.0f - 1.0f;
        *ndcY = -*ndcY; // Y 轴在 OpenGL 中是倒置的
    }


沪ICP备19023445号-2号
友情链接