镜像网格和错误的 UV 贴图运行时导出

Posted

技术标签:

【中文标题】镜像网格和错误的 UV 贴图运行时导出【英文标题】:Mirrored mesh and wrong UV map runtime export 【发布时间】:2017-10-06 03:43:23 【问题描述】:

编辑:所以在与 Assimp 开发人员短暂接触后,我被指出了导入过程。当我从别人那里接过代码时,我没想到会看那部分:

using (var importer = new AssimpContext())

   scene = importer.ImportFile(file, PostProcessSteps.Triangulate | PostProcessSteps.FlipUVs | PostProcessSteps.JoinIdenticalVertices);

FlipUVs 完全按照它说的做,它在 y 轴上翻转,所以原点现在位于左上角。所以现在我能够得到具有适当 UV 但仍然是镜像网格的模型。使用 scale x = -1 设置父对象会将其翻转回正常状态并使其看起来不错,但我想这不是故意的。所以我一直在寻找。


看图,有两种起重机型号。左边的是在运行时通过序列化和重建加载的,而右边的是原始的,只是简单地拖到场景中。 序列化发生在 Assimp 库中。

恰好首先创建了地板,并且似乎获得了正确的 uv 贴图。而其他项目得到错误的 uv 贴图。虽然我正在打印 uv 贴图的值,但它们似乎与原始贴图相匹配。

这是如何序列化的,这是Assimp的Mesh类,不是Unity Mesh类,应用序列化是UWP内置的Windows应用:

    private static void SerializeMeshes(BinaryWriter writer, IEnumerable<Mesh> meshes)
    
        foreach (Mesh mesh in meshes)
        
            ICollection<int> triangles = MeshLoadTriangles(mesh);
            MeshSerializeHeader(writer, mesh.Name, mesh.VertexCount, triangles.Count, mesh.MaterialIndex);
            MeshSerializeVertices(writer, mesh.Vertices);
            MeshSerializeUVCoordinate(writer, mesh.TextureCoordinateChannels);
            MeshSerializeTriangleIndices(writer, triangles);                       
        
    

    private static void MeshSerializeUVCoordinate(BinaryWriter writer, List<Vector3D>[] textureCoordinateChannels)
    
        // get first channel and serialize to writer. Discard z channel
        // This is Vector3D since happening outside Unity
        List<Vector3D> list = textureCoordinateChannels[0];
        foreach (Vector3D v in list)
        
            float x = v.X;
            float y = v.Y;
            writer.Write(x);
            writer.Write(y);
        
    
    private static void MeshSerializeVertices(BinaryWriter writer, IEnumerable<Vector3D> vertices)
    
        foreach (Vector3D vertex in vertices)
        
            Vector3D temp = vertex;
            writer.Write(temp.X);
            writer.Write(temp.Y);
            writer.Write(temp.Z);
        
    
    private static void MeshSerializeTriangleIndices(BinaryWriter writer, IEnumerable<int> triangleIndices)
    
          foreach (int index in triangleIndices)   writer.Write(index); 
    

这是反转过程:

    private static void DeserializeMeshes(BinaryReader reader, SceneGraph scene)
               
        MeshData[] meshes = new MeshData[scene.meshCount];
        for (int i = 0; i < scene.meshCount; i++)
        
             meshes[i] = new MeshData();
             MeshReadHeader(reader, meshes[i]);
             MeshReadVertices(reader, meshes[i]);
             MeshReadUVCoordinate(reader, meshes[i]);
             MeshReadTriangleIndices(reader, meshes[i]);
        
        scene.meshes = meshes as IEnumerable<MeshData>;
    
private static void MeshReadUVCoordinate(BinaryReader reader, MeshData meshData)
    
        bool hasUv = reader.ReadBoolean();
        if(hasUv == false)  return; 
        Vector2[] uvs = new Vector2[meshData.vertexCount];
        for (int i = 0; i < uvs.Length; i++)
        
            uvs[i] = new Vector2();
            uvs[i].x = reader.ReadSingle();
            uvs[i].y = reader.ReadSingle();
        
        meshData.uvs = uvs;
    
    private static void MeshReadHeader(BinaryReader reader, MeshData meshData)
    
        meshData.name = reader.ReadString();
        meshData.vertexCount = reader.ReadInt32();
        meshData.triangleCount = reader.ReadInt32();
        meshData.materialIndex = reader.ReadInt32();
    
    private static void MeshReadVertices(BinaryReader reader, MeshData meshData)
    
        Vector3[] vertices = new Vector3[meshData.vertexCount];

        for (int i = 0; i < vertices.Length; i++)
        
            vertices[i] = new Vector3();
            vertices[i].x = reader.ReadSingle();
            vertices[i].y = reader.ReadSingle();
            vertices[i].z = reader.ReadSingle();
        
        meshData.vertices = vertices;
    
    private static void MeshReadTriangleIndices(BinaryReader reader, MeshData meshData)
    
        int[] triangleIndices = new int[meshData.triangleCount];

        for (int i = 0; i < triangleIndices.Length; i++)
        
            triangleIndices[i] = reader.ReadInt32();
        
        meshData.triangles = triangleIndices;
    

MeshData 只是一个临时容器,其中包含来自 fbx 的反序列化值。 然后,创建网格:

private static Mesh[] CreateMeshes(SceneGraph scene)

    Mesh[] meshes = new Mesh[scene.meshCount];
    int index = 0;
    foreach (MeshData meshData in scene.meshes)
    
        meshes[index] = new Mesh();           
        Vector3[] vec = meshData.vertices;
        meshes[index].vertices = vec;
        meshes[index].triangles = meshData.triangles;
        meshes[index].uv = meshData.uvs;   
        meshes[index].normals = meshData.normals;  
        meshes[index].RecalculateNormals();
        index++;
    
    return meshes;

我认为代码中没有任何原因会导致这种行为,我想说如果值错误,它会完全搞砸网格。

我可以看到我拥有的 fbx 文件使用四边形而不是三角形进行索引。

会不会是 Assimp 在这方面做得不好?

【问题讨论】:

我能想到的唯一原因是使用 foreach 循环。有时 foreach 不会按预期进行迭代。尝试在序列化和反序列化中都使用经典的 for() 循环。希望这会有所帮助 也许这些是从 3D 建模软件以不同设置导出的? 调试时什么时候出现异常? 也不例外。它实际上一直有效,但不是以预期的方式。 听起来您的创作工具使用右手坐标系,而 Unity 使用左手坐标系。如果是这种情况,如果您导出带有旋转的游戏对象层次结构,您还需要做更多的事情。 【参考方案1】:

我没有从 Assimp 以适当的方式解决问题。

我们使用的基本解决方案是对对象变换中翻转的轴进行负缩放。

更合适的解决方案是将所有顶点提供给 Unity 端的矩阵,以便正确解析顶点的位置。

获取顶点列表 foreach 顶点与旋转矩阵相乘 将数组分配给网格 使用网格进行渲染

【讨论】:

以上是关于镜像网格和错误的 UV 贴图运行时导出的主要内容,如果未能解决你的问题,请参考以下文章

如何在3dmax中将模型导出成FBX格式并带贴图动画?

将纹理 3d 模型和纹理从搅拌机导出到 xna 4.0

DAE文件解析错误

DAE 文件解析错误

OC纹理投射、转换节点属性讲解0022

是否可以从 Blender 中的渲染对象创建 UV 贴图