MENU

OpenGL 学习笔记

December 8, 2023 • 阅读: 1387 • 笔记&折腾

可以使用 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 中是倒置的
}