浅入浅出游戏引擎笔记(二)
1/索引与缓冲区对象
在上一章的实例中,引擎主循环每执行一次,就要上传顶点数据到GPU来实现渲染.对于简单的图形来说,这是可以接受的.但是实际项目中动辄几W的顶点数量不可能也使用这种方法来处理顶点,不然每一帧都要把所有顶点上传一次,那主循环不用干别的事了.
所以要优化这种情况,有两种方式:
- 减少数据上传量
- 在
GPU
上缓存数据
OpenGL对应的提供了两种方法来实现,分别是顶点索引
和缓冲区对象
.
1.1/顶点数组对象
而且各位不要忘了,OpenGL是一个典型的C/S
架构,也就是说CPU在OpenGL里面扮演的是客户端
的角色,而GPU在OpenGL里面扮演的是服务端
的角色.
这就会产生一个问题:CPU在通知GPU执行一个API之后,必须等待GPU返回一个结果之后才能继续执行下一个API.换句话说,要调用的OpenGL API越多,性能就越差(要等待很多次返回结果)
那么有没有一种方法,把数个命令的结果保存在GPU上面,CPU只需要调用一个API就可以完成执行数个命令的结果呢?
答案是顶点数组对象
1.1.1/何为顶点索引
回顾之前画矩形的时候,我们上传了6个顶点,但是实际上只有4个顶点是真正有用的,剩余两个顶点只是为了凑出两个三角形而上传的.这样不仅增加了顶点的上传数量,也增加了GPU顶点着色器的运行次数
以正方形的顶点坐标为例,顶点坐标是一个数组,那么顶点索引就是这个数组的index
1 | static const glm::vec3 kPositions[6] = |
去掉重复的定点之后,其实只有四个顶点
1 | static const glm::vec3 kPositions[4] = |
但是只有4个顶点的情况下,如何组成两个三角形呢?答案是去重之后的顶点坐标数据不重复,但是可以新建一个数组,存储重复的顶点索引,利用索引调用顶点坐标
1 | static const glm::vec3 indices[6] = |
借助顶点索引数组,我们间接地形成了两个三角形.
1.1.2/何为顶点
在作者之前的文章中,使用了如下的代码来设置了顶点的属性
1 | glVertexAttribPointer(vpos_location, 3, GL_FLOAT, false, sizeof(glm::vec3), kPositions); |
具体绘制多少个顶点,是在如下代码中决定的
1 | glDrawArrays(GL_TRIANGLES, 0, 6*6);//表示从第0个顶点开始画,总共画6个面,每个面6个顶点(两个三角形)。 |
之前我们提到过,顶点着色器是每个顶点都要执行一次.换句话说,glDrawArrays
制定了多少个顶点,就执行多少次.
那么每次执行,就以当前顶点序号为下标,从
glVertexAttribPointer
设置的顶点属性去拿数据,设置到顶点shader的变量vpos_location
中。那么顶点属性的数据,这个数组,长度一定是要等于
glDrawArrays
指定的顶点数的,不然就会取不到数据。按照这个逻辑,我将所有用
glVertexAttribPointer
设置的顶点属性,相同下标的,称之为一个顶点。
也就是说,一个顶点
包含了
- 坐标
- 颜色
- UV坐标
三个数据,只要这三个数据有一个不同,那就不能称之为两个相同的顶点
.也就是说,使用顶点索引需要去重,本质上是去掉三者完全相同的顶点.
因此,对于项目中存放顶点的定义vertex_data.h
需要修改为如下代码.
1 | //顶点 |
至此,我们已经模糊掉了顶点属性的概念,我们面对的是整个顶点对象.