镜像网格和错误的 UV 贴图运行时导出
Mirrored mesh and wrong UV map runtime export
编辑:在与 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 class,不是 Unity Mesh class,应用序列化是 Windows UWP 中构建的应用程序:
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 不适合这个?
我没有通过 Assimp 以正确的方式解决问题。
我们使用的基本解决方案是对对象变换中翻转的轴进行负缩放。
更合适的解决方案是将所有顶点提供给 Unity 端的矩阵,以便正确解析顶点的位置。
- 获取顶点列表
- foreach顶点与旋转矩阵相乘
- 将数组分配给网格
- 使用网格渲染
编辑:在与 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 class,不是 Unity Mesh class,应用序列化是 Windows UWP 中构建的应用程序:
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 不适合这个?
我没有通过 Assimp 以正确的方式解决问题。
我们使用的基本解决方案是对对象变换中翻转的轴进行负缩放。
更合适的解决方案是将所有顶点提供给 Unity 端的矩阵,以便正确解析顶点的位置。
- 获取顶点列表
- foreach顶点与旋转矩阵相乘
- 将数组分配给网格
- 使用网格渲染