OpenGL之绘制三角形使用顶点颜色
● VBO、VAO 与 EBO 之间的联系与区别: ⭐️ ⭐️
① 顶点缓冲对象 VBO 是在显卡存储空间中开辟出的一块内存缓存区,用于存储顶点的各类属性信息,如顶点坐标、顶点法向量、顶点颜色数据等。在渲染时,可以直接从 VBO 中取出顶点的各类属性数据,由于 VBO 在显存而不是在内存中,不需要从CPU传输数据,所以处理效率更高。
所以可以理解为 VBO 就是显存中的一个存储区域,可以保持大量的顶点属性信息。并且可以开辟很多个 VBO ,每个 VBO 在 OpenGL 中有它的唯一标识 ID ,这个 ID 对应着具体的 VBO 的显存地址,通过这个 ID 可以对特定的 VBO 内的数据进行存取操作。
② VAO 是一个保存了所有顶点数据属性的状态结合,它存储了顶点数据的格式以及顶点数据所需的 VBO 对象的引用。
因为 VBO 保存了一个模型的顶点属性信息,每次绘制模型之前需要绑定顶点的所有信息。当数据量很大时,重复这样的动作变得非常麻烦。VAO 可以把这些所有的配置都存储在一个对象中,每次绘制模型时,只需要绑定这个 VAO 对象就可以了。
另外,VAO 本身并没有存储顶点的相关属性数据,这些信息是存储在 VBO 中的,VAO 相当于是对很多个 VBO 的引用,把一些 VBO 组合在一起作为一个对象统一管理。
③ 索引缓冲对象 EBO 相当于 OpenGL 中的顶点数组的概念,是为了解决同一个顶点多次重复调用的问题,可以减少内存空间浪费,提高执行效率。当需要使用重复的顶点时,通过顶点的位置索引来调用顶点,而不是对重复的顶点信息重复记录,重复调用。
EBO 中存储的内容就是顶点位置的索引 indices,EBO 跟 VBO 类似,也是在显存中的一块内存缓冲器,只不过 EBO 保存的是顶点的索引。
1.Shader.h
1 #pragma once 2 3 #include<string> 4 #include<fstream> 5 #include<sstream> 6 #include<iostream> 7 using namespace std; 8 9 // 我们自己的着色器 10 class Shader 11 { 12 private: 13 GLuint vertex, fragment; // 顶点着色器 和 片元着色器 14 public: 15 GLuint Program; // 着色器程序的ID 16 17 // Constructor(着色器构造函数) 18 Shader(const GLchar* vertexPath, const GLchar* fragmentPath) 19 { 20 // 文件读取系列的变量定义 21 string vertexCode; 22 string fragmentCode; 23 ifstream vShaderFile; 24 ifstream fShaderFile; 25 26 // 异常机制处理:保证ifstream对象可以抛出异常: 27 vShaderFile.exceptions(ifstream::badbit); 28 fShaderFile.exceptions(ifstream::badbit); 29 30 try 31 { 32 // 打开文件 33 vShaderFile.open(vertexPath); 34 fShaderFile.open(fragmentPath); 35 stringstream vShaderStream, fShaderStream; 36 37 // 读取文件的缓冲内容到数据流中 38 vShaderStream << vShaderFile.rdbuf(); 39 fShaderStream << fShaderFile.rdbuf(); 40 41 // 关闭文件处理器 42 vShaderFile.close(); 43 fShaderFile.close(); 44 45 // 转换数据流到string 46 vertexCode = vShaderStream.str(); 47 fragmentCode = fShaderStream.str(); 48 49 } 50 catch (ifstream::failure e) { // 发生异常时输出 51 cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ" << endl; 52 } 53 54 /* 将 string 类型的字符串转化为 char数组 类型 */ 55 const GLchar* vShaderCode = vertexCode.c_str(); 56 const GLchar* fShaderCode = fragmentCode.c_str(); 57 58 /* 顶点着色器 */ 59 vertex = glCreateShader(GL_VERTEX_SHADER); // 创建顶点着色器对象 60 glShaderSource(vertex, 1, &vShaderCode, NULL); // 将顶点着色器的内容传进来 61 glCompileShader(vertex); // 编译顶点着色器 62 GLint flag; // 用于判断编译是否成功 63 GLchar infoLog[512]; 64 glGetShaderiv(vertex, GL_COMPILE_STATUS, &flag); // 获取编译状态 65 if (!flag) 66 { 67 glGetShaderInfoLog(vertex, 512, NULL, infoLog); 68 cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << endl; 69 } 70 71 /* 片元着色器 */ 72 fragment = glCreateShader(GL_FRAGMENT_SHADER); // 创建片元着色器对象 73 glShaderSource(fragment, 1, &fShaderCode, NULL); // 将片元着色器的内容传进来 74 glCompileShader(fragment); // 编译顶点着色器 75 glGetShaderiv(fragment, GL_COMPILE_STATUS, &flag); // 获取编译状态 76 if (!flag) 77 { 78 glGetShaderInfoLog(fragment, 512, NULL, infoLog); 79 cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << endl; 80 } 81 82 /* 着色器程序 */ 83 this->Program = glCreateProgram(); 84 glAttachShader(this->Program, vertex); 85 glAttachShader(this->Program, fragment); 86 glLinkProgram(this->Program); 87 if (!flag) 88 { 89 glGetProgramInfoLog(this->Program, 512, NULL, infoLog); 90 cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << endl; 91 } 92 // 删除着色器,它们已经链接到我们的程序中了,已经不再需要了 93 glDeleteShader(vertex); 94 glDeleteShader(fragment); 95 } 96 97 // Deconstructor(析构函数) 98 ~Shader() 99 { 100 glDetachShader(this->Program, vertex); 101 glDetachShader(this->Program, fragment); 102 glDeleteShader(vertex); 103 glDeleteShader(fragment); 104 glDeleteProgram(this->Program); 105 } 106 107 void Use() 108 { 109 glUseProgram(this->Program); 110 } 111 };
2.shader_f.txt
1 // 文件名为 “shader_f.txt” 2 #version 330 core // 3.30版本 3 in vec3 ourColor; // 输入(3维)颜色向量 4 out vec4 FragColor; // 输出到四个浮点数构成的一个(4维)向量 FragColor 5 void main() 6 { 7 FragColor = vec4(ourColor, 1.0f); // 核心函数(颜色信息赋值) 8 }
3.shader_v.txt
1 // 文件名为 “shader_v.txt” 2 #version 330 core // 3.30版本 3 layout(location = 0) in vec3 position; // 位置变量的属性位置值为0 4 layout(location = 1) in vec3 color; // 颜色变量的属性位置值为1 5 out vec3 ourColor; // 颜色输出 6 void main() 7 { 8 gl_Position = vec4(position, 1.0f); // 核心函数(位置信息赋值) 9 ourColor = color; 10 }
4.DrawTriangleUseVColor.cpp
1 /* 引入相应的库 */ 2 #include <iostream> 3 using namespace std; 4 5 #define GLEW_STATIC 6 7 #include <GL/glew.h> 8 #include <GLFW/glfw3.h> 9 #include "Shader.h" 10 11 /* 编写各顶点位置与颜色 */ 12 GLfloat vertices_1[] = 13 { 14 // position // color 15 0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 上顶点(红色) 16 -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 左顶点(绿色) 17 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f // 右顶点(蓝色) 18 }; 19 20 const GLint WIDTH = 800, HEIGHT = 600; // 窗口的长和宽 21 22 int main() 23 { 24 /* 初始化 */ 25 glfwInit(); 26 GLFWwindow* window_1 = glfwCreateWindow(WIDTH, HEIGHT, "A Beautiful Triangle", nullptr, nullptr); 27 int screenWidth_1, screenHeight_1; 28 glfwGetFramebufferSize(window_1, &screenWidth_1, &screenHeight_1); 29 glfwMakeContextCurrent(window_1); 30 glewInit(); 31 32 /* 将我们自己设置的着色器文本传进来 */ 33 Shader ourShader = Shader("shader_v.txt", "shader_f.txt"); // 相对路径 34 35 /* 设置顶点缓冲对象(VBO) + 设置顶点数组对象(VAO) */ 36 GLuint VAO, VBO; 37 glGenVertexArrays(1, &VAO); 38 glGenBuffers (1, &VBO); 39 glBindVertexArray(VAO); 40 glBindBuffer (GL_ARRAY_BUFFER, VBO); 41 glBufferData (GL_ARRAY_BUFFER, sizeof(vertices_1), vertices_1, GL_STATIC_DRAW); 42 43 /* 设置链接顶点属性 */ 44 glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0); 45 glEnableVertexAttribArray(0); // 通道 0 打开 46 glVertexAttribPointer (1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); 47 glEnableVertexAttribArray(1); // 通道 1 打开 48 49 // draw loop 画图循环 50 while (!glfwWindowShouldClose(window_1)) 51 { 52 glViewport (0, 0, screenWidth_1, screenHeight_1); 53 glfwPollEvents(); 54 glClearColor (0.0f, 0.0f, 0.0f, 1.0f); 55 glClear (GL_COLOR_BUFFER_BIT); 56 57 /* 第九步:绘制三角形 */ 58 ourShader.Use(); // 图形渲染 59 glBindVertexArray(VAO); // 绑定 VAO 60 glDrawArrays(GL_TRIANGLES, 0, 3); // 画三角形 从第0个顶点开始 一共画3次 61 glBindVertexArray(0); // 解绑定 62 63 glfwSwapBuffers(window_1); 64 } 65 66 glDeleteVertexArrays(1, &VAO); // 释放资源 67 glDeleteBuffers(1, &VBO); 68 glfwTerminate(); // 结束 69 return 0; 70 }