从 Assimp 加载 Collada (dae) 模型显示不正确的法线
Loading a Collada (dae) model from Assimp shows incorrect normals
解决方案:
感谢 Rabbid76,我需要用模型矩阵更新顶点着色器中的 Normal
向量。更新的顶点着色器:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform float scale;
void main()
{
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = vec3(model * vec4(aNormal, 0.0));
gl_Position = projection * view * vec4(FragPos, 1.0);
}
问题
我试图在 Assimp 中正确加载 collada (dae) 文件,但法线似乎出错了。我想帮助解决这个问题。我觉得这与我处理转换矩阵的方式有关。例如,这是加载 obj 文件的 OpenGL 应用程序的屏幕截图:
在上面的屏幕截图中,光源位于模型正上方 x=0 和 z=0 处。法线显示正确。当我加载 dae 文件时,我得到以下信息:
光线位置似乎来自-z 侧。
这是我目前必须加载模型的代码:
- 加载模型文件,并调用包含
aiMatrix4x4()
的processNode()
方法
void Model::loadModel(std::string filename)
{
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(filename, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace | aiProcess_GenBoundingBoxes);
if (!scene || !scene->mRootNode) {
std::cout << "ERROR::ASSIMP Could not load model: " << importer.GetErrorString() << std::endl;
}
else {
this->directory = filename.substr(0, filename.find_last_of('/'));
this->processNode(scene->mRootNode, scene, aiMatrix4x4());
}
}
processNode()
是一种递归方法,主要迭代 node->mMeshes
我乘以转换。
void Model::processNode(aiNode* node, const aiScene* scene, aiMatrix4x4 transformation)
{
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
// only apply transformation on meshs not entities such as lights or camera.
transformation *= node->mTransformation;
this->meshes.push_back(processMesh(mesh, scene, transformation));
}
for (unsigned int i = 0; i < node->mNumChildren; i++)
{
processNode(node->mChildren[i], scene, transformation);
}
}
processMesh()
处理收集所有网格数据(顶点、索引等)
Mesh Model::processMesh(aiMesh* mesh, const aiScene* scene, aiMatrix4x4 transformation)
{
glm::vec3 extents;
glm::vec3 origin;
std::vector<Vertex> vertices = this->vertices(mesh, extents, origin, transformation);
std::vector<unsigned int> indices = this->indices(mesh);
std::vector<Texture> textures = this->textures(mesh, scene);
return Mesh(
vertices,
indices,
textures,
extents,
origin,
mesh->mName
);
}
- 接下来调用
vertices()
方法获取所有的顶点。它传递变换矩阵。在这里,我将顶点与矩阵相乘 (transformation * mesh->mVertices[i];
)。我有一种强烈的感觉,我没有在这里做某事,而且我错过了一些东西。
std::vector<Vertex> Model::vertices(aiMesh* mesh, glm::vec3& extents, glm::vec3 &origin, aiMatrix4x4 transformation)
{
std::vector<Vertex> vertices;
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
Vertex vertex;
glm::vec3 vector3;
aiVector3D v = transformation * mesh->mVertices[i];
// Vertices
vector3.x = v.x;
vector3.y = v.y;
vector3.z = v.z;
vertex.position = vector3;
// Normals
if (mesh->mNormals) {
vector3.x = mesh->mNormals[i].x;
vector3.y = mesh->mNormals[i].y;
vector3.z = mesh->mNormals[i].z;
vertex.normal = vector3;
}
// Texture coordinates
if (mesh->mTextureCoords[0]) {
glm::vec2 vector2;
vector2.x = mesh->mTextureCoords[0][i].x;
vector2.y = mesh->mTextureCoords[0][i].y;
vertex.texCoord = vector2;
}
else {
vertex.texCoord = glm::vec2(0, 0);
}
if (mesh->mTangents) {
vector3.x = mesh->mTangents[i].x;
vector3.y = mesh->mTangents[i].y;
vector3.z = mesh->mTangents[i].z;
vertex.tangent = vector3;
}
// Bitangent
if (mesh->mBitangents) {
vector3.x = mesh->mBitangents[i].x;
vector3.y = mesh->mBitangents[i].y;
vector3.z = mesh->mBitangents[i].z;
vertex.bitangent = vector3;
}
vertices.push_back(vertex);
}
glm::vec3 min = glm::vec3(mesh->mAABB.mMin.x, mesh->mAABB.mMin.y, mesh->mAABB.mMin.z);
glm::vec3 max = glm::vec3(mesh->mAABB.mMax.x, mesh->mAABB.mMax.y, mesh->mAABB.mMax.z);
extents = (max - min) * 0.5f;
origin = glm::vec3((min.x + max.x) / 2.0f, (min.y + max.y) / 2.0f, (min.z + max.z) / 2.0f);
printf("%f,%f,%f\n", origin.x, origin.y, origin.z);
return vertices;
}
作为补充说明,如果有帮助,这是我在模型上使用的片段着色器:
#version 330 core
out vec4 FragColor;
in vec3 Normal;
in vec3 FragPos;
uniform vec3 lightPos;
uniform vec3 viewPos;
vec3 lightColor = vec3(1,1,1);
vec3 objectColor = vec3(0.6, 0.6, 0.6);
uniform float shininess = 32.0f;
uniform vec3 material_specular = vec3(0.1f, 0.1f, 0.1f);
uniform vec3 light_specular = vec3(0.5f, 0.5f, 0.5f);
void main()
{
// ambient
float ambientStrength = 0.2;
vec3 ambient = ambientStrength * lightColor;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
// specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), shininess);
vec3 specular = light_specular * (spec * material_specular);
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
}
这是顶点着色器:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform float scale;
void main()
{
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = aNormal;
gl_Position = projection * view * vec4(FragPos, 1.0);
}
FragPos
是worldspace中的一个位置,因为它是模型矩阵变换后的顶点位置。 lightPos
和 viewPos
似乎也是世界 space 中的位置。
因此必须将法向量 aNormal
从模型 space 转换为世界 space。
你必须通过inverse transposed 左上角的3*3,4*4的法向量进行变换模型矩阵:
Normal = transpose(inverse(mat3(model))) * aNormal;
可能4*4模型矩阵的左上3*3变换就够了:
(参见 In which cases is the inverse matrix equal to the transpose?)
Normal = mat3(model) * aNormal;
另见:
Why is the transposed inverse of the model view matrix used to transform the normal vectors?
Why transforming normals with the transpose of the inverse of the modelview matrix?
解决方案:
感谢 Rabbid76,我需要用模型矩阵更新顶点着色器中的 Normal
向量。更新的顶点着色器:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform float scale;
void main()
{
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = vec3(model * vec4(aNormal, 0.0));
gl_Position = projection * view * vec4(FragPos, 1.0);
}
问题
我试图在 Assimp 中正确加载 collada (dae) 文件,但法线似乎出错了。我想帮助解决这个问题。我觉得这与我处理转换矩阵的方式有关。例如,这是加载 obj 文件的 OpenGL 应用程序的屏幕截图:
在上面的屏幕截图中,光源位于模型正上方 x=0 和 z=0 处。法线显示正确。当我加载 dae 文件时,我得到以下信息:
光线位置似乎来自-z 侧。
这是我目前必须加载模型的代码:
- 加载模型文件,并调用包含
aiMatrix4x4()
的
processNode()
方法
void Model::loadModel(std::string filename)
{
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(filename, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace | aiProcess_GenBoundingBoxes);
if (!scene || !scene->mRootNode) {
std::cout << "ERROR::ASSIMP Could not load model: " << importer.GetErrorString() << std::endl;
}
else {
this->directory = filename.substr(0, filename.find_last_of('/'));
this->processNode(scene->mRootNode, scene, aiMatrix4x4());
}
}
processNode()
是一种递归方法,主要迭代node->mMeshes
我乘以转换。
void Model::processNode(aiNode* node, const aiScene* scene, aiMatrix4x4 transformation)
{
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
// only apply transformation on meshs not entities such as lights or camera.
transformation *= node->mTransformation;
this->meshes.push_back(processMesh(mesh, scene, transformation));
}
for (unsigned int i = 0; i < node->mNumChildren; i++)
{
processNode(node->mChildren[i], scene, transformation);
}
}
processMesh()
处理收集所有网格数据(顶点、索引等)
Mesh Model::processMesh(aiMesh* mesh, const aiScene* scene, aiMatrix4x4 transformation)
{
glm::vec3 extents;
glm::vec3 origin;
std::vector<Vertex> vertices = this->vertices(mesh, extents, origin, transformation);
std::vector<unsigned int> indices = this->indices(mesh);
std::vector<Texture> textures = this->textures(mesh, scene);
return Mesh(
vertices,
indices,
textures,
extents,
origin,
mesh->mName
);
}
- 接下来调用
vertices()
方法获取所有的顶点。它传递变换矩阵。在这里,我将顶点与矩阵相乘 (transformation * mesh->mVertices[i];
)。我有一种强烈的感觉,我没有在这里做某事,而且我错过了一些东西。
std::vector<Vertex> Model::vertices(aiMesh* mesh, glm::vec3& extents, glm::vec3 &origin, aiMatrix4x4 transformation)
{
std::vector<Vertex> vertices;
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
Vertex vertex;
glm::vec3 vector3;
aiVector3D v = transformation * mesh->mVertices[i];
// Vertices
vector3.x = v.x;
vector3.y = v.y;
vector3.z = v.z;
vertex.position = vector3;
// Normals
if (mesh->mNormals) {
vector3.x = mesh->mNormals[i].x;
vector3.y = mesh->mNormals[i].y;
vector3.z = mesh->mNormals[i].z;
vertex.normal = vector3;
}
// Texture coordinates
if (mesh->mTextureCoords[0]) {
glm::vec2 vector2;
vector2.x = mesh->mTextureCoords[0][i].x;
vector2.y = mesh->mTextureCoords[0][i].y;
vertex.texCoord = vector2;
}
else {
vertex.texCoord = glm::vec2(0, 0);
}
if (mesh->mTangents) {
vector3.x = mesh->mTangents[i].x;
vector3.y = mesh->mTangents[i].y;
vector3.z = mesh->mTangents[i].z;
vertex.tangent = vector3;
}
// Bitangent
if (mesh->mBitangents) {
vector3.x = mesh->mBitangents[i].x;
vector3.y = mesh->mBitangents[i].y;
vector3.z = mesh->mBitangents[i].z;
vertex.bitangent = vector3;
}
vertices.push_back(vertex);
}
glm::vec3 min = glm::vec3(mesh->mAABB.mMin.x, mesh->mAABB.mMin.y, mesh->mAABB.mMin.z);
glm::vec3 max = glm::vec3(mesh->mAABB.mMax.x, mesh->mAABB.mMax.y, mesh->mAABB.mMax.z);
extents = (max - min) * 0.5f;
origin = glm::vec3((min.x + max.x) / 2.0f, (min.y + max.y) / 2.0f, (min.z + max.z) / 2.0f);
printf("%f,%f,%f\n", origin.x, origin.y, origin.z);
return vertices;
}
作为补充说明,如果有帮助,这是我在模型上使用的片段着色器:
#version 330 core
out vec4 FragColor;
in vec3 Normal;
in vec3 FragPos;
uniform vec3 lightPos;
uniform vec3 viewPos;
vec3 lightColor = vec3(1,1,1);
vec3 objectColor = vec3(0.6, 0.6, 0.6);
uniform float shininess = 32.0f;
uniform vec3 material_specular = vec3(0.1f, 0.1f, 0.1f);
uniform vec3 light_specular = vec3(0.5f, 0.5f, 0.5f);
void main()
{
// ambient
float ambientStrength = 0.2;
vec3 ambient = ambientStrength * lightColor;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
// specular
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), shininess);
vec3 specular = light_specular * (spec * material_specular);
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
}
这是顶点着色器:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
out vec3 FragPos;
out vec3 Normal;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform float scale;
void main()
{
FragPos = vec3(model * vec4(aPos, 1.0));
Normal = aNormal;
gl_Position = projection * view * vec4(FragPos, 1.0);
}
FragPos
是worldspace中的一个位置,因为它是模型矩阵变换后的顶点位置。 lightPos
和 viewPos
似乎也是世界 space 中的位置。
因此必须将法向量 aNormal
从模型 space 转换为世界 space。
你必须通过inverse transposed 左上角的3*3,4*4的法向量进行变换模型矩阵:
Normal = transpose(inverse(mat3(model))) * aNormal;
可能4*4模型矩阵的左上3*3变换就够了:
(参见 In which cases is the inverse matrix equal to the transpose?)
Normal = mat3(model) * aNormal;
另见:
Why is the transposed inverse of the model view matrix used to transform the normal vectors?
Why transforming normals with the transpose of the inverse of the modelview matrix?