第一个3D程序
1 #include <GL/glew.h> 2 #include <GLFW/glfw3.h> 3 #include <string> 4 #include <iostream> 5 #include <fstream> 6 #include <cmath> 7 #include <glm/glm.hpp> 8 #include <glm/gtc/type_ptr.hpp> 9 #include <glm/gtc/matrix_transform.hpp> 10 #include "Utils.h" 11 using namespace std; 12 13 const int numVAOs = 1; 14 const int numVBOs = 2; 15 16 float cameraX, cameraY, cameraZ; 17 float cubeLocX, cubeLocY, cubeLocZ; 18 19 GLuint renderingProgram; 20 GLuint vao[numVAOs]; 21 GLuint vbo[numVBOs]; 22 23 GLuint mvLoc, projLoc; 24 int width, height; 25 float aspect; 26 glm::mat4 pMat, vMat, mMat, mvMat; 27 28 29 void setupVertices() 30 { 31 // 一个立方体,6个面需要12个三角形,共有36个点(包含重复点),108个坐标值 32 float vertexPositions[108] = { 33 -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 34 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 35 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 36 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 37 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 38 -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 39 -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 40 -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 41 -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 42 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 43 -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 44 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f 45 }; 46 // 生成numVAOs个顶点数组对象 47 glGenVertexArrays(numVAOs, vao); 48 // 绑定(激活)第一个顶点数组对象 49 glBindVertexArray(vao[0]); 50 // 生成numVBOs个缓存区对象 51 glGenBuffers(numVBOs, vbo); 52 // 绑定第一个缓存区对象为顶点缓存类型 53 glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); 54 // 把顶点数据送入顶点缓存 55 glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW); 56 } 57 58 // 在循环外部调用,负责初始化只需执行一次的任务 59 void init(GLFWwindow* window) { 60 // 创建着色器程序 61 renderingProgram = Utils::createShaderProgram("vertShader.glsl", "fragShader.glsl"); 62 // 相机在世界坐标中的位置 63 cameraX = 0.0f, cameraY = 0.0f, cameraZ = 8.0f; 64 // 立方体在世界坐标中的位置 65 cubeLocX = 0.0f, cubeLocY = -2.0f, cubeLocZ = 0.0f; 66 // 将立方体顶点数据加载到顶点缓存对象中 67 setupVertices(); 68 } 69 // 在循环内部调用,实现重复执行,调用它的速率被称为帧率 70 void display(GLFWwindow* window, double currentTime) 71 { 72 // 清除深度缓存 73 glClear(GL_DEPTH_BUFFER_BIT); 74 // 指定颜色缓冲区清除后填充的值 75 glClearColor(0.0, 0.0, 0.0, 1.0); 76 // 用指定颜色清除(填充)颜色缓存区 77 glClear(GL_COLOR_BUFFER_BIT); 78 // 将含有两个已编译着色器的程序载入OpenGL管线阶段,并没有运行着色器 79 glUseProgram(renderingProgram); 80 81 // 获取MV矩阵和投影矩阵的统一变量 82 mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix"); 83 projLoc = glGetUniformLocation(renderingProgram, "proj_matrix"); 84 85 // 构建透视矩阵 86 glfwGetFramebufferSize(window, &width, &height); // 检索窗口的帧缓冲区的当前大小 87 aspect = (float)width / (float)height; // 计算纵横比 88 pMat = glm::perspective(1.0472f, aspect, 0.1f, 1000.0f); // 1.0472 radians = 60 degrees 89 90 // 构建视图矩阵、模型矩阵和MV矩阵 91 vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ)); 92 mMat = glm::translate(glm::mat4(1.0f), glm::vec3(cubeLocX, cubeLocY, cubeLocZ)); 93 mvMat = vMat * mMat; 94 95 // 将透视矩阵和MV矩阵赋值给相应的统一变量 96 glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat)); 97 glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat)); 98 99 // 将VBO关联给顶点着色器中相应的顶点属性 100 glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); // 激活第一个顶点缓存对象 101 // 第一个参数指定我们要配置的顶点属性layout(location = 0); 102 // 第二个参数指定顶点属性的大小; 103 // 第三个参数指定数据的类型; 104 // 第四个参数定义我们是否希望数据被标准化(Normalize); 105 // 第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔。 106 // 由于下个组位置数据在3个float之后,我们把步长设置为3 * sizeof(float)。 107 // 要注意的是由于我们知道这个数组是紧密排列的(在两个顶点属性之间没有空隙)我们也可以设置为0来让OpenGL决定具体步长是多少(只有当数值是紧密排列时才可用)。 108 // 一旦我们有更多的顶点属性,我们就必须更小心地定义每个顶点属性之间的间隔,这个参数的意思简单说就是从这个属性第二次出现的地方到整个数组0位置之间有多少字节。 109 // 最后一个参数的类型是void * ,数据指针, 这个值受到VBO的影响; 110 // 1:在不使用VBO的情况下,就是一个指针,指向的是需要上传到顶点数据指针, 111 // 项目中通常在不使用VBO的情况下,绘制之前,执行glBindBuffer(GL_ARRAY_BUFFER, 0),否则会导致数组顶点无效,界面无法显示; 112 // 2:使用VBO的情况下,先要执行glBindBuffer(GL_ARRAY_BUFFER, 1), 113 // 如果一个名称非零的缓冲对象被绑定至GL_ARRAY_BUFFER目标(见glBindBuffer)且此时一个定点属性数组被指定了, 114 // 那么pointer被当做该缓冲对象数据存储区的字节偏移量。并且,缓冲对象绑定(GL_ARRAY_BUFFER_BINDING)会被存为索引为index的顶点属性数组客户端状态; 115 // 此时指针指向的就不是具体的数据了。因为数据已经缓存在缓冲区了。这里的指针表示位置数据在缓冲中起始位置的偏移量(Offset)。 116 // 由于位置数据在数组的开头,所以这里是0。我们会在后面详细解释这个参数。 117 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0); 118 // 启用第一个顶点属性 119 glEnableVertexAttribArray(0); 120 // 调整OpenGL设置 121 glEnable(GL_DEPTH_TEST); 122 // 深度测试函数:如果输入的深度值小于或等于参考值,则通过 123 glDepthFunc(GL_LEQUAL); 124 // 线框模式 125 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 126 // 启动管线处理过程,开始绘制 127 glDrawArrays(GL_TRIANGLES, 0, 36); 128 } 129 130 int main(void) 131 { 132 // 如果glfw初始化失败则返回 133 if (!glfwInit()) { exit(EXIT_FAILURE); } 134 // 设置OpenGL程序的主版本号 135 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); 136 // 设置OpenGL程序的副版本号 137 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 138 // 借助GLFW创建一个窗口 139 GLFWwindow* window = glfwCreateWindow(600, 600, "Graphics Program With OpenGL", nullptr, nullptr); 140 // 将GLFW创建的窗口与当前OpenGL的上下文关联起来 141 glfwMakeContextCurrent(window); 142 // 如果glew初始化失败则返回,glew负责调用OpenGL相关的函数 143 if (glewInit() != GLEW_OK) { exit(EXIT_FAILURE); } 144 // 交换间隔指示了直到交换缓冲区前需要等待多少帧,通常被理解为垂直同步。 145 // 默认情况下,交换间隔为0,意味着缓冲区交换会立即发生。 146 // 在一些快速的机器上,因为屏幕保持以典型的60 - 75次每秒的速度更新,许多帧会永远看不到,所以这会浪费许多CPU和GPU周期。 147 // 而且,因为缓冲区可能会在屏幕更新的中途被交换,导致画面撕裂。 148 // 因此,应用需要代表性地设置交换间隔为1。 149 glfwSwapInterval(1); 150 151 init(window); 152 153 // 当用户点击关闭窗口按钮时会返回true 154 while (!glfwWindowShouldClose(window)) 155 { 156 display(window, glfwGetTime()); 157 // GLFW默认使用了双缓冲技术。这意味着每个窗口会有两个渲染缓冲区,一个前置缓冲区和一个后置缓冲区。 158 // 前置缓冲区会在屏幕上显示而后置缓冲区是你渲染的目标。 159 // 当整个帧已经渲染完毕时,两个缓冲区需要进行交换,所以后置缓冲区会变成前置缓冲区,反之亦然。 160 glfwSwapBuffers(window); 161 // 处理窗口相关事件 162 glfwPollEvents(); 163 } 164 // 销毁窗口 165 glfwDestroyWindow(window); 166 // 终止运行 167 glfwTerminate(); 168 169 exit(EXIT_SUCCESS); 170 }
顶点着色器源码:
1 #version 430 2 3 layout (location=0) in vec3 position; 4 5 uniform mat4 mv_matrix; 6 uniform mat4 proj_matrix; 7 8 void main(void) 9 { 10 gl_Position = proj_matrix * mv_matrix * vec4(position, 1.0); 11 }
片段着色器源码:
1 #version 430 2 3 out vec4 color; 4 5 void main(void) 6 { 7 color = vec4(1.0, 0.0, 0.0, 1.0); 8 }
工具类源码:
/*****************************************************************//** * \file Utils.h * \brief OpenGL工具类 * * \author 禅院天道 * \date 2024-04-08 *********************************************************************/ #ifndef __UTILS_H__ #define __UTILS_H__ #include <GL/glew.h> #include <GLFW/glfw3.h> #include <SOIL2/soil2.h> #include <string> #include <iostream> #include <fstream> #include <cmath> #include <vector> #include <glm/glm.hpp> #include <glm/gtc/type_ptr.hpp> class Utils { public: Utils(); static void displayComputeShaderLimits(); static bool checkOpenGLError(); static GLuint createShaderProgram(const char* cp); /** * 创建着色器程序. * * \param vertex 顶点着色器源码文件 * \param fragment 片段着色器源码文件 * \return */ static GLuint createShaderProgram(const char* vertex, const char* fragment); /** * 创建着色器程序. * * \param vertex 顶点着色器源码文件 * \param geometry 几何着色器源码文件 * \param fragment 片段着色器源码文件 * \return */ static GLuint createShaderProgram(const char* vertex, const char* geometry, const char* fragment); /** * 创建着色器程序. * * \param vertex 顶点着色器源码文件 * \param tCS 细分控制材质着色器源码文件 * \param tES 细分执行材质着色器源码文件 * \param fragment 片段着色器源码文件 * \return */ static GLuint createShaderProgram(const char* vertex, const char* tCS, const char* tES, const char* fragment); /** * 创建着色器程序. * * \param vertex 顶点着色器源码文件 * \param tCS 细分控制材质着色器源码文件 * \param tES 细分执行材质着色器源码文件 * \param geometry 几何着色器源码文件 * \param fragment 片段着色器源码文件 * \return */ static GLuint createShaderProgram(const char* vertex, const char* tCS, const char* tES, const char* geometry, const char* fragment); static GLuint loadTexture(const char* texImagePath); static GLuint loadCubeMap(const char* mapDir); static float* goldAmbient(); static float* goldDiffuse(); static float* goldSpecular(); static float goldShininess(); static float* silverAmbient(); static float* silverDiffuse(); static float* silverSpecular(); static float silverShininess(); static float* bronzeAmbient(); static float* bronzeDiffuse(); static float* bronzeSpecular(); static float bronzeShininess(); private: /** * 读取着色器源码程序. * * \param filePath 源码路径 * \return 源码字符串 */ static std::string readShaderFile(const char* filePath); /** * 打印shader错误日志. * * \param shader shader */ static void printShaderLog(GLuint shader); /** * 打印着色器程序错误日志. * * \param prog 着色器程序 */ static void printProgramLog(int prog); /** * 预处理shader(获取源码、编译、检查错误). * * \param shaderTYPE shader类型 * \param shaderPath shader源码路径 * \return 成功时返回shader,失败返回0 */ static GLuint prepareShader(int shaderTYPE, const char* shaderPath); /** * 链接着色器程序. * * \param sprogram 着色器程序 * \return 成功时返回shader,失败返回0 */ static int finalizeShaderProgram(GLuint sprogram); }; #endif // !__UTILS_H__ // 启用GLM中一些实验性的拓展 #define GLM_ENABLE_EXPERIMENTAL #include <GL/glew.h> #include <GLFW/glfw3.h> #include <SOIL2/soil2.h> #include <string> #include <iostream> #include <fstream> #include <cmath> #include <glm/glm.hpp> #include <glm/gtc/type_ptr.hpp> // glm::value_ptr #include <glm/gtc/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective #include <glm/gtx/euler_angles.hpp> #include "Utils.h" Utils::Utils() {} void Utils::displayComputeShaderLimits() { } bool Utils::checkOpenGLError() { bool foundError = false; int glErr = glGetError(); while (glErr != GL_NO_ERROR) { std::cout << "glError: " << glErr << std::endl; foundError = true; glErr = glGetError(); } return foundError; } GLuint Utils::createShaderProgram(const char* cp) { return 0; } GLuint Utils::createShaderProgram(const char* vertex, const char* fragment) { GLuint vShader = prepareShader(GL_VERTEX_SHADER, vertex); GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fragment); GLuint vfprogram = glCreateProgram(); glAttachShader(vfprogram, vShader); glAttachShader(vfprogram, fShader); finalizeShaderProgram(vfprogram); // 链接完的shader可以删除 glDeleteShader(vShader); glDeleteShader(fShader); return vfprogram; } GLuint Utils::createShaderProgram(const char* vertex, const char* geometry, const char* fragment) { GLuint vShader = prepareShader(GL_VERTEX_SHADER, vertex); GLuint gShader = prepareShader(GL_GEOMETRY_SHADER, geometry); GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fragment); GLuint vgfprogram = glCreateProgram(); glAttachShader(vgfprogram, vShader); glAttachShader(vgfprogram, gShader); glAttachShader(vgfprogram, fShader); finalizeShaderProgram(vgfprogram); // 链接完的shader可以删除 glDeleteShader(vShader); glDeleteShader(gShader); glDeleteShader(fShader); return vgfprogram; } GLuint Utils::createShaderProgram(const char* vertex, const char* tCS, const char* tES, const char* fragment) { GLuint vShader = prepareShader(GL_VERTEX_SHADER, vertex); GLuint tcShader = prepareShader(GL_TESS_CONTROL_SHADER, tCS); GLuint teShader = prepareShader(GL_TESS_EVALUATION_SHADER, tES); GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fragment); GLuint vtfprogram = glCreateProgram(); glAttachShader(vtfprogram, vShader); glAttachShader(vtfprogram, tcShader); glAttachShader(vtfprogram, teShader); glAttachShader(vtfprogram, fShader); finalizeShaderProgram(vtfprogram); // 链接完的shader可以删除 glDeleteShader(vShader); glDeleteShader(tcShader); glDeleteShader(teShader); glDeleteShader(fShader); return vtfprogram; } GLuint Utils::createShaderProgram(const char* vertex, const char* tCS, const char* tES, const char* geometry, const char* fragment) { GLuint vShader = prepareShader(GL_VERTEX_SHADER, vertex); GLuint tcShader = prepareShader(GL_TESS_CONTROL_SHADER, tCS); GLuint teShader = prepareShader(GL_TESS_EVALUATION_SHADER, tES); GLuint gShader = prepareShader(GL_GEOMETRY_SHADER, geometry); GLuint fShader = prepareShader(GL_FRAGMENT_SHADER, fragment); GLuint vtgfprogram = glCreateProgram(); glAttachShader(vtgfprogram, vShader); glAttachShader(vtgfprogram, tcShader); glAttachShader(vtgfprogram, teShader); glAttachShader(vtgfprogram, gShader); glAttachShader(vtgfprogram, fShader); finalizeShaderProgram(vtgfprogram); // 链接完的shader可以删除 glDeleteShader(vShader); glDeleteShader(tcShader); glDeleteShader(teShader); glDeleteShader(gShader); glDeleteShader(fShader); return vtgfprogram; } GLuint Utils::loadTexture(const char* texImagePath) { return 0; } GLuint Utils::loadCubeMap(const char* mapDir) { return 0; } float* Utils::goldAmbient() { return nullptr; } float* Utils::goldDiffuse() { return nullptr; } float* Utils::goldSpecular() { return nullptr; } float Utils::goldShininess() { return 0.0f; } float* Utils::silverAmbient() { return nullptr; } float* Utils::silverDiffuse() { return nullptr; } float* Utils::silverSpecular() { return nullptr; } float Utils::silverShininess() { return 0.0f; } float* Utils::bronzeAmbient() { return nullptr; } float* Utils::bronzeDiffuse() { return nullptr; } float* Utils::bronzeSpecular() { return nullptr; } float Utils::bronzeShininess() { return 0.0f; } std::string Utils::readShaderFile(const char* filePath) { std::string content; std::ifstream fileStream(filePath, std::ios::in); if (!fileStream.is_open()) { throw("Unable to open file"); } std::string line = ""; while (!fileStream.eof()) { std::getline(fileStream, line); content.append(line + "\n"); } fileStream.close(); return content; } void Utils::printShaderLog(GLuint shader) { int len = 0; int chWrittn = 0; char* log; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); if (len > 0) { log = (char*)malloc(len); glGetShaderInfoLog(shader, len, &chWrittn, log); std::cout << "Shader Info Log: " << log << std::endl; free(log); } } void Utils::printProgramLog(int prog) { int len = 0; int chWrittn = 0; char* log; glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len); if (len > 0) { log = (char*)malloc(len); glGetProgramInfoLog(prog, len, &chWrittn, log); std::cout << "Program Info Log: " << log << std::endl; free(log); } } GLuint Utils::prepareShader(int shaderTYPE, const char* shaderPath) { GLint shaderCompiled; // 读取着色器源码 std::string shaderStr = readShaderFile(shaderPath); const char* shaderSrc = shaderStr.c_str(); // 根据类型创建着色器 GLuint shaderRef = glCreateShader(shaderTYPE); // 判断是否创建成功 if (shaderRef == 0 || shaderRef == GL_INVALID_ENUM) { printf("Error: Could not create shader \"%s\" of type:%d\n", shaderPath, shaderTYPE); return 0; } // 设置源码 glShaderSource(shaderRef, 1, &shaderSrc, NULL); // 编译着色器 glCompileShader(shaderRef); // 错误检查 checkOpenGLError(); // 获取着色器编译状态(是否成功) glGetShaderiv(shaderRef, GL_COMPILE_STATUS, &shaderCompiled); if (shaderCompiled != GL_TRUE) { if (shaderTYPE == GL_VERTEX_SHADER) std::cout << "Vertex "; if (shaderTYPE == GL_TESS_CONTROL_SHADER) std::cout << "Tess Control "; if (shaderTYPE == GL_TESS_EVALUATION_SHADER) std::cout << "Tess Eval "; if (shaderTYPE == GL_GEOMETRY_SHADER) std::cout << "Geometry "; if (shaderTYPE == GL_FRAGMENT_SHADER) std::cout << "Fragment "; if (shaderTYPE == GL_COMPUTE_SHADER) std::cout << "Compute "; std::cout << "shader compilation error for shader: '" << shaderPath << "'." << std::endl; printShaderLog(shaderRef); GLint log_size = 0; glGetShaderiv(shaderRef, GL_INFO_LOG_LENGTH, &log_size); printf("Shader log length: %d\n", log_size); GLchar* info_log = (GLchar*)malloc(sizeof(GLchar) * log_size); glGetShaderInfoLog(shaderRef, log_size, &log_size, info_log); printf("Compilation Log: '%s'\n", info_log); printf("First 5 chars: %d %d %d %d %d\n", info_log[0], info_log[1], info_log[2], info_log[3], info_log[4]); free(info_log); glDeleteShader(shaderRef); return 0; } return shaderRef; } int Utils::finalizeShaderProgram(GLuint sprogram) { GLint linked; glLinkProgram(sprogram); checkOpenGLError(); glGetProgramiv(sprogram, GL_LINK_STATUS, &linked); if (linked != 1) { std::cout << "linking failed" << std::endl; printProgramLog(sprogram); } return sprogram; }