1、创建相同的两个三角形,但对它们的数据使用不同的VAO和VBO
#include <glad/glad.h> // 包含 GLAD 库的头文件,用于加载 OpenGL 函数指针
#include <GLFW/glfw3.h> // 包含 GLFW 库的头文件,用于创建窗口和处理输入
#include <iostream> // 包含标准输入输出流库
// 定义 framebuffer_size_callback 函数原型
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
// 定义 processInput 函数原型
void processInput(GLFWwindow* window);
// 设置窗口的宽度和高度
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
// 定义顶点着色器的 GLSL 源码
const char* vertexShaderSource = "#version 400 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\n0";
// 定义片元着色器的 GLSL 源码
const char* fragmentShaderSource = "#version 400 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
int main()
{
// 初始化 GLFW
glfwInit();
// 设置 GLFW 创建 OpenGL 4.0 核心配置文件的提示
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 苹果系统需要设置为向前兼容模式
#endif
// 创建 GLFW 窗口
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// 加载 OpenGL 函数指针
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// 创建和编译着色器程序
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
unsigned int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// 设置顶点数据和缓冲区
float firstTriangle[] = {
-0.9f, -0.5f, 0.0f, // 左下
0.0f, -0.5f, 0.0f, // 右下
-0.45f, 0.5f, 0.0f, // 上
};
float secondTriangle[] = {
0.0f, -0.5f, 0.0f, // 左
0.9f, -0.5f, 0.0f, // 右
0.45f, 0.5f, 0.0f // 上
};
unsigned int VBOs[2], VAOs[2];
glGenVertexArrays(2, VAOs);
glGenBuffers(2, VBOs);
// 配置第一个三角形的 VAO 和 VBO
glBindVertexArray(VAOs[0]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(firstTriangle), firstTriangle, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 配置第二个三角形的 VAO 和 VBO
glBindVertexArray(VAOs[1]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(secondTriangle), secondTriangle, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);
// 渲染循环
while (!glfwWindowShouldClose(window))
{
processInput(window);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shaderProgram);
// 绘制第一个三角形
glBindVertexArray(VAOs[0]);
glDrawArrays(GL_TRIANGLES, 0, 3);
// 绘制第二个三角形
glBindVertexArray(VAOs[1]);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(window);
glfwPollEvents();
}
// 清理资源
glDeleteVertexArrays(2, VAOs);
glDeleteBuffers(2, VBOs);
glDeleteProgram(shaderProgram);
// 终止 GLFW
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
// 处理输入的函数
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// 当窗口大小改变时调用的回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
这段代码展示了如何在 OpenGL 中创建和管理多个三角形,每个三角形使用独立的 VAO 和 VBO。
代码解释:
初始化和配置GLFW:设置 OpenGL 的版本和配置文件,创建一个窗口,并注册一个视口改变的回调函数。
加载OpenGL函数指针:使用 GLAD 库加载所有 OpenGL 函数的指针。
创建和编译着色器程序:创建顶点着色器和片元着色器,编译它们,并将它们附加到着色器程序,然后链接着色器程序。
设置顶点数据和缓冲区:定义两个三角形的顶点数据,创建两个 VAO 和两个 VBO,将顶点数据复制到 VBO 中,并配置顶点属性指针。
渲染循环:在渲染循环中,处理输入,清除颜色缓冲区,使用着色器程序,绘制两个三角形,交换前后缓冲区,并处理事件。
清理资源:在程序结束时删除分配的 VAO、VBO 和着色器程序。
处理输入:定义
processInput
函数来处理用户输入,例如按下ESC
键关闭窗口。视口改变的回调函数:定义
framebuffer_size_callback
函数来处理窗口大小改变事件,设置视口以匹配新窗口尺寸。
输出结果:
2、 创建两个着色器程序,第二个程序使用一个不同的片段着色器,输出橙色;再次绘制这两个三角形,让其中一个输出为黄色
解读:
创建两个三角形,每个三角形都有自己的 VAO 和 VBO,并且使用不同的片元着色器为它们着色。第一个三角形被渲染成橙色,第二个三角形被渲染成黄色。
#include <glad/glad.h> // 包含 GLAD 库的头文件,用于加载 OpenGL 函数指针
#include <GLFW/glfw3.h> // 包含 GLFW 库的头文件,用于创建窗口和处理输入
#include <iostream> // 包含标准输入输出流库
// 定义 framebuffer_size_callback 函数原型
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
// 定义 processInput 函数原型
void processInput(GLFWwindow* window);
// 设置窗口的宽度和高度
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
// 定义顶点着色器的 GLSL 源码
const char* vertexShaderSource = "#version 400 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\n\0";
// 定义第一个片元着色器的 GLSL 源码,输出颜色为橙色
const char* fragmentShader1Source = "#version 400 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";
// 定义第二个片元着色器的 GLSL 源码,输出颜色为黄色
const char* fragmentShader2Source = "#version 400 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 1.0f, 0.0f, 1.0f);\n"
"}\n\0";
int main()
{
// 初始化 GLFW
glfwInit();
// 设置 GLFW 创建 OpenGL 3.3 核心配置文件的提示
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 苹果系统需要设置为向前兼容模式
#endif
// 创建 GLFW 窗口
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// 加载 OpenGL 函数指针
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// 创建和编译我们的着色器程序
// 我们跳过了编译日志检查,以提高可读性(如果遇到问题,请添加编译检查!参见以前的代码示例)
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
unsigned int fragmentShaderOrange = glCreateShader(GL_FRAGMENT_SHADER); // 第一个片元着色器输出橙色
unsigned int fragmentShaderYellow = glCreateShader(GL_FRAGMENT_SHADER); // 第二个片元着色器输出黄色
unsigned int shaderProgramOrange = glCreateProgram();
unsigned int shaderProgramYellow = glCreateProgram(); // 第二个着色器程序
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
glShaderSource(fragmentShaderOrange, 1, &fragmentShader1Source, NULL);
glCompileShader(fragmentShaderOrange);
glShaderSource(fragmentShaderYellow, 1, &fragmentShader2Source, NULL);
glCompileShader(fragmentShaderYellow);
// 链接第一个程序对象
glAttachShader(shaderProgramOrange, vertexShader);
glAttachShader(shaderProgramOrange, fragmentShaderOrange);
glLinkProgram(shaderProgramOrange);
// 然后链接第二个程序对象,使用不同的片元着色器(但顶点着色器相同)
// 这是完全允许的,因为两个顶点和片元着色器的输入和输出都匹配。
glAttachShader(shaderProgramYellow, vertexShader);
glAttachShader(shaderProgramYellow, fragmentShaderYellow);
glLinkProgram(shaderProgramYellow);
// 设置顶点数据(和缓冲区)并配置顶点属性
// ------------------------------------------------------------------
float firstTriangle[] = {
-0.9f, -0.5f, 0.0f, // 左下
-0.0f, -0.5f, 0.0f, // 右下
-0.45f, 0.5f, 0.0f, // 上
};
float secondTriangle[] = {
0.0f, -0.5f, 0.0f, // 左
0.9f, -0.5f, 0.0f, // 右
0.45f, 0.5f, 0.0f // 上
};
unsigned int VBOs[2], VAOs[2];
glGenVertexArrays(2, VAOs); // 我们也可以同时生成多个 VAO 或缓冲区
glGenBuffers(2, VBOs);
// 第一个三角形设置
// --------------------
glBindVertexArray(VAOs[0]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(firstTriangle), firstTriangle, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 顶点属性保持不变
glEnableVertexAttribArray(0);
// glBindVertexArray(0); // 不需要全部解绑,因为我们直接绑定了不同的 VAO 下面几行
// 第二个三角形设置
// --------------------
glBindVertexArray(VAOs[1]); // 注意我们现在绑定到不同的 VAO
glBindBuffer(GL_ARRAY_BUFFER, VBOs[1]); // 和不同的 VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(secondTriangle), secondTriangle, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); // 因为顶点数据是紧密打包的,我们也可以指定 0 作为顶点属性的步长,让 OpenGL 计算出来
glEnableVertexAttribArray(0);
// glBindVertexArray(0); // 不是必要的,但要注意可能影响 VAO 的调用(如绑定元素缓冲区对象,或启用/禁用顶点属性)
// 注释:取消注释这行代码以绘制线框多边形。
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// 渲染循环
// -----------
while (!glfwWindowShouldClose(window))
{
// 输入
// -----
processInput(window);
// 渲染
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 现在,当我们绘制三角形时,我们首先使用第一个程序中的顶点和橙色片元着色器
glUseProgram(shaderProgramOrange);
// 使用第一个 VAO 的数据绘制第一个三角形
glBindVertexArray(VAOs[0]);
glDrawArrays(GL_TRIANGLES, 0, 3); // 这个调用应该输出一个橙色三角形
// 然后我们绘制第二个三角形,使用第二个 VAO 的数据
// 当我们绘制第二个三角形时,我们想要使用一个不同的着色器程序,所以我们切换到着色器程序,它有我们的黄色片元着色器。
glUseProgram(shaderProgramYellow);
glBindVertexArray(VAOs[1]);
glDrawArrays(GL_TRIANGLES, 0, 3); // 这个调用应该输出一个黄色三角形
// glfw:交换缓冲区并轮询 IO 事件(按键按下/释放,鼠标移动等)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// 可选:一旦它们的目的已经达到,就释放所有资源:
// --------------------------------------------------------------------
glDeleteVertexArrays(2, VAOs);
glDeleteBuffers(2, VBOs);
glDeleteProgram(shaderProgramOrange);
glDeleteProgram(shaderProgramYellow);
// glfw:终止,清除所有先前分配的 GLFW 资源。
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
// 处理所有输入:查询 GLFW 本帧是否有相关按键被按下/释放,并相应地做出反应
// ----------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// glfw:每当窗口大小改变(由操作系统或用户调整大小时)此回调函数执行
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// 确保视口与新窗口尺寸匹配;注意,在视网膜显示器上宽度和高度将明显大于指定值。
glViewport(0, 0, width, height);
}
在渲染循环中,首先激活橙色片元着色器程序,然后绘制第一个三角形,然后激活黄色片元着色器程序,绘制第二个三角形。这样,可以在同一个渲染循环中看到两个颜色不同的三角形。
输出结果:
参考: