计算机图形学编程(使用OpenGL和C++) 第4章 管理3D图形数据 笔记
数据处理
想要绘制一个对象,它的顶点数据需要发送给顶点着色器。通常会把顶点数据在C++端放入
一个缓冲区,并把这个缓冲区和着色器中声明的顶点属性相关联。
- 初始化立方体顶点数据:声明vertexPositions数组
我们需要用三角形来构建这个立方体,因此立方体的每一个面都需要由两个三角形构成,总共6×2=12个三角形(见图4.4)。由于每个三角形都由 3 个顶点指定,因此总共有36 个顶点。由于每个顶点具有 3 个值,因此数组中总共有
36×3=108 个值。
float vertexPositions[108] = {
-1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f
};
-
加载方体顶点到VBO中:
在OpenGL中,缓冲区被包含在顶点缓冲对象(Vertex Buffer Object,VBO)中,VBO在C++/OpenGL 应用程序中被声明和实例化。一个场景可能需要很多VBO,所以我们常常会在init()中生成并填充若干个VBO,以备程序需要时直接使用。OpenGL 中另一个作顶点数组对象(Vertex Array Object,VAO)。OpenGL要求至少创建一个VAO
#define numVAOs 1
#define numVBOs 1
GLuint vao[numVAOs];
GLuint vbo[numVBOs];
glGenVertexArrays(1, vao);//创建 VAO 并返回它们的整数型ID,存进数组vao
glBindVertexArray(vao[0]);//将指定的VAO 标记为“活跃”,这样生成的缓冲区①就会和这个VAO相关联。
glGenBuffers(numVBOs, vbo);//创建 VBO并返回它们的整数型ID,存进数组vbo
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);//第0个将缓冲区标记为“活跃”;
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);//将包含顶点数据的数组复制进活跃缓冲区
- 写顶点着色器
#version 430
layout (location=0) in vec3 position;
每个缓冲区需要有在顶点着色器中声明的相应顶点属性变量。顶点属性通常是着色器中首先声明的变量。
- 关键字in意思是“输入”(input),表示这个顶点属性将会从缓冲区中接收数值。
- vec3的意思是着色器的每次调用会抓到3个浮点类型数值(分别表示x轴、y轴、z轴坐标,它们组成一个顶点数据)。
- 变量的名字是position。
- layout (location=0)称为“layout 修饰符”,也就是我们把顶点属性和特定缓冲区关联起来的方法。这个顶点属性的识别号是0。
- 将缓冲区中的值发送到着色器中的顶点属性
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);// 标记第 0 个缓冲区为“活跃”
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); 将第 0 个属性关联到缓冲区
glEnableVertexAttribArray(0); // 启用第 0 个顶点属性
当glDrawArrays()执行时,缓冲区中的数据开始流动,从缓冲区的开头开始,按顺序流过顶点着色器。像第 2 章中介绍的一样,顶点着色器对每个顶点执行一次。
MVP Matrix
MVP矩阵顾名思义,它是由M,V,P三个矩阵组够成的矩阵,其中M为模型矩阵,V为视图矩阵,P为投影矩阵。M矩阵用来将物体顶点在模型空间下的坐标转换为在世界空间下的坐标,V矩阵用来将物体顶点在世界空间下的坐标转换为视图空间下的坐标。P矩阵用来将物体顶点在视图空间的坐标转换为裁剪空间的坐标。M矩阵包括平移矩阵、旋转矩阵、缩放矩阵。V矩阵包括旋转矩阵,平移矩阵。P矩阵则包括正交投影矩阵和透视投影矩阵。
世界空间
以世界原点为原点建立的坐标系。
模型空间
模型空间是以模型某一点为原点建立坐标系而形成的空间
如图左边球,以球心为原点建立坐标系
由于世界空间和模型空间有两套坐标系,我们需要进行坐标系变换将物体放在世界坐标系下面:
模型矩阵描述的是 3D Point 的仿射变换,包含 Scale(缩放)、Rotate(旋转)、Translate(平移)。
可以按照下面的方式表示:
M = S c a l e × R o t a t e × T r a n s l a t e M = Scale \times Rotate \times Translate M=Scale×Rotate×Translate
最后进行 Translation 是为了保证前面的操作参考坐标轴不会变化。
OpenGL 是左乘的,因此编程时计算模型矩阵需要按照 Translate、Rotate、Scale 的顺序进行。
写的具体一点如下: [ x ^ y ^ z ^ 0 ] = [ a b c t x d e f t y g h i t z 0 0 0 1 ] × [ x y z 1 ] \begin{bmatrix} \hat{x}\\ \hat{y}\\ \hat{z}\\ 0\\ \end{bmatrix}= \begin{bmatrix} a & b & c & t_x\\ d & e & f & t_y\\ g & h & i & t_z\\ 0 & 0 & 0 & 1 \end{bmatrix}\times \begin{bmatrix} x \\ y \\ z \\ 1 \\ \end{bmatrix} x^y^