OpenGL入门之使用EBO绘制四边形
程序员文章站
2022-04-01 18:42:10
...
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
using namespace std;
const float vertices[] =
{
//第一个三角形
0.5f, 0.5f, 0.0f, // 右上
0.5f, -0.5f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, // 左下
// 第二个三角形
-0.5f, -0.5f, 0.0f, // 左下
0.5f, 0.5f, 0.0f, // 右上
-0.5f, 0.5f, 0.0f // 左上
};
unsigned int indices[] =
{
0, 1, 5, // 第一个三角形
1, 2, 5 // 第二个三角形
};
int screen_width = 1280;
int screen_height = 720;
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);// 使用核心模式(无需向后兼容性)
glfwWindowHint(GLFW_RESIZABLE, false); //不可改变视窗的大小
GLFWwindow *window = glfwCreateWindow(screen_width, screen_height, "Quad", NULL, NULL);
if (window == NULL) {
cout << "Failed to create GLFW window" << endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
cout << "Failed to initialize GLAD" << endl;
return -1;
}
glViewport(0, 0, screen_width, screen_height);
// 生成并绑定 VBO
// 生成并绑定VAO和VBO
GLuint vertex_array_object; // == VAO
glGenVertexArrays(1, &vertex_array_object);
glBindVertexArray(vertex_array_object);
GLuint vertex_buffer_object; // == VBO
glGenBuffers(1, &vertex_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object);
// 将顶点数据绑定至当前默认的缓冲中
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//将顶点数据绑定到当前默认的缓冲上,GL_STATIC_DRAW 表示我们的四边形位置数据不会被改变
//在CoreProfile中做顶点数据传入时,必须使用VAO方式
GLuint element_buffer_object; // == EBO
glGenBuffers(1, &element_buffer_object);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer_object);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);// 设置顶点属性指针
//第一个参数是我们后面会用到的顶点着色器的位置值,3 表示的
//是顶点属性是一个三分量的向量,第三个参数表示的是我们顶点的类型,第四
//个是我们是否希望数据被标准化,就是映射到 0 - 1 之间,第五个参数叫做步长,
//它表示连续顶点属性之间的间隔,表示下组数据在 3 个 float 之后
//最后一个是数据的偏移量,这里我们的位置属性是在数组的开头,因此这里是0
glEnableVertexAttribArray(0);
//默认情况下,出于性能考虑,所有顶点着色器的属性(Attribute)变量都是关闭的,
//意味着数据在着色器端是不可见的,哪怕数据已经上传到GPU,
//由glEnableVertexAttribArray启用指定属性,才可在顶点着色器中访问逐顶点的属性数据。
//glEnableVertexAttribArray的功能,允许顶点着色器读取GPU(服务器端)数据。
//解绑VAO和VBO
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//顶点着色器源码
const char *vertex_shader_source =
"#version 330 core\n" //表示我们使用的是 OpenGL3.3 的核心
"layout (location = 0) in vec3 aPos;\n" // 0是之前设置顶点属性指针的位置值
"void main()\n"
"{\n" " gl_Position = vec4(aPos, 1.0);\n"
"}\n\0";
//Main 函数中的部分就是将我们之前的顶点数据直接输出到 GLSL 已经定义好的一个内建变量 gl_Position
//片元着色器源码
const char *fragment_shader_source =
"#version 330 core\n"
"out vec4 FragColor;\n" //输出的颜色变量
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.8f, 0.5f, 0.1f);\n"//四分量的 RGBA表示
"}\n\0";
//生成并编译着色器
//首先是顶点着色器
int vertex_shader = glCreateShader(GL_VERTEX_SHADER);//glCreateShader创建一个空的着色器对象,并返回一个可以引用的非零值
glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);//替换着色器对象中的源代码,
//把空的着色器,用我们之前定义的vertex_shader_source替换
//void glCompileShader(GLuint shader);shader指定要编译的着色器对象
glCompileShader(vertex_shader);//编译着色器
int success;
char info_log[512];
//void glGetShaderiv(GLuint shader,GLenum pname,GLint *params);
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
//GL_COMPILE_STATUS对于支持着色器编译器的实现,如果着色器上的最后一次编译操作成功,则params返回GL_TRUE,否则返回GL_FALSE。
if (!success)
{
glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n"
<< info_log << std::endl;
}
// 片元着色器
int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
glCompileShader(fragment_shader);// 检查着色器是否成功编译,如果编译失败,打印错误信息
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragment_shader, 512, NULL, info_log);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << info_log << std::endl;
}
//链接着色器到着色器程序
int shader_program = glCreateProgram();//创建一个program
glAttachShader(shader_program, vertex_shader);//将着色器对象附加到program对象
glAttachShader(shader_program, fragment_shader);
glLinkProgram(shader_program);//连接一个program对象。
// 检查着色器是否成功链接,如果链接失败,打印错误信息
glGetProgramiv(shader_program, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(shader_program, 512, NULL, info_log);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" <<
info_log << std::endl;
}
//删除着色器,因为已经用不到了,后面渲染的时候我们只需要用那个
//我们之前链接好的着色器程序就可以了
// 删除着色器
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
//渲染
while (!glfwWindowShouldClose(window))
{
// 清空颜色缓冲
glClearColor(0.0f, 0.34f, 0.57f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 使用着色器程序
glUseProgram(shader_program);
// 绘制三角形
glBindVertexArray(vertex_array_object); // 绑定VAO
//glDrawArrays(GL_TRIANGLES, 0, 6); // 绘制三角形,第一个参数表示要绘制三角形,
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//使用EBO绘制四边形
//第二个参数表示第二个参数表示我们顶点数组的起始索引值,第三个参数表示绘制的顶点个数
glBindVertexArray(0); // 绘制结束后解除绑定
// 交换缓冲并且检查是否有触发事件(比如键盘输入、鼠标移动等)
glfwSwapBuffers(window);//双缓冲
//电脑绘图是一个个像素逐一画的,需要时间,如果单一缓冲,我们可能会看到具体绘画过程
//会造成屏幕闪烁等问题,而我们用户不需要具体看到你绘制的过程,所以为了解决这个问题,
//这里用了双缓冲技术,用两个内存区域来保存数据,分为前缓冲区和后缓冲区,
//前缓冲区用于展示屏幕上的内容,而后缓冲区就用来绘制,然后每一帧开始的时候,
//将两个缓冲区交换,这样后缓冲区又可以画新的内容。
glfwPollEvents();
}
//善后工作, 删除括删除我们之前所创建的 VAO、VBO EBO
//以及调用 GLFW 的函数来清理所有的资源并退出程序
// 删除VAO VBO EBO
glDeleteVertexArrays(1, &vertex_array_object);
glDeleteBuffers(1, &vertex_buffer_object);
glDeleteBuffers(1, &element_buffer_object);
// 清理所有的资源并正确退出程序
glfwTerminate();
return 0;
}