下面是一个OpenGL程序的最基本结构。
// 如果我们直接使用图形厂商实现的OpenGL库将会是一件异常艰难复杂的工作// 现在市面上已经有很多第三方库,用于辅助OpenGL开发。这里采用glfw+glad库。#include "glad.h" // glad库,简化库函数调用,https://www.yuque.com/tvvhealth/cs/mma5zk#1cf8ee09#include "glfw3.h" // glfw库,辅助管理窗口、GL上下文context、用户输入、事件等。https://www.yuque.com/tvvhealth/cs/mma5zk#vVgy8void initGLFW(); // 初始化glfwvoid initGLAD(); // 初始化gladGLFWwindow* createWindow(); // 创建窗口void initGLState(); // 设置一些起始的OpenGL状态,https://www.yuque.com/tvvhealth/cs/mma5zk#vJqlnvoid cacheData(); // 缓存一部分数据到GPU显存中,如顶点数据、纹理,shader。void processInput(); // 处理输入void render(); // 渲染画面void glEnd(); // 循环结束,清理工作,比如清理显存中的buffer、纹理。void main(){initGLFW(); // 初始化glfwGLFWWindow* window = createWindow(); // 创建窗口initGLAD(); // 初始化gladinitGLState(); // 设置一些初始的OpenGL状态cacheData(); // 预加载一些数据,如顶点数据、纹理,shader。// 渲染循环,一个循环就绘制一帧,一帧就是一个画面while(!glfwWindowShouldClose(window)) {if( deltaTime >= 1/frameRate ) { // 帧率:间隔不小于1/frameRate秒才渲染一次processInput( window ); // 处理输入:键盘、鼠标事件等。render(); // 渲染一帧glfwSwapBuffers( window ); // 双缓冲机制glfwPollEvents(); // IO轮询,检查有没有触发什么事件(keys pressed/released, mouse moved etc.),// 并调用对应的回调函数(可以通过回调方法手动设置),更新窗口状态,}}glEnd(); // 循环结束,清理工作,比如一些常驻缓存}void initGLFW() {glfwInit(); // 初始化glfw库// 2、配置gflw:运行要求,至少是OpenGL 3.3以上。glfwWindowHint( GLFW_CONTEXT_VERSION_MAJOR, 3 ); // 主版本号:3glfwWindowHint( GLFW_CONTEXT_VERSION_MINOR, 3 ); // 次版本号:3glfwWindowHint( GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE ); // 使用opengl core profile核心模式,导致必须使用VAO。......; // 其他一些参数设置}void initGLAD() {if( !gladLoadGLLoader( ( GLADloadproc ) glfwGetProcAddress ) ) {cout << "Failed to initialize GLAD" << endl;return false;}return true;}GLFWwindow* createWindow() {GLFWwindow *window = glfwCreateWindow(width, height, // 窗口宽高windowName, // 窗口名称NULL, NULL );glfwMakeContextCurrent(window);// glfwSetCursorPosCallback(...); // 鼠标移动回调// glfwSetScrollCallback(...); // 鼠标滚轮回调// glfwSetFramebufferSizeCallback(...); // 窗口大小改变回调......return window;}void initGLState() {// glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); // GL_LINE:线框模式;GL_FILL:填充模式// glEnable(GL_DEPTH_TEST); // 开启深度测试// glEnable(GL_BLEND); // 开启混合// glEnable(GL_STENCIL_TEST); // 开启模板测试}void cacheData() {// 构建着色器,详细见https://www.yuque.com/tvvhealth/cs/lrzlc9// 一般就是顶点着色器、片段着色器,再高级一点用到几何着色器、细分着色器auto shader = buildShader(...);// 构建顶点数据,详细见https://www.yuque.com/tvvhealth/cs/nq6g1cbuild_VAO_VBO_EBO(...);// 构建纹理(图片文件数据转换成合适纹理格式上传至GPU显存中),// 详细见https://www.yuque.com/tvvhealth/cs/d53753ed-0848-468a-a41a-62ca25b9b4fcloadTexture(...);}void processInput(){glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS; // 按下esc键glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS; // 松开esc键......}void render() {// 绘制工作流程,总的来说就两步:// 1、准备数据:着色器、顶点数据、纹理等等// 2、执行绘制:glDrawArrays\glDrawElements。// *********************************************// ************ 一、设置GL state// *********************************************glClearColor( 0.1f, 0.1f, 0.1f, 0.1f );glEnable( GL_DEPTH_TEST ); // 深度测试......// *********************************************// ************ 二、设置shader,见OpenGL_shader部分// *********************************************glUseProgram(shaderID); // 在渲染管线上着色器使用shaderID指定的着色器。// 后续的顶点数据将由指定的顶点着色器处理// 生成的片段将由指定的片段着色器处理// 设置shader中的uniform全局变量值,比如设置MVP矩阵,纹理sampler// 下面是作用,用glsl伪代码描述:uniform int name = value,当然实际不能直接赋值。GLint uniformLocation = glGetUniformLocation(shaderID, name);glUniform1i(shaderID, name, value); // 还有很多的glUniform*(...)函数用于给各种类型赋值。// *********************************************// ************ 三、绑定纹理// *********************************************glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, texture0); // texture unit 0 绑定纹理texture0glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, texture1); // texture unit 0 绑定纹理texture1......;// 通过上面的工作,就可以在片段着色器中同时使用多张纹理(多重纹理)// *********************************************// ************ 四、绑定VAO、VBO、EBO// *********************************************// 从CPU内存大批量上传顶点数据到GPU内存中,作为顶点着色器的输入。// 详细见https://www.yuque.com/tvvhealth/cs/nq6g1cglBindVertexArray( VAO_id ); // 一般都是采用VAO,简化设置工作......;// *********************************************// ************ 五、其他一些数据,如framebuffer// *********************************************// 详细见:https://www.yuque.com/tvvhealth/cs/kos5bzglBindFrameBuffer( ...... );......;// *********************************************// ************ 六、执行绘制// *********************************************glDrawArrays(......); // 采用顶点数据绘制glDrawElements(......); // 采用顶点索引绘制,节省顶点数}void glEnd() {// 为了减少CPU和GPU之间数据传输,提升效率,我们可以将CPU内存中的数据缓存到GPU显存中。// 这也就意味这些数据会常驻于显存中,因此我们需要及时的清除。常见的缓存数据有如下这些:glDeleteTextures(n, &textures); // 清理纹理对象glDeleteVertexArrays(n, &VAOs); // 清理VAO顶点数组对象glDeleteBuffers(n, &buffers); // 清理各种缓冲对象(VBO顶点缓冲对象、EBO索引缓冲对象)glDeleteProgram(shaderProgramIDs); // 清理着色器程序对象glDeleteFramebuffers(n, &framebuffers); // 清理自建帧缓冲对象(RenderTexture)glDeleteRenderbuffers(n, &renderbuffers); // 清理自建渲染缓存对象(RenderTexture)......}
