6.1顶点索引


文档摘要

## 6.1 顶点索引 回顾 这一章的实例,绘制一个正方形,需要上传6个顶点数据到GPU。 但是我们都知道,实际上只有4个顶点数据。只是为了凑成2个三角形,将这4个顶点分为了 3 3 两份。 重复的顶点不仅增加了上传到GPU的数据量,也增加了GPU顶点着色器的执行次数(每个顶点执行一次)。 OpenGL提供了新的机制 - ,来进行优化。 何为顶点索引 从实例代码中也可以看到,顶点坐标数据是一个数组,那么顶点索引就是这个数组的index。 以正方形的顶点坐标数据为例: 去掉重复,实际上只有4个顶点: 这4个顶点,要怎么组成2个三角形呢? 去重后顶点坐标数据不可以重复,但是可以新建一个数组,存储重复的顶点索引。 借助顶点索引数组,间接地组成了2个三角形。 何为顶点?

6.1 顶点索引

回顾3.2 画个正方形 这一章的实例,绘制一个正方形,需要上传6个顶点数据到GPU。

但是我们都知道,实际上只有4个顶点数据。只是为了凑成2个三角形,将这4个顶点分为了 3 3 两份。

重复的顶点不仅增加了上传到GPU的数据量,也增加了GPU顶点着色器的执行次数(每个顶点执行一次)。

OpenGL提供了新的机制 - 顶点索引,来进行优化。

1. 何为顶点索引

从实例代码中也可以看到,顶点坐标数据是一个数组,那么顶点索引就是这个数组的index。

以正方形的顶点坐标数据为例:

static const glm::vec3 kPositions[6] = { //第一个三角形 { -1.0f, -1.0f, 0.0f},//左下 { 1.0f, -1.0f, 0.0f},//右下 { 1.0f, 1.0f, 0.0f},//右上 //第二个三角形 { 1.0f, 1.0f, 0.0f},//右上 { -1.0f, 1.0f, 0.0f},//左上 { -1.0f, -1.0f, 0.0f}//左下 };

去掉重复,实际上只有4个顶点:

static const glm::vec3 kPositions[4] = { { -1.0f, -1.0f, 0.0f},//左下 { 1.0f, -1.0f, 0.0f},//右下 { 1.0f, 1.0f, 0.0f},//右上 { -1.0f, 1.0f, 0.0f},//左上 };

这4个顶点,要怎么组成2个三角形呢?

去重后顶点坐标数据不可以重复,但是可以新建一个数组,存储重复的顶点索引。

static const glm::vec3 indices[6] = { //第一个三角形 0,1,2, //第二个三角形 2,3,1 };

借助顶点索引数组,间接地组成了2个三角形。

2. 何为顶点?

本段落CLion项目文件位于 samples\vertex_index_and_buffer\vertex

上面说了,将顶点坐标去重,然后新建数组存储下标,那么顶点颜色和UV坐标怎么处理呢?

这里我们要明确一个概念 -- 顶点是什么?

在之前的例子,我用glVertexAttribPointer来设置顶点属性,如下面的代码将顶点坐标数组与 顶点shader中的变量vpos_location关联。

glVertexAttribPointer(vpos_location, 3, GL_FLOAT, false, sizeof(glm::vec3), kPositions);

具体绘制多少个顶点,是在glDrawArrays指定的,如下面代码绘制一个立方体,就有6*6=36个顶点。

glDrawArrays(GL_TRIANGLES, 0, 6*6);//表示从第0个顶点开始画,总共画6个面,每个面6个顶点。

4.3 顶点着色器 这小节里说过,顶点着色器是每个顶点执行一次。

其实就是说glDrawArrays指定了多少个顶点,就执行多少次。

那么每次执行,就以当前顶点序号为下标,从glVertexAttribPointer设置的顶点属性去拿数据,设置到顶点shader的变量vpos_location中。

那么顶点属性的数据,这个数组,长度一定是要等于glDrawArrays指定的顶点数的,不然就会取不到数据。

按照这个逻辑,我将所有用glVertexAttribPointer设置的顶点属性,相同下标的,称之为一个顶点。

如上图,虚线框中的为一个顶点。

一般来说,一个顶点,包含了顶点坐标、顶点颜色、UV坐标这三个数据。

三者任意之一不同,那么就是2个不同的顶点。

所以使用顶点索引需要进行去重,是去掉三者完全相同的顶点。

为了更深入理解顶点概念,对vertex_data.h进行修改,引入struct Vertex,并将之前的顶点坐标(kPositions)、顶点颜色(kColors)、UV坐标(kUvs)这三个数据合并为顶点(kVertexs)。

//顶点 struct Vertex { glm::vec3 pos_; glm::vec4 color_; glm::vec2 uv_; }; static const Vertex kVertexs[36] ={ //Front glm::vec3(-1.0f, -1.0f, 1.0f), glm::vec4(1.0f,1.0f,1.0f,1.0f), glm::vec2(0.0f, 0.0f), glm::vec3( 1.0f, -1.0f, 1.0f), glm::vec4(1.0f,1.0f,1.0f,1.0f), glm::vec2(1.0f, 0.0f), glm::vec3( 1.0f, 1.0f, 1.0f), glm::vec4(1.0f,1.0f,1.0f,1.0f), glm::vec2(1.0f, 1.0f), glm::vec3(-1.0f, -1.0f, 1.0f), glm::vec4(1.0f,1.0f,1.0f,1.0f), glm::vec2(0.0f, 0.0f), glm::vec3( 1.0f, 1.0f, 1.0f), glm::vec4(1.0f,1.0f,1.0f,1.0f), glm::vec2(1.0f, 1.0f), glm::vec3(-1.0f, 1.0f, 1.0f), glm::vec4(1.0f,1.0f,1.0f,1.0f), glm::vec2(0.0f, 1.0f), //back glm::vec3(-1.0f, -1.0f, -1.0f), glm::vec4(1.0f,1.0f,1.0f,1.0f), glm::vec2(0.0f, 0.0f), ...... };

然后在将顶点数据与顶点Shader属性进行关联时,需要加上指针偏移,代码如下:

//main.cpp //启用顶点Shader属性(a_pos),指定与顶点坐标数据进行关联 glEnableVertexAttribArray(vpos_location); glVertexAttribPointer(vpos_location, 3, GL_FLOAT, false, sizeof(Vertex), kVertexs); //启用顶点Shader属性(a_color),指定与顶点颜色数据进行关联 glEnableVertexAttribArray(vcol_location); glVertexAttribPointer(vcol_location, 4, GL_FLOAT, false, sizeof(Vertex), ((float*)kVertexs) + 3); //启用顶点Shader属性(a_uv),指定与顶点UV数据进行关联 glEnableVertexAttribArray(a_uv_location); glVertexAttribPointer(a_uv_location, 2, GL_FLOAT, false, sizeof(Vertex), ((float*)kVertexs) + 3 + 4);

代码修改后,模糊掉了顶点坐标、顶点颜色、UV坐标这三个数据,面对的就是顶点这个整体。

3. 使用顶点索引绘制立方体

下面就在上一段落的实例项目基础之上,使用顶点索引绘制立方体。

CLion项目文件位于 samples\vertex_index_and_buffer\indices

3.1 去除重复顶点

上一段落的实例项目,存有36个顶点在kVertexs[36]中,首先编写代码将顶点数据去重:

//vertex_data.h //去重的顶点Vector static vector<Vertex> kVertexRemoveDumplicateVector; //顶点索引Vector static vector<unsigned short> kVertexIndexVector; //顶点去重 static void VertexRemoveDumplicate(){ for (int i = 0; i < 36; ++i) { const Vertex& vertex=kVertexs[i]; //判断顶点是否存在 ...... } }

断点调试,发现顶点去重后只剩下16个,节省了一大半内存。

后续渲染就使用去重后的顶点数据(kVertexRemoveDumplicateVector) 和 顶点索引(kVertexIndexVector)。

3.2 使用顶点索引绘制

首先将去重后的顶点数据(kVertexRemoveDumplicateVector)与Shader属性进行绑定:

//启用顶点Shader属性(a_pos),指定与顶点坐标数据进行关联 glVertexAttribPointer(vpos_location, 3, GL_FLOAT, false, sizeof(Vertex), (float*)(&kVertexRemoveDumplicateVector[0])); //启用顶点Shader属性(a_color),指定与顶点颜色数据进行关联 glVertexAttribPointer(vcol_location, 4, GL_FLOAT, false, sizeof(Vertex), ((float*)(&kVertexRemoveDumplicateVector[0]) + 3)); //顶启用顶点Shader属性(a_uv),指定与点UV数据进行关联 glVertexAttribPointer(a_uv_location, 2, GL_FLOAT, false, sizeof(Vertex), ((float*)(&kVertexRemoveDumplicateVector[0]) + 3 + 4));

然后使用顶点索引(kVertexIndexVector)进行绘制,需要引入新的API - glDrawElements

void glDrawElements(GLenum mode,GLsizei count,GLenum type,const void * indices); 参数解析: mode 几何图元类型,可选 GL_POINT(点)、GL_LINE(线)、GL_TRIANGLES(三角形) count 几何图元数量 type 索引数据类型 indices 索引数组

将原来的代码修改为:

//glDrawArrays(GL_TRIANGLES, 0, 6*6);//表示从第0个顶点开始画,总共画6个面,每个面6个顶点。 glDrawElements(GL_TRIANGLES,36,GL_UNSIGNED_SHORT,(float*)(&kVertexIndexVector[0]));//使用顶点索引进行绘制。

再次编译运行,结果正常:


发布者: 作者: 转发
评论区 (0)
U