顶点去重
按照之前的处理,我们没有达到索引缓冲节约空间的目的。三角形表面的顶点是被多个三角形表面共用的,而我们则是每个顶点都重新定义一次,vertices向量包含了大量重复的顶点数据。我们可以将完全相同的顶点数据只保留一个,来解决空间。这一去重过程可以通过STL的map或unordered_map来实现:
#include <unordered_map>
...
std::unordered_map<Vertex, uint32_t> uniqueVertices = {};
for (const auto& shape : shapes) {
for (const auto& index : shape.mesh.indices) {
Vertex vertex = {};
...
if (uniqueVertices.count(vertex) == 0) {
uniqueVertices[vertex] = static_cast<uint32_t>(vertices.size());
vertices.push_back(vertex);
}
indices.push_back(uniqueVertices[vertex]);
}
}
在从OBJ模型文件加载模型数据时,我们检查加载的顶点数据是否与已经加载的数据完全相同,如果相同,就不再将其加入vertices向量,将已经加载的顶点数据的索引存储到indices向量中。如果不同,将其加入vertices向量,并存储它对应的索引值到uniqueVertices容器中。然后将其索引存储在indices向量中。
我们需要实现两个函数来让Vertex结构体可以作为map变量的键值来检索map变量,首先是==函数:
bool operator==(const Vertex& other) const {
return pos == other.pos && color == other.color && texCoord == other.texCoord;
}
然后是对Vertex结构体进行哈希的函数:
namespace std {
template<> struct hash<Vertex> {
size_t operator()(Vertex const& vertex) const {
return ((hash<glm::vec3>()(vertex.pos) ^ (hash<glm::vec3>()(vertex.color) << 1)) >> 1) ^ (hash<glm::vec2>()(vertex.texCoord) << 1);
}
};
}
上面这两个函数的代码需要放在Vertex结构体的定义外。GLM库的变量类型的哈希函数可以通过下面的代码包含到我们的程序中:
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/hash.hpp>
GLM库的哈希函数目前还是一个试验性的扩展,被定义在了GLM库的gtx目录下。所以需要我们定义GLM_ENABLE_EXPERIMENTAL宏来启用它。作为试验性扩展意味着在未来版本的GLM库有可能发生变化,但一般而言,我们可以认为变化不会太大。
现在重新编译运行程序,查看vertices向量的大小,可以发现vertices向量的大小从1,500,000下降到了265,645。这也说明对于我们的模型数据,每个顶点数据平均被6个三角形表面使用。
本章节代码:
C++:
https://vulkan-tutorial.com/code/27_model_loading.cpp
Vertex Shader:
https://vulkan-tutorial.com/code/26_shader_depth.vert
Fragment Shader: