OpenGL-ES 学习(6)---- Ubuntu OES 环境搭建

时间:2024-06-14 07:01:37

OpenGL-ES Ubuntu 环境搭建

此的方法在 ubuntu 和 deepin 上验证都可以成功搭建

目录

    • OpenGL-ES Ubuntu 环境搭建
      • 软件包安装
      • 第一个三角形
        • 基于 glfw 实现
        • 基于 X11 实现

软件包安装

sudo apt install libx11-dev
sudo apt install libglfw3 libglfw3-dev
sudo apt-get install libgles2-mesa
sudo apt-get install libgles2-mesa-dev

检查环境是否安装成功:
/usr/include 下是否有 EGL GL GLES2 GLES3 的目录

Note: 上面的环境中同时安装了 x11 和 glfw,实际上只需要安装一个自己需要的即可, x11 和 glfw 都是为 OES 环境对接到窗口系统中,
个人觉得 x11 的API 对 egl 的封装比较标准话一些,可以用于学习 egl 的api

第一个三角形

基于 glfw 实现
#include <stdio.h>
#include <time.h>
#include <GLES2/gl2.h>
#include <GLFW/glfw3.h>

// Vertex Shader source code
// Vertex Shader source code
const GLchar* vertexSource =
        "#version 300 es\n"
        "layout(location = 0) in vec4 position;\n"
        "void main() {\n"
        "    gl_Position = position;\n"
        "}\n";

// Fragment Shader source code
const GLchar* fragmentSource =
        "#version 300 es\n"
        "precision mediump float;\n"
        "out vec4 fragColor;\n"
        "void main() {\n"
        "    fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
        "}\n";

int main() {
    printf("main testsuites enter n");
    // Initialize GLFW
    if (!glfwInit()) {
        fprintf(stderr, "Failed to initialize GLFW\n");
        return -1;
    }

    // Create a windowed mode window and its OpenGL context
    GLFWwindow* window = glfwCreateWindow(640, 480, "opengles-glfw", NULL, NULL);
    if (!window) {
        fprintf(stderr, "Failed to create GLFW window\n");
        glfwTerminate();
        return -1;
    }
    // Make the window's context current
    glfwMakeContextCurrent(window);

    // Load the OpenGL ES functions
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    // Create and compile the vertex shader
    GLint status;
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexSource, NULL);
    glCompileShader(vertexShader);
    // Check for compilation errors
    if (status != GL_TRUE) {
        char buffer[512];
        glGetShaderInfoLog(vertexShader, 512, NULL, buffer);
        fprintf(stderr, "Vertex Shader Compile Error: %s\n", buffer);
    }

    // Create and compile the fragment shader
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentSource, NULL);
    glCompileShader(fragmentShader);
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &status);
    if (status != GL_TRUE) {
        char buffer[512];
        glGetShaderInfoLog(fragmentShader, 512, NULL, buffer);
        fprintf(stderr, "Fragment Shader Compile Error: %s\n", buffer);
    }

    // Link the vertex and fragment shader into a shader program
    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    // Specify the layout of the vertex data
    GLfloat vertices[] = {
            0.0f,  0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
    };
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Specify the layout of the vertex data
    glUseProgram(shaderProgram);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0);
    glEnableVertexAttribArray(0);

    // Main loop
    while (!glfwWindowShouldClose(window)) {

        // calculate for extecute time,about 16.6ms every period
        {
            struct timespec currentts;
            static uint64_t timeInMiliSeconds = 0;
            clock_gettime(CLOCK_REALTIME, &currentts);
            uint64_t currentMilliseconds = currentts.tv_sec * 1000LL + currentts.tv_nsec / 1000000;
            int periodInMs = currentMilliseconds - timeInMiliSeconds;
            timeInMiliSeconds = currentMilliseconds;
            printf("current time in milliseconds %lld period:%d\n", currentMilliseconds,
                   (periodInMs > 0 ? periodInMs : -1));
        }
        // Clear the screen
        glClear(GL_COLOR_BUFFER_BIT);

        // Draw the triangle
        glUseProgram(shaderProgram);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glEnableVertexAttribArray(0);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        // Swap front and back buffers
        glfwSwapBuffers(window);
        // Poll for and process events
        glfwPollEvents();
    }

    // Clean up
    glDeleteBuffers(1, &vbo);
    glDeleteProgram(shaderProgram);
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}

基本步骤如下:

  1. GLFW初始化和窗口创建
    初始化GLFW并设置OpenGL ES上下文版本
    创建窗口并设置上下文,将创建的窗口用于opengl-es上下文,此时opengl-es和系统的窗口系统相关联 , 创建着色器,编译着色器,最近将其链接到一个程序对象

  2. 编译顶点着色器和片段着色器,并将它们链接到一个程序中

  3. 定义一个简单的三角形顶点数据,并绘制三角形

  4. 设置视口,清除颜色缓冲区,加载顶点数据,并调用绘制命令

  5. 主循环中不断交换缓冲区并处理事件,以保持窗口响应。

  6. 删除程序和着色器,销毁窗口,并终止GLFW

对应的 CMakeLists.txt 实现

cmake_minimum_required(VERSION 3.27)
project(opengles_glfw C)

set(CMAKE_C_STANDARD 11)

find_package(PkgConfig REQUIRED)
pkg_search_module(GLFW REQUIRED glfw3)

include_directories(${GLFW_INCLUDE_DIRS})

add_executable(opengles_glfw main.c)
target_link_libraries(opengles_glfw ${GLFW_LIBRARIES} GLESv2)

在程序主循环中,还添加了计时相关的代码逻辑,在glfw 的模式下,主循环也是跟屏幕刷新率相同也是60Hz, 可以看到每次循环体的执行间隔都是 16.6ms

current time in milliseconds 1717986851291 period:14
current time in milliseconds 1717986851308 period:17
current time in milliseconds 1717986851324 period:16
基于 X11 实现

基于 X11 实现的 opengl-es 环境如下:

#include <malloc.h>
#include "glesbasicTriangle.h"

typedef struct {
    GLuint programObject;
} UserData;

static int initInternal(ESContext* esContext) {
    UserData *userData = esContext->userData;
    // Vertex Shader source code
    const char* vertexShaderSource =
            "#version 300 es                            \n"
            "layout(location = 0) in vec4 a_position;   \n"
            "void main() {\n"
            "   gl_Position = a_position;\n"
            "}\n";

    // Fragment Shader source code
    const char* fragmentShaderSource =
            "#version 300 es                            \n"
            "precision mediump float;\n"
            "layout(location = 0) out vec4 outColor;             \n"
            "void main() {\n"
            "   outColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
            "}\n";

    // 这里封装了 createshader-compilershader-createprogramobject-link programoobject 的操作
    GLuint programObject = esLoadProgram(vertexShaderSource, fragmentShaderSource);
    if (programObject == 0) {
        return GL_FALSE;
    }

    // Store the program object
    userData->programObject = programObject;
    return GL_TRUE;
}

static int drawLoopInternal(ESContext* esContext) {
    // Vertex data
    GLfloat vertices[] = {
            0.0f,  0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f
    };

    // Set the viewport
    glViewport(0, 0, 640, 480);

    UserData *userData = esContext->userData;
    glUseProgram(userData->programObject);

    // Clear the color buffer
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(0);

    // Draw the triangle
    glDrawArrays(GL_TRIANGLES, 0, 3);

    // Swap buffers
    eglSwapBuffers(esContext->eglDisplay, esContext->eglSurface);
}

static int cleanupInternal(ESContext* esContext) {
    printf("%s enter!.\n",__FUNCTION__);
    UserData *userData = esContext->userData;
    glDeleteProgram(userData->programObject);
    eglDestroySurface(esContext->eglDisplay, esContext->eglSurface);
    eglDestroyContext(esContext->eglDisplay, esContext->eglContext);
    eglTerminate(esContext->eglDisplay);
    XDestroyWindow(esContext->x_display, esContext->win);
    XCloseDisplay(esContext->x_display);
    return GL_TRUE;
}


int testbasicDrawTriangle(ESContext* esContext) {
    printf("%s enter!.\n", __FUNCTION__);
    esContext->userData = (UserData*)malloc(sizeof(UserData));

    initInternal(esContext);
    while (1) {
        XEvent xev;
        while (XPending(esContext->x_display)) {
            XNextEvent(esContext->x_display, &xev);
            if (xev.type == KeyPress) {
                cleanupInternal(esContext);
            }
        }
        drawLoopInternal(esContext);
    }

}

其中在调用 testbasicDrawTriangle 之前, 还有窗口系统的准备工作需要完成,实现就是下面的函数:esCreateWindow 其他文件中实现, main 函数在调用 testbasicDrawTriangle 之前,就已经调用了 esCreateWindow 函数

GLboolean esCreateWindow ( ESContext *esContext, const char *title, GLint width, GLint height, GLuint flags )
{
// Open X11 display
    EGLint majorVersion;
    EGLint minorVersion;

    Display* x_display = XOpenDisplay(NULL);
    if (x_display == NULL) {
        printf("Failed to open X display\n");
        return GL_FALSE;
    }
    esContext->x_display = x_display;
    esContext->width = width;
    esContext->height = height;

    // Create X11 window
    Window root = DefaultRootWindow(esContext->x_display);
    XSetWindowAttributes swa;
    swa.event_mask = ExposureMask | PointerMotionMask | KeyPressMask;
    Window win = XCreateWindow(
            esContext->x_display, root,
            0, 0, width, height, 0,
            CopyFromParent, InputOutput,
            CopyFromParent, CWEventMask,
            &swa);
    XMapWindow(esContext->x_display, win);
    XStoreName(esContext->x_display, win, title);
    esContext->win = win;

    // Initialize EGL
    EGLDisplay egl_display = eglGetDisplay((EGLNativeDisplayType)esContext->x_display);
    if (egl_display == EGL_NO_DISPLAY) {
        printf("Failed to get EGL display\n");
        return GL_FALSE;
    }
    esContext->eglDisplay = egl_display;

    if (!eglInitialize(esContext->eglDisplay, &majorVersion, &minorVersion)) {
        printf("Failed to initialize EGL\n");
        return GL_FALSE;
    }

    printf("%s majorVersion:%d minorVersion:%d \n", __FUNCTION__, majorVersion, minorVersion);

    // Choose an EGL config
    EGLint configAttribs[] = {
            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
            EGL_BLUE_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_RED_SIZE, 8,
            EGL_DEPTH_SIZE, 8,
            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
            EGL_NONE
    };

    EGLint attribList[] =
    {
            EGL_RED_SIZE,       5,
            EGL_GREEN_SIZE,     6,
            EGL_BLUE_SIZE,      5,
            EGL_ALPHA_SIZE,     ( flags & ES_WINDOW_ALPHA ) ? 8 : EGL_DONT_CARE,
            EGL_DEPTH_SIZE,     ( flags & ES_WINDOW_DEPTH ) ? 8 : EGL_DONT_CARE,
            EGL_STENCIL_SIZE,   ( flags & ES_WINDOW_STENCIL ) ? 8 : EGL_DONT_CARE,
            EGL_SAMPLE_BUFFERS, ( flags & ES_WINDOW_MULTISAMPLE ) ? 1 : 0,
            // if EGL_KHR_create_context extension is supported, then we will use
            // EGL_OPENGL_ES3_BIT_KHR instead of EGL_OPENGL_ES2_BIT in the attribute list
            EGL_RENDERABLE_TYPE, GetContextRenderableType ( esContext->eglDisplay ),
            EGL_NONE
    };

    EGLConfig egl_config;
    EGLint numConfigs;
    eglChooseConfig(esContext->eglDisplay, attribList, &egl_config, 1, &numConfigs);

    if (numConfigs != 1) {
        printf("Failed to choose EGL config\n");
        return GL_FALSE;
    }

    // Create an EGL window surface
    EGLSurface egl_surface = eglCreateWindowSurface(esContext->eglDisplay, egl_config, (EGLNativeWindowType)esContext->win, NULL);
    if (egl_surface == EGL_NO_SURFACE) {
        printf("Failed to create EGL surface\n");
        return GL_FALSE;
    }
    esContext->eglSurface = egl_surface;

    // Create an EGL context
    EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
    EGLContext egl_context = eglCreateContext(esContext->eglDisplay, egl_config, EGL_NO_CONTEXT, contextAttribs);
    if (egl_context == EGL_NO_CONTEXT) {
        printf("Failed to create EGL context\n");
        return GL_FALSE;
    }
    esContext->eglContext = egl_context;

    // Make the context current
    if (!eglMakeCurrent(esContext->eglDisplay, esContext->eglSurface , esContext->eglSurface , esContext->eglContext)) {
        printf("Failed to make EGL context current\n");
        return GL_FALSE;
    }
    return GL_TRUE;
}

esCreateWindow 的代码中,使用 x_display 关联到了 egl 的display 对象中,同时,使用在 eglCreateWindowSurface 中 使用了 x11 创建的 win handle 创建 EGLSurface

其余绘制三角形的步骤和 glfw 的基本一致
实现效果如下:
basic_triangles