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, ¤tts);
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;
}
基本步骤如下:
-
GLFW初始化和窗口创建
初始化GLFW并设置OpenGL ES上下文版本
创建窗口并设置上下文,将创建的窗口用于opengl-es上下文,此时opengl-es和系统的窗口系统相关联 , 创建着色器,编译着色器,最近将其链接到一个程序对象 -
编译顶点着色器和片段着色器,并将它们链接到一个程序中
-
定义一个简单的三角形顶点数据,并绘制三角形
-
设置视口,清除颜色缓冲区,加载顶点数据,并调用绘制命令
-
主循环中不断交换缓冲区并处理事件,以保持窗口响应。
-
删除程序和着色器,销毁窗口,并终止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 的基本一致
实现效果如下: