镜像网格和错误的UV映射运行时导出

24

编辑:与Assimp开发人员短暂联系后,他们指向了导入过程。由于我接手了别人的代码,我没有考虑到那一部分:

using (var importer = new AssimpContext())
{
   scene = importer.ImportFile(file, PostProcessSteps.Triangulate | PostProcessSteps.FlipUVs | PostProcessSteps.JoinIdenticalVertices);
}

FlipUVs正如其名,它在y轴上翻转,使原点现在位于左上角。现在我能够获得正确UV的模型,但是仍然是镜像的网格。将父对象设置为x = -1可以将其翻转回来并使其看起来很好,但我想这不是我的本意,所以我继续寻找。


看图片,有两个起重机模型。左边的一个是在运行时通过序列化和重构加载的,而右边的一个则是简单地拖到场景中的原始模型。 序列化使用Assimp库进行。

enter image description here

地板恰好是最先创建的,并似乎得到了正确的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()循环。希望这可以帮助。 - Nika Kasradze
也许这些是使用不同的设置从三维建模软件导出的? - Nika Kasradze
没有异常。它实际上一直在工作,但不是以预期的方式。 - Everts
2
听起来你的创作工具使用的是右手坐标系,而Unity使用的是左手坐标系。如果是这样的话,如果您导出带有旋转的游戏对象层次结构,则需要做更多的工作。 - Leo Bartkus
我排除了坐标系问题,因为如果是坐标系问题,结果应该是旋转而不是镜像。我实际上看到了那种情况,它会被镜像和旋转。这是同一个fbx文件在两种情况下使用,并且如果我将其拖动到Unity中,则可以正常加载。我认为这与Assimp处理加载的方式有关,我没有正确告诉它要做什么。 - Everts
显示剩余3条评论
1个回答

0

我没有以正确的方式从Assimp解决问题。

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

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

  • 获取顶点列表
  • 对于每个顶点,乘以旋转矩阵
  • 将数组分配给网格
  • 使用网格进行渲染

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接