ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

OpenGL 渲染一个三角形

2021-05-12 12:02:01  阅读:256  来源: 互联网

标签:VAO 渲染 绑定 VBO OpenGL 三角形 GL 解绑


本篇是通过OpenGL库,版本为3.3,渲染出一个三角形,效果如下:
img

参考链接:
https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/


渲染流程如下:(GLFW配置正常写,可参考上篇博文:OpenGL 渲染一个窗口

  • 创建顶点着色器程序并编译:glCreateShader(GL_VERTEX_SHADER)
  • 创建片段着色器程序并编译:glCreateShader(GL_FRAGMENT_SHADER)
  • 定义并编译链接程序(将多个着色器合并之后并最终链接完成的版本):glCreateProgram()
  • 定义三角形的三维结构坐标(我们渲染是三角形,所以z轴坐标为0)
  • 定义并绑定与解绑VBO和VAO: 注意VBO与VAO的绑定与解绑顺序(绑定: 先VAO 再VBO;解绑:先VBO 再VAO))
  • 在渲染循环中绘制图形

至于为什么要绑定与解绑(一家之言):这里的绑定与解绑不是对VBO或VAO怎么样,而是把VBO绑定到OpenGL的特定传输通道(不一定是传输通道,可以理解成一种数据结构,如:GL_ARRAY_BUFFER)上。

while循环每循环一次就需要绑定一次(传输一次数据),绑定好理解,要绘制图形,肯定要传数据过去,就绑定,至于解绑,首先不一定要写,测试过将解绑语句注释掉依旧可以运行,此外当你再次绑定时,之前绑定的就被覆盖了,相当于解绑。

所以不要过分纠结绑定与解绑,但一定要注意绑定顺序,先VAO再VBO再EBO

如何绑定:

  • 使用glBindVertexArray绑定VAO,从绑定之后起,我们应该绑定和配置对应的VBO和属性指针,之后解绑VAO供之后使用
  • 绑定VAO之后,当目标是 GL_ARRAY_BUFFER 或者 GL_ELEMENT_ARRAY_BUFFER 的时候,VAO会储存glBindBuffer的函数调用,也就会自动存储VBO和EBO
// 1. 绑定顶点数组对象
glBindVertexArray(VAO);
// 2. 把我们的顶点数组复制到一个顶点缓冲中,供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 复制我们的索引数组到一个索引缓冲中,供OpenGL使用
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 4. 设定顶点属性指针
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);

关于绘图:

// 不使用EBO
/* 
* glDrawArrays(GL_TRIANGLES, 0, 3):
* 第一个参数是我们打算绘制的OpenGL图元的类型
* 第二个参数指定了顶点数组的起始索引,我们这里填0
* 第三个参数指定我们打算绘制多少个顶点
*/
glDrawArrays(GL_TRIANGLES, 0, 3);

// 使用EBO
/*
* glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0):
* 第一个参数指定了我们绘制的模式
* 第二个参数是我们打算绘制顶点的个数
* 第三个参数是索引的类型
* 最后一个参数里我们可以指定EBO中的偏移量
*/
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

源码:

#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>


// 定义回调函数
// --------------
// 窗口改变回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
// 窗口按键输入
void processInput(GLFWwindow* window);

// 其他相关变量
// --------------
// 宽高
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// 顶点着色器
const char* vertexShaderSource = "#version 330 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"
"}\0";

// 片段着色器
const char* fragmentShaderSource = "#version 330 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()
{
	// 1.初始化窗口和设置属性
	// ----------------------
	// 1.1 初始化和配置 glfw
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	// 1.2 创建glfw窗口
	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Hello world", NULL, NULL);
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);

	// 1.3 使用glad加载OpenGL函数指针
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to inittalize GLAD" << std::endl;
		return -1;
	}

	// 1.4 确定视口(Viewport)的大小
	glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

	// 2.设置两个必须的着色器:顶点和片段着色器
	// ----------------------------------------
	// 2.1 设置顶点着色器
	unsigned int vertexShader; 
	vertexShader = glCreateShader(GL_VERTEX_SHADER);            // 把需要创建的着色器类型以参数形式提供给glCreateShader
	glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); // 把这个着色器源码附加到着色器对象上
	glCompileShader(vertexShader);                              // 编译源码

	// 2.2 检测顶点着色器编译结果
	int  success;
	char infoLog[512];
	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
	}

	// 2.3 片段着色器
	unsigned int fragmentShader;
	fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);        // 使用GL_FRAGMENT_SHADER常量作为着色器类型
	glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
	glCompileShader(fragmentShader);

	glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); // 检测片段着色器编译结果
	if (!success)
	{
		glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
	}
	// 3.定义并编译链接程序
	// --------------------
	// 3.1 着色器程序:多个着色器合并之后并最终链接完成的版本,称链接
	unsigned int shaderProgram;
	shaderProgram = glCreateProgram();
	glAttachShader(shaderProgram, vertexShader);
	glAttachShader(shaderProgram, fragmentShader);
	glLinkProgram(shaderProgram);

	glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);  // 检测链接结果
	if (!success) {
		glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
		std::cout << "ERROR::LINK_FAILED\n" << infoLog << std::endl;
	}

	// 3.2 着色器对象链接到程序对象以后,就不再需要它们,可以删除着色器对象
	glDeleteShader(vertexShader); 
	glDeleteShader(fragmentShader);
	
	// 4.定义物体三维结构坐标
	// ---------------------- 
	// 定义三角形3D坐标
	float vertices[] = {
	-0.5f, -0.5f, 0.0f,
	 0.5f, -0.5f, 0.0f,
	 0.0f,  0.5f, 0.0f
	};

	// 5.定义并绑定与解绑VBO和VAO: 注意VBO与VAO的绑定与解绑顺序
	// --------------------------------------------------
	// 5.1 设置顶点缓冲对象(Vertex Buffer Objects, VBO)管理内存(坐标点数据)与 VAO
	unsigned int VBO, VAO;
	glGenVertexArrays(1, &VAO);  // 使用glGenVertexArrays函数和一个缓冲ID生成一个VAO对象
	glGenBuffers(1, &VBO);       // 使用glGenBuffers函数和一个缓冲ID生成一个VBO对象

	// 5.2 绑定: 先VAO 再VBO
	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);  // 使用glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上
	// 调用的任何(在GL_ARRAY_BUFFER目标上的)缓冲调用都会用来配置当前绑定的缓冲(VBO)
	// 
	// 5.3 传入数据:调用glBufferData函数,它会把之前定义的顶点数据复制到缓冲的内存中
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	// 5.4 设置顶点属性指针:在渲染前指定OpenGL该如何解释顶点数据
	// 使用glVertexAttribPointer函数告诉OpenGL该如何解析顶点数据(应用到逐个顶点属性上)
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	// 使用glEnableVertexAttribArray,以顶点属性位置值作为参数,启用顶点属性,顶点属性默认是禁用的
	glEnableVertexAttribArray(0);

	// 5.5 解绑:先VBO 再VAO
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);

	// 这是一段很重要的话,阐述VAO的作用:
	// ----------------------------------
	/*
	从绑定之后起,我们应该绑定和配置对应的VBO和属性指针,之后解绑VAO供之后使用。
	当我们打算绘制一个物体的时候,我们只要在绘制物体前简单地把VAO绑定到希望使用的设定上就行了。
	这段代码应该看起来像这样:
		// 1. 绑定VAO
		glBindVertexArray(VAO);
		// 2. 把顶点数组复制到缓冲中供OpenGL使用
		glBindBuffer(GL_ARRAY_BUFFER, VBO);
		glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
		// 3. 设置顶点属性指针
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
		glEnableVertexAttribArray(0);
		// 4. 绘制物体
		glUseProgram(shaderProgram);
		glBindVertexArray(VAO);
		someOpenGLFunctionThatDrawsOurTriangle();

	注:前面做的一切都是等待这一刻,一个储存了我们顶点属性配置和应使用的VBO的顶点数组对象。
	一般当你打算绘制多个物体时,你首先要生成/配置所有的VAO(和必须的VBO及属性指针),然后储存它们供后面使用。
	当我们打算绘制物体的时候就拿出相应的VAO,绑定它,绘制完物体后,再解绑VAO。
	如果乜有VAO,就需要每绘制一个物体时,就需要重复下面这个过程:
		// 0. 复制顶点数组到缓冲中供OpenGL使用
		glBindBuffer(GL_ARRAY_BUFFER, VBO);
		glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
		// 1. 设置顶点属性指针
		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
		glEnableVertexAttribArray(0);
		// 2. 当我们渲染一个物体时要使用着色器程序
		glUseProgram(shaderProgram);
		// 3. 绘制物体
		someOpenGLFunctionThatDrawsOurTriangle();
	*/

	// 6.开启渲染循环(Render Loop)
	while (!glfwWindowShouldClose(window))
	{
		// 6.1 输入控制:
		processInput(window);

		// 6.2 渲染指令
		// 修改背景颜色
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);
		// 绘图
		glUseProgram(shaderProgram);
		glBindVertexArray(VAO);
		glDrawArrays(GL_TRIANGLES, 0, 3);
		
		// 6.3 检查调用事件,并交换缓冲
		glfwPollEvents();
		glfwSwapBuffers(window);
	}

	// 7.回收资源
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	glDeleteProgram(shaderProgram);

	glfwTerminate();
	return 0;
}

// 窗口改变回调函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}
// 窗口按键输入
void processInput(GLFWwindow* window)
{
	// 按下esc按键,退出程序
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);
}

标签:VAO,渲染,绑定,VBO,OpenGL,三角形,GL,解绑
来源: https://blog.csdn.net/y_dd6011/article/details/116698744

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有