使用 Assimp 和 OpenGL 渲染问题
Render Issues Using Assimp and OpenGL
我正在使用 Assimp 加载模型以在 OpenGL 中渲染,但 运行 遇到了 chunks/pieces 网格不渲染的问题。
示例:
什么样的模型应该看起来像:
我最终渲染的是:
如您所见,部分模型渲染正确,但并非全部。
我已经多次验证从 assimp 加载的网格正在将正确的顶点和索引加载到我的“网格”中 class。这是我加载模型的代码:
此函数将递归地为所有子节点调用自身并加载节点内的每个网格。然后通过创建顶点和面的向量,每个网格将被转换成我自己的“网格”class。
void Model::LoadAssimpNode(aiNode* node, const aiScene* scene)
{
// Process assimp meshes
for (unsigned int i = 0; i < node->mNumMeshes; i++)
{
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
this->meshes.push_back(this->LoadAssimpMesh(mesh, scene));
}
// Recursivley processes child nodes
for (unsigned int i = 0; i < node->mNumChildren; i++)
{
this->LoadAssimpNode(node->mChildren[i], scene);
}
}
Mesh Model::LoadAssimpMesh(aiMesh* mesh, const aiScene* scene)
{
std::vector<sVertex> vertices;
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
sVertex vertex;
vertex.x = mesh->mVertices[i].x;
vertex.y = mesh->mVertices[i].y;
vertex.z = mesh->mVertices[i].z;
vertex.nx = mesh->mNormals[i].x;
vertex.ny = mesh->mNormals[i].y;
vertex.nz = mesh->mNormals[i].z;
if (mesh->mTextureCoords[0])
{
vertex.u0 = mesh->mTextureCoords[0][i].x;
vertex.v0 = mesh->mTextureCoords[0][i].y;
}
vertices.push_back(vertex);
}
std::vector<sTriangle> faces;
for (unsigned int i = 0; i < mesh->mNumFaces; i++)
{
sTriangle face;
aiFace assimpFace = mesh->mFaces[i];
if (assimpFace.mNumIndices != 3)
{
std::cout << "Face is not a triangle!" << std::endl;
}
for (unsigned int j = 0; j < assimpFace.mNumIndices; j++)
{
face.vertIndex[j] = assimpFace.mIndices[j];
}
faces.push_back(face);
}
std::vector<Texture> textures;
if (mesh->mMaterialIndex >= 0)
{
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
// Sampler names should adhere to the following convention:
// Diffuse: texure_diffuseN
// Specular: texture_specularN
// Normal: texture_normalN
// Where N = texture numbers
for (Texture texture : this->LoadAssimpMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse"))
{
this->loadedTextures.insert(std::make_pair(texture.path.C_Str(), texture));
textures.push_back(texture);
}
for (Texture texture : this->LoadAssimpMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular"))
{
this->loadedTextures.insert(std::make_pair(texture.path.C_Str(), texture));
textures.push_back(texture);
}
}
return Mesh(vertices, faces, textures);
}
sVertex 和 sTriangle 结构定义为:
struct sVertex
{
float x, y, z;
float nx, ny, nz;
float u0, v0;
};
struct sTriangle
{
unsigned int vertIndex[3];
};
现在模型已从 assimp 有效加载,我们现在调用 SetupMesh() 函数来设置网格各自的 VAO、VBO 和 EBO:
void Mesh::SetupMesh()
{
// Generate IDs for our VAO, VBO and EBO
glGenVertexArrays(1, &this->VAO);
glGenBuffers(1, &this->VBO);
glGenBuffers(1, &this->EBO);
glBindVertexArray(this->VAO);
// Now ANY state that is related to vertex or index buffer
// and vertex attribute layout, is stored in the 'state'
// of the VAO...
// Tell open GL where to look for for vertex data
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(sVertex), &this->vertices[0], GL_STATIC_DRAW);
// Tell open GL where our index buffer begins (AKA: where to look for faces)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->faces.size() * sizeof(sTriangle), &this->faces[0], GL_STATIC_DRAW);
// Set the vertex attributes for this shader
// Layout information can be found in the vertex shader, currently:
// 0 = position
// 1 = normals
// 2 = texture coordinates
glEnableVertexAttribArray(0); // position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*) offsetof(sVertex, x));
glEnableVertexAttribArray(1); // normal
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*) offsetof(sVertex, nx));
glEnableVertexAttribArray(2); // textureCoordinates
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*)offsetof(sVertex, u0));
// Now that all the parts are set up, unbind buffers
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
完成所有设置后,我现在将为每个网格调用 Draw 方法以在我的渲染循环中渲染:
void Mesh::Draw(const CompiledShader& shader)
{
glm::mat4 matModel = glm::mat4(1.0f);
glm::mat4 matTranslate = glm::translate(glm::mat4(1.0f), this->positionXYZ); // Translation matrix
glm::mat4 rotateX = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.x, glm::vec3(1.0f, 0.0f, 0.0f)); // X axis rotation
glm::mat4 rotateY = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.y, glm::vec3(0.0f, 1.0f, 0.0f)); // Y axis rotation
glm::mat4 rotateZ = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.z, glm::vec3(0.0f, 0.0f, 1.0f)); // Z axis rotation
glm::mat4 matScale = glm::scale(glm::mat4(1.0f), glm::vec3(this->scale, this->scale, this->scale)); // Scale the mesh
glm::mat4 matInvTransposeModel = glm::inverse(glm::transpose(matModel));
// Apply all the transformations to our matrix
matModel = matModel * matTranslate;
matModel = matModel * rotateZ;
matModel = matModel * rotateY;
matModel = matModel * rotateX;
matModel = matModel * matScale;
glUseProgram(shader.ID);
glUniformMatrix4fv(glGetUniformLocation(shader.ID, "matModel"), 1, GL_FALSE, glm::value_ptr(matModel)); // Tell shader the model matrix (AKA: Position orientation and scale)
glUniformMatrix4fv(glGetUniformLocation(shader.ID, "matModelInverseTranspose"), 1, GL_FALSE, glm::value_ptr(matInvTransposeModel));
// Draw the mesh
glBindVertexArray(this->VAO);
glDrawElements(GL_TRIANGLES, this->faces.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
我的着色器非常简单,其中像素的颜色等于顶点的法线:
顶点着色器:
#version 420
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 textureCoordinates;
uniform mat4 matModel;
uniform mat4 matView;
uniform mat4 matProjection;
uniform mat4 matModelInverseTranspose; // For normal calculation
out vec4 fVertWorldLocation;
out vec4 fNormal;
out vec2 TextureCoordinates;
void main()
{
mat4 MVP = matProjection * matView * matModel;
gl_Position = MVP * vec4(position, 1.0f);
TextureCoordinates = textureCoordinates;
// The location of the vertex in "world" space (not screen space)
fVertWorldLocation = matModel * vec4(position, 1.0f);
// Calculate the normal based on any rotation we've applied.
// This inverse transpose removes scaling and tranlation (movement)
// from the matrix.
fNormal = matModelInverseTranspose * vec4(normal, 1.0f);
};
片段着色器:
#version 420
in vec2 TextureCoordinates;
in vec4 fNormal;
out vec4 Color;
uniform sampler2D texture_diffuse;
void main()
{
//Color = vec4(texture(texture_diffuse, TextureCoordinates));
//Color = vec4(TextureCoordinates, 1.0f, 1.0f);
Color = fNormal;
}
很抱歉这篇 post 的篇幅太长了,但我觉得所有这些都是让我的观点得到理解所必需的。
如果有人能指出我在这里做错了什么,将不胜感激!我觉得我在这里需要多一双眼睛,因为我已经无数次阅读我的代码,但似乎什么也想不出来。
犯了一个愚蠢的错误,我的印象是 glDrawElements() 函数中的“计数”参数想要面数而不是索引数。
问题已通过更改我的 glDrawElements 调用解决:
glDrawElements(GL_TRIANGLES, this->faces.size(), GL_UNSIGNED_INT, 0);
为此:
glDrawElements(GL_TRIANGLES, this->faces.size() * 3, GL_UNSIGNED_INT, 0);
我正在使用 Assimp 加载模型以在 OpenGL 中渲染,但 运行 遇到了 chunks/pieces 网格不渲染的问题。
示例:
什么样的模型应该看起来像:
我最终渲染的是:
如您所见,部分模型渲染正确,但并非全部。
我已经多次验证从 assimp 加载的网格正在将正确的顶点和索引加载到我的“网格”中 class。这是我加载模型的代码:
此函数将递归地为所有子节点调用自身并加载节点内的每个网格。然后通过创建顶点和面的向量,每个网格将被转换成我自己的“网格”class。
void Model::LoadAssimpNode(aiNode* node, const aiScene* scene)
{
// Process assimp meshes
for (unsigned int i = 0; i < node->mNumMeshes; i++)
{
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
this->meshes.push_back(this->LoadAssimpMesh(mesh, scene));
}
// Recursivley processes child nodes
for (unsigned int i = 0; i < node->mNumChildren; i++)
{
this->LoadAssimpNode(node->mChildren[i], scene);
}
}
Mesh Model::LoadAssimpMesh(aiMesh* mesh, const aiScene* scene)
{
std::vector<sVertex> vertices;
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
sVertex vertex;
vertex.x = mesh->mVertices[i].x;
vertex.y = mesh->mVertices[i].y;
vertex.z = mesh->mVertices[i].z;
vertex.nx = mesh->mNormals[i].x;
vertex.ny = mesh->mNormals[i].y;
vertex.nz = mesh->mNormals[i].z;
if (mesh->mTextureCoords[0])
{
vertex.u0 = mesh->mTextureCoords[0][i].x;
vertex.v0 = mesh->mTextureCoords[0][i].y;
}
vertices.push_back(vertex);
}
std::vector<sTriangle> faces;
for (unsigned int i = 0; i < mesh->mNumFaces; i++)
{
sTriangle face;
aiFace assimpFace = mesh->mFaces[i];
if (assimpFace.mNumIndices != 3)
{
std::cout << "Face is not a triangle!" << std::endl;
}
for (unsigned int j = 0; j < assimpFace.mNumIndices; j++)
{
face.vertIndex[j] = assimpFace.mIndices[j];
}
faces.push_back(face);
}
std::vector<Texture> textures;
if (mesh->mMaterialIndex >= 0)
{
aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];
// Sampler names should adhere to the following convention:
// Diffuse: texure_diffuseN
// Specular: texture_specularN
// Normal: texture_normalN
// Where N = texture numbers
for (Texture texture : this->LoadAssimpMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse"))
{
this->loadedTextures.insert(std::make_pair(texture.path.C_Str(), texture));
textures.push_back(texture);
}
for (Texture texture : this->LoadAssimpMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular"))
{
this->loadedTextures.insert(std::make_pair(texture.path.C_Str(), texture));
textures.push_back(texture);
}
}
return Mesh(vertices, faces, textures);
}
sVertex 和 sTriangle 结构定义为:
struct sVertex
{
float x, y, z;
float nx, ny, nz;
float u0, v0;
};
struct sTriangle
{
unsigned int vertIndex[3];
};
现在模型已从 assimp 有效加载,我们现在调用 SetupMesh() 函数来设置网格各自的 VAO、VBO 和 EBO:
void Mesh::SetupMesh()
{
// Generate IDs for our VAO, VBO and EBO
glGenVertexArrays(1, &this->VAO);
glGenBuffers(1, &this->VBO);
glGenBuffers(1, &this->EBO);
glBindVertexArray(this->VAO);
// Now ANY state that is related to vertex or index buffer
// and vertex attribute layout, is stored in the 'state'
// of the VAO...
// Tell open GL where to look for for vertex data
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(sVertex), &this->vertices[0], GL_STATIC_DRAW);
// Tell open GL where our index buffer begins (AKA: where to look for faces)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->faces.size() * sizeof(sTriangle), &this->faces[0], GL_STATIC_DRAW);
// Set the vertex attributes for this shader
// Layout information can be found in the vertex shader, currently:
// 0 = position
// 1 = normals
// 2 = texture coordinates
glEnableVertexAttribArray(0); // position
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*) offsetof(sVertex, x));
glEnableVertexAttribArray(1); // normal
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*) offsetof(sVertex, nx));
glEnableVertexAttribArray(2); // textureCoordinates
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*)offsetof(sVertex, u0));
// Now that all the parts are set up, unbind buffers
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
完成所有设置后,我现在将为每个网格调用 Draw 方法以在我的渲染循环中渲染:
void Mesh::Draw(const CompiledShader& shader)
{
glm::mat4 matModel = glm::mat4(1.0f);
glm::mat4 matTranslate = glm::translate(glm::mat4(1.0f), this->positionXYZ); // Translation matrix
glm::mat4 rotateX = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.x, glm::vec3(1.0f, 0.0f, 0.0f)); // X axis rotation
glm::mat4 rotateY = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.y, glm::vec3(0.0f, 1.0f, 0.0f)); // Y axis rotation
glm::mat4 rotateZ = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.z, glm::vec3(0.0f, 0.0f, 1.0f)); // Z axis rotation
glm::mat4 matScale = glm::scale(glm::mat4(1.0f), glm::vec3(this->scale, this->scale, this->scale)); // Scale the mesh
glm::mat4 matInvTransposeModel = glm::inverse(glm::transpose(matModel));
// Apply all the transformations to our matrix
matModel = matModel * matTranslate;
matModel = matModel * rotateZ;
matModel = matModel * rotateY;
matModel = matModel * rotateX;
matModel = matModel * matScale;
glUseProgram(shader.ID);
glUniformMatrix4fv(glGetUniformLocation(shader.ID, "matModel"), 1, GL_FALSE, glm::value_ptr(matModel)); // Tell shader the model matrix (AKA: Position orientation and scale)
glUniformMatrix4fv(glGetUniformLocation(shader.ID, "matModelInverseTranspose"), 1, GL_FALSE, glm::value_ptr(matInvTransposeModel));
// Draw the mesh
glBindVertexArray(this->VAO);
glDrawElements(GL_TRIANGLES, this->faces.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
我的着色器非常简单,其中像素的颜色等于顶点的法线:
顶点着色器:
#version 420
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 textureCoordinates;
uniform mat4 matModel;
uniform mat4 matView;
uniform mat4 matProjection;
uniform mat4 matModelInverseTranspose; // For normal calculation
out vec4 fVertWorldLocation;
out vec4 fNormal;
out vec2 TextureCoordinates;
void main()
{
mat4 MVP = matProjection * matView * matModel;
gl_Position = MVP * vec4(position, 1.0f);
TextureCoordinates = textureCoordinates;
// The location of the vertex in "world" space (not screen space)
fVertWorldLocation = matModel * vec4(position, 1.0f);
// Calculate the normal based on any rotation we've applied.
// This inverse transpose removes scaling and tranlation (movement)
// from the matrix.
fNormal = matModelInverseTranspose * vec4(normal, 1.0f);
};
片段着色器:
#version 420
in vec2 TextureCoordinates;
in vec4 fNormal;
out vec4 Color;
uniform sampler2D texture_diffuse;
void main()
{
//Color = vec4(texture(texture_diffuse, TextureCoordinates));
//Color = vec4(TextureCoordinates, 1.0f, 1.0f);
Color = fNormal;
}
很抱歉这篇 post 的篇幅太长了,但我觉得所有这些都是让我的观点得到理解所必需的。
如果有人能指出我在这里做错了什么,将不胜感激!我觉得我在这里需要多一双眼睛,因为我已经无数次阅读我的代码,但似乎什么也想不出来。
犯了一个愚蠢的错误,我的印象是 glDrawElements() 函数中的“计数”参数想要面数而不是索引数。
问题已通过更改我的 glDrawElements 调用解决:
glDrawElements(GL_TRIANGLES, this->faces.size(), GL_UNSIGNED_INT, 0);
为此:
glDrawElements(GL_TRIANGLES, this->faces.size() * 3, GL_UNSIGNED_INT, 0);