VAO vertex array object 顶点数组对象
VBO vertex buffer object 顶点缓冲对象
EBO element(index) buffer object 索引缓冲对象
所以VBO
的作用就是将PC内存中的顶点数据缓冲到显卡的显存中:
CPU -> bytes(PC Memory) -> Display Card Memory -> GPU
CPU为通用的中央处理器,其特点是通用性,缺点是浮点运算能力,计算机的内存好处是CPU操作方便,相比显卡的显存来说,同时期相对来说显存会比内存速度更快,所以VBO
的好处就是将磁盘到内存后的顶点数据缓冲到显卡,让GPU图形处理器去操作显存中的数据,而GPU对比CPU的好处就是图形图像的处理能力,浮点运算能力更强。
所以我们在渲染之前,让CPU使用VBO
将所有数据发送到GPU,这样为我们的OpenGL处理图形带来效率的提升。
而VAO
则是VBO
和着色器shader
变量之间的链接,描述如何从VBO
中提取数据并将其提供给着色器变量。
(VAO <-> VBO) <-> shader variables
我们要操作VAO
实际上就是使用顶点属性指针,并激活顶点属性数组。
// 定义定点数组
GLfloat vertices1[] = {
// 坐标 // 颜色
0.4f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f, // 右下 红
-0.4f, -0.4f, 0.0f, 0.0f, 1.0f, 0.0f, // 左下 绿
0.0f, 0.4f, 0.0f, 0.0f, 0.0f, 1.0f // 顶部 蓝
};
GLfloat vertices2[] = {
// 坐标 // 颜色
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, // 右上 黄
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, // 右下 品
0.0f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f, // 左下 青
0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f // 左上 黑
};
// 定义索引数据
GLuint indices[] = {
0, 1, 3, // 起始顶点:右上 -> 右下 -> 左上
1, 2, 3 // 第二顶点:右下 -> 左下 -> 左上
};
GLuint VBO[2], VAO[2], EBO; // 我们通过VBO或者VAO来管理显存中的顶点数据 / 索引缓冲对象
glGenBuffers(2, VBO); // 通过glGenBuffers函数和一个ID,这里ID为 '1',来创建VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]); // 将VBO(顶点缓冲)绑定到 GL_ARRAY_BUFFER 目标上
glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
// 将顶点数据缓冲到显存
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices1), vertices1, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW);
glGenVertexArrays(2, VAO); // 通过glGenVertexArrays函数和一个ID,这里ID为 '1',来创建VAO
glBindVertexArray(VAO[0]); // 要想使用VAO,要做的只是使用glBindVertexArray绑定VAO。
// 从绑定之后起,我们应该绑定和配置对应的VBO和属性指针,之后解绑
// VAO供之后使用。当我们打算绘制一个物体的时候,我们只要在绘制物
// 体前简单地把VAO绑定到希望使用的设定上就行了。
glBindVertexArray(VAO[1]);
// 指向顶点属性的指针(操作VAO与Shader)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
// 激活顶点属性数组
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
// 产生`EBO`、绑定`EBO`到`索引数组缓冲`、缓冲索引数据
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
这个hpp文件主要的目的就是读取:vertex shader、fragment shader 与 geometry shader 链接到 shader program
文件:3.3.shader.vs
#version 330 core 是说明我们使用的opengl版本: “opengl 3.3 core profile”。
vec3 实际上就是3个float
分量值(列表)作为列表的vector向量类型,x,y,z
。
vec4 即4个分量的向量类型。
in、out 的意思是方向,in 表示输入到当前GLSL program
中的变量,out 表示通过处理后输出到opengl
的变量。
上面的gl_Position
是我们需要控制VBO
中顶点数据的坐标,我们通过uniform
统一类型将opengl
中通过使用矩阵变换后传入到当前shader program
中,与vec4(aPos, 1.0f)
即当前VBO
中的顶点坐标进行向量乘法,实现平移。
ourColor
是我们将VAO
顶点属性数组中顶点属性指针所指向到VBO
中的顶点的颜色数据获取并传入其中,实现顶点的颜色设置。
layout (location = n)
将VBO
中顶点数据、颜色数据或则贴图数据所在的缩影获取出来,例如上面(location = 0)
是获取顶点数据,(location = 1)
则是顶点颜色数据的索引。
Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。
文件:3.3.shader.fs
out 是我们要输出的颜色(FragColor 就是片段着色器所需要处理颜色的变量)。
in 表示我们opengl
程序向片段着色器传递的变量。
变量名已经说的很清楚了,ourColor
是我们向着色器传递的颜色数据的变量,FragColor
则是输出到片段着色器的颜色数据变量。
这两个文件中的内容属于GLSL
即:OpenGL Shader Language,实际上就是C代码。
渲染管线与OpenGL的着色器:
上面的流程,其实就是渲染管线中数据的流水线过程(其中着色器部分有3个:VS,GS,FS)
调用这2个文件,实际上是使用了Shader
类shader_s.h
文件,代码:
Shader ourShader("src/shader/3.3.shader.vs", "src/shader/3.3.shader.fs");
在主循环中,我们渲染的时候需要使用:
ourShader.use();