OpenGL深入探索——Assimp加载模型并绑定VAO
转载自:第三十二课 顶点数组对象
背景
顶点数组对象( VAO )是一种特殊类型对象,它封装了与顶点处理器有关的所有数据,它仅仅是记录顶点缓存区和索引缓冲区的引用,以及顶点的各种属性的布局而不是实际的数据。这样做的好处是一旦你为一个 mesh 设置一个 VAO ,你就可以通过简单的绑定 VAO 来导入 mesh 的所有状态。之后你就可以直接渲染 mesh 对象而不需要担心它的状态,VAO 为你记住了它。如果你的程序需要对顶点属性布局不同的 mesh 进行处理,VAO 同样可以帮忙,你只需要确保在创建 VAO 的时候为其设置了正确的布局即可,之后就完全不用管它了,因为这些信息都已经被保存在了 VAO 中。
当你正确使用时,VAO也可以代表一种对 GPU 驱动的优化,如果 VAO 被创建了一次,并且在之后被多次使用,由于驱动程序知道了顶点缓存与索引缓存之间的映射关系以及缓存中顶点属性的布局,所以就可以对其进行一些优化。很显然,这都取决于你使用的驱动程序,而且不同的驱动其优化方式也不能保证完全一样。但是不管怎么说, 复用 VAO 总是最好的选择。
在这一课中,我们基于上面所讲的 VAO 对 Mesh类进行更新。除此之外,我们将使用 SOA(存放数组的结构体)方法组织缓冲区中的顶点数据。现在为止我们的顶点数据都是用一个包含各种顶点属性(如位置属性等)的结构体表示的,顶点缓存中包含了一个又一个的顶点结构体,缓存中的这种数据保存方式称作 AOS(存放结构体的数组)。 SOA 是这种模式的一个简单变换,这种模式下缓存中存放的是一个包含了多个数组的结构体,每个数组中都只存放顶点的某一个属性,为了得到某一个顶点的所有属性,GPU 会使用这个顶点的索引从每个数组中读取数据,这个方式对某些 3D 模型文件来说效率会更高一些,而且对于同样的问题,了解一些不同的解决方法也是很有趣的。
下面的图片介绍了 AOS 和 SOA:
-
class Mesh
-
{
-
public:
-
Mesh();
-
~Mesh();
-
bool LoadMesh(const std::string& Filename);
-
void Render();
-
private:
-
bool InitFromScene(const aiScene* pScene, const std::string& Filename);
-
void InitMesh(const aiMesh* paiMesh,
-
std::vector& Positions,
-
std::vector& Normals,
-
std::vector& TexCoords,
-
std::vector& Indices);
-
bool InitMaterials(const aiScene* pScene, const std::string& Filename);
-
void Clear();
-
#define INVALID_MATERIAL 0xFFFFFFFF
-
#define INDEX_BUFFER 0
-
#define POS_VB 1
-
#define NORMAL_VB 2
-
#define TEXCOORD_VB 3
-
GLuint m_VAO;
-
GLuint m_Buffers[4];
-
struct MeshEntry {
-
MeshEntry()
-
{
-
NumIndices = 0;
-
BaseVertex = 0;
-
BaseIndex = 0;
-
MaterialIndex = INVALID_MATERIAL;
-
}
-
unsigned int BaseVertex;
-
unsigned int BaseIndex;
-
unsigned int NumIndices;
-
unsigned int MaterialIndex;
-
};
-
std::vector m_Entries;
-
std::vector m_Textures;
-
};
NumIndices BaseVertex BaseIndex 因为所有的子模型都被依次存放在同一个缓冲区中
-
bool Mesh::LoadMesh(const string& Filename)
-
{
-
// Release the previously loaded mesh (if it exists)
-
Clear();
-
// Create the VAO
-
glGenVertexArrays(1, &m_VAO);
-
glBindVertexArray(m_VAO);
-
// Create the buffers for the vertices atttributes
-
glGenBuffers(ARRAY_SIZE_IN_ELEMENTS(m_Buffers), m_Buffers);
-
bool Ret = false;
-
Assimp::Importer Importer;
-
const aiScene* pScene = Importer.ReadFile(Filename.c_str(), aiProcess_Triangulate |
-
aiProcess_GenSmoothNormals | aiProcess_FlipUVs);
-
if (pScene) {
-
Ret = InitFromScene(pScene, Filename);
-
}
-
else {
-
printf("Error parsing '%s': '%s'\n", Filename.c_str(), Importer.GetErrorString());
-
}
-
// Make sure the VAO is not changed from outside code
-
glBindVertexArray(0);
-
return Ret;
-
}
不论何时,都只能有一个 VAO 被绑定。
-
bool Mesh::InitFromScene(const aiScene* pScene, const string& Filename)
-
{
-
m_Entries.resize(pScene->mNumMeshes);
-
m_Textures.resize(pScene->mNumMaterials);
-
// Prepare vectors for vertex attributes and indices
-
vector Positions;
-
vector Normals;
-
vector TexCoords;
-
vector Indices;
-
unsigned int NumVertices = 0;
-
unsigned int NumIndices = 0;
-
// Count the number of vertices and indices
-
for (unsigned int i = 0 ; i < m_Entries.size() ; i++) {
-
m_Entries[i].MaterialIndex = pScene->mMeshes[i]->mMaterialIndex;
-
m_Entries[i].NumIndices = pScene->mMeshes[i]->mNumFaces * 3;
-
m_Entries[i].BaseVertex = NumVertices;
-
m_Entries[i].BaseIndex = NumIndices;
-
NumVertices += pScene->mMeshes[i]->mNumVertices;
-
NumIndices += m_Entries[i].BaseIndex;
-
}
-
// Reserve space in the vectors for the vertex attributes and indices
-
Positions.reserve(NumVertices);
-
Normals.reserve(NumVertices);
-
TexCoords.reserve(NumVertices);
-
Indices.reserve(NumIndices);
-
// Initialize the meshes in the scene one by one
-
for (unsigned int i = 0 ; i < m_Entries.size() ; i++) {
-
const aiMesh* paiMesh = pScene->mMeshes[i];
-
InitMesh(paiMesh, Positions, Normals, TexCoords, Indices);
-
}
-
if (!InitMaterials(pScene, Filename)) {
-
return false;
-
}
-
// Generate and populate the buffers with vertex attributes and the indices
-
glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[POS_VB]);
-
glBufferData(GL_ARRAY_BUFFER, sizeof(Positions[0]) * Positions.size(), &Positions[0],
-
GL_STATIC_DRAW);
-
glEnableVertexAttribArray(0);
-
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
-
glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[TEXCOORD_VB]);
-
glBufferData(GL_ARRAY_BUFFER, sizeof(TexCoords[0]) * TexCoords.size(), &TexCoords[0],
-
GL_STATIC_DRAW);
-
glEnableVertexAttribArray(1);
-
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
-
glBindBuffer(GL_ARRAY_BUFFER, m_Buffers[NORMAL_VB]);
-
glBufferData(GL_ARRAY_BUFFER, sizeof(Normals[0]) * Normals.size(), &Normals[0],
-
GL_STATIC_DRAW);
-
glEnableVertexAttribArray(2);
-
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
-
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_Buffers[INDEX_BUFFER]);
-
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices[0]) * Indices.size(), &Indices[0],
-
GL_STATIC_DRAW);
-
return true;
-
}
我们先计算在 aiScene 结构体中的顶点和索引的数量,而且对于每一个 aiMesh 结构体我们都会将其材质索引、索引数量、base vertex 和 base index 保存在 m_Entries 数组中
遍历 aiScene 中每个 aiMesh 结构体
一个接一个的绑定到 GL_ARRAY_BUFFER 目标上
- 使用 glBufferData() 将数据填充到缓存中;
- 使用 glEnableVertexAttribArray() 来启用对应的顶点属性;
- 使用 glVertexAttribPointer() 来配置顶点属性(分量的数量,分量的类型等)。
-
void Mesh::InitMesh(const aiMesh* paiMesh,
-
vector& Positions,
-
vector& Normals,
-
vector& TexCoords,
-
vector& Indices)
-
{
-
const aiVector3D Zero3D(0.0f, 0.0f, 0.0f);
-
// Populate the vertex attribute vectors
-
for (unsigned int i = 0 ; i < paiMesh->mNumVertices ; i++) {
-
const aiVector3D* pPos = &(paiMesh->mVertices[i]);
-
const aiVector3D* pNormal = &(paiMesh->mNormals[i]);
-
const aiVector3D* pTexCoord = paiMesh->HasTextureCoords(0) ?
-
&(paiMesh->mTextureCoords[0][i]) : &Zero3D;
-
Positions.push_back(Vector3f(pPos->x, pPos->y, pPos->z));
-
Normals.push_back(Vector3f(pNormal->x, pNormal->y, pNormal->z));
-
TexCoords.push_back(Vector2f(pTexCoord->x, pTexCoord->y));
-
}
-
// Populate the index buffer
-
for (unsigned int i = 0 ; i < paiMesh->mNumFaces ; i++) {
-
const aiFace& Face = paiMesh->mFaces[i];
-
assert(Face.mNumIndices == 3);
-
Indices.push_back(Face.mIndices[0]);
-
Indices.push_back(Face.mIndices[1]);
-
Indices.push_back(Face.mIndices[2]);
-
}
-
}
-
void Mesh::Render()
-
{
-
glBindVertexArray(m_VAO);
-
for (unsigned int i = 0 ; i < m_Entries.size() ; i++) {
-
const unsigned int MaterialIndex = m_Entries[i].MaterialIndex;
-
assert(MaterialIndex < m_Textures.size());
-
if (m_Textures[MaterialIndex]) {
-
m_Textures[MaterialIndex]->Bind(GL_TEXTURE0);
-
}
-
glDrawElementsBaseVertex(GL_TRIANGLES,
-
m_Entries[i].NumIndices,
-
GL_UNSIGNED_INT,
-
(void*)(sizeof(unsigned int) * m_Entries[i].BaseIndex),
-
m_Entries[i].BaseVertex);
-
}
-
// Make sure the VAO is not changed from the outside
-
glBindVertexArray(0);
-
}
glDrawElementsBaseVertex() 第四个参数 索引缓存 同一个 所占字节数 同一个 第五个参数 需要注意的是这里我们设置的是一个索引而不是字节偏移量
glDeleteVertexArrays(1, &m_VAO);
文章来源: panda1234lee.blog.csdn.net,作者:panda1234lee,版权归原作者所有,如需转载,请联系作者。
原文链接:panda1234lee.blog.csdn.net/article/details/51744309
- 点赞
- 收藏
- 关注作者
评论(0)