欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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;

}