局部空间法线转世界空间异常问题求助
局部空间法线转世界空间异常问题
我在将局部空间网格转换为世界空间时,法线向量结果不正确。顶点位置转换正常,说明世界变换矩阵可用,但通过世界变换矩阵取逆再转置得到的法线变换矩阵转换局部法线后,结果不符合预期:
- 当前计算的法线效果混乱,无法正确反映模型表面朝向
- 预期法线应呈现出每个面的合理朝向
问题代码(标注Bug位置)
FMeshData &MeshData = ModelData.Meshes[MeshIndex]; aiMesh *Mesh = Scene->mMeshes[MeshIndex]; aiNode *Node = GetParentNode(Scene->mRootNode, MeshIndex); // 获取世界变换矩阵 FMatrix PositionMatrix = GetWorldTransformOfNode(Node); FMatrix NormalMatrix = UKismetMathLibrary::Matrix_GetTransposed(PositionMatrix.Inverse()); // 材质ID MeshData.MaterialId = Mesh->mMaterialIndex; // LOD数据 GetLodData(LodFilePath, FString(Mesh->mName.C_Str()), MeshData.LodData); // 遍历顶点 for (uint32 VertexIndex = 0; VertexIndex < Mesh->mNumVertices; VertexIndex++) { // 顶点位置转换(正常) aiVector3D &aiVertex = Mesh->mVertices[VertexIndex]; FVector PositionVertex = PositionMatrix.TransformFVector4(FVector( aiVertex.x, aiVertex.y, aiVertex.z)); FVector3f Position( PositionVertex.X, PositionVertex.Y, PositionVertex.Z); // 法线转换(存在问题) FVector3f Normal = FVector3f::ZeroVector; if (Mesh->HasNormals()) { //TODO: Fix normals (Buggy) aiVector3D &aiNormal = Mesh->mNormals[VertexIndex]; FVector NormalVector = NormalMatrix.TransformFVector4(FVector( aiNormal.x, aiNormal.y, aiNormal.z)); Normal = FVector3f( NormalVector.X, NormalVector.Y, NormalVector.Z); } // 切线处理 FVector3f Tangent = FVector3f::ZeroVector; if (Mesh->HasTangentsAndBitangents()) { aiVector3D &aiTangent = Mesh->mTangents[VertexIndex]; Tangent = FVector3f( aiTangent.x, aiTangent.y, aiTangent.z); } // 顶点颜色处理 FLinearColor LinearColor = FLinearColor::White; if (Mesh->HasVertexColors(0)) { aiColor4D &aiColor = Mesh->mColors[0][VertexIndex]; LinearColor = FLinearColor( aiColor.r, aiColor.g, aiColor.b, aiColor.a); } // UV坐标处理 FVector2f UV0 = FVector2f::ZeroVector; FVector2f UV1 = FVector2f::ZeroVector; FVector2f UV2 = FVector2f::ZeroVector; FVector2f UV3 = FVector2f::ZeroVector; for (uint32 ChannelIndex = 0; ChannelIndex <= 3; ChannelIndex++) { if (Mesh->HasTextureCoords(ChannelIndex)) { aiVector3D &aiCoordinate = Mesh->mTextureCoords[ChannelIndex][VertexIndex]; FVector2f Coordinate(aiCoordinate.x, -aiCoordinate.y); switch (ChannelIndex) { case 0: UV0 = Coordinate; break; case 1: UV1 = Coordinate; break; case 2: UV2 = Coordinate; break; case 3: UV3 = Coordinate; break; } } } // 创建顶点数据 FVertexData Vertex; Vertex.Position = Position; Vertex.Normal = Normal; Vertex.Tangent = Tangent; Vertex.Color = LinearColor; Vertex.UV0 = UV0; Vertex.UV1 = UV1; Vertex.UV2 = UV2; Vertex.UV3 = UV3; // 保存顶点 MeshData.Verticies.Push(Vertex); }
修复方案
正确处理法线的齐次坐标
法线是方向向量,不受平移变换影响,因此需要用w=0的齐次向量进行变换,避免被矩阵的平移分量干扰。强制归一化法线
变换后的法线长度可能因缩放(尤其是非均匀缩放)发生变化,必须归一化才能保证方向正确。修改后的法线转换代码
//TODO: Fix normals (Buggy) aiVector3D &aiNormal = Mesh->mNormals[VertexIndex]; // 用w=0的齐次向量表示法线,排除平移影响 FVector4 NormalHomogeneous(aiNormal.x, aiNormal.y, aiNormal.z, 0.f); FVector4 TransformedNormal = NormalMatrix.TransformFVector4(NormalHomogeneous); // 归一化,确保法线长度为1 FVector NormalVector = TransformedNormal.GetSafeNormal(); Normal = FVector3f( NormalVector.X, NormalVector.Y, NormalVector.Z);
- 额外检查点
- 确认
GetWorldTransformOfNode返回的矩阵是否包含非均匀缩放:如果存在非均匀缩放,逆转置矩阵是必须的;如果只有旋转和平移,可直接用转置矩阵代替逆转置(正交矩阵的逆等于转置)。 - 若模型有缩放,确保法线变换矩阵的计算没有误差,Unreal的
FMatrix::Inverse()对正交矩阵的计算是高效且准确的。
内容的提问来源于stack exchange,提问作者Fynn Haupt
相关产品推荐
相关产品推荐

