Unity - 导出的FBX模型,无法将 vector4 保存在 uv 中(使用 Unity Mesh 保存即可)

博主为还原王者荣耀杨玉环皮肤头发效果抓模型,发现导出FBX后UV的zw分量无法保存。经分析FBX Exporter插件代码仍未解决,后通过查阅资料猜测FBX不能保存超4分量数据。验证发现Unity Mesh可保存,*.obj文件需DIY importer,处理后渲染效果正常。


目的

备忘,便于日后自己索引


问题

为了学习了解大厂项目的效果:
上周为了将 王者荣耀的 杨玉环 的某个皮肤的头发效果还原
所以我想直接抓模型,再还原 shader
我使用的还是以前的老方法: GPA + 夜神模拟器,具体可以查看以前的另一篇教程,具体参考:教你如何使用GPA导出模型,另送一个 GPA CSV2MESH Tool in unity
在这里插入图片描述

抓出来的数据,导出 FBX 后,我看不出什么异常

直到,我逐行的 shader 还原效果的时候
发现 vertex input 数据有 float4 uv1 : TEXCOORD1; float4 uv2 : TEXCOORD2;

但是发现 shader 调试发现,uv1, uv2 使用颜色输出都发现了数据不对的 BUG

然后我还想在 unity Game 视图下,使用 RenderDoc 抓帧分析一下
结果 Load RenderDoc 之后,直接导致 unity 闪退
在这里插入图片描述
在这里插入图片描述

瞄了一下 CSharp 代码,发现我使用的是 Mesh.uv API,getter and setter 都是 Vector2[] 的,所以 zw 是不可能设置上的
在这里插入图片描述

然后瞄了一下 Mesh 是有 void SetUVs(int channel, Vector4[] uvs) 的 API 的
在这里插入图片描述
但是经过测试,还是发现 UV的 zw 无法保存下来
在这里插入图片描述

最终我问了一下unity 技术官方,结果他们测试是OK的 (因为他们是对 Mesh 内存数据的实时修改)
然后我也试了一下,确实OK,但是经过自己跟进一步测试,发现使用 FBX Exporter 导出之后,UV 还是会丢失的
我将测试总结一下: unity Mesh 中会保存 uv vector4 的数据,到时经过 FBX Exporter 插件导出之后,uv 就不可能保存 Vector4 了
在这里插入图片描述

然后我分析了一下 FBX Exporter 插件的代码
发现一丢丢问题:

  1. 我将 FBX Exporter Local 化后,再按照我下面截图的内容,修改后,还是无法导出 (如何 local 化,可以参考我之前的文章:Unity - 如何修改一个 Package 或是如何将 Package Local化
    在这里插入图片描述

  2. 发现 Unity 中 AutoDesk 的 package 里面封装的 API FbxLayerElemetnUV.Create 进入是继承 UV2
    也要先 local,但是这个 package 比较特殊,在 PackageManager 中不显示的,方法可以是先从 Library/PackageCache/com.autodesk.fbx@4.2.0 剪贴到 [项目目录]/Packages/下面,然后使用 Package add from disk 的方式
    然后再开始修改代码
    public class FbxLayerElementUV : FbxLayerElementTemplateFbxVector2 修改为
    public class FbxLayerElementUV : FbxLayerElementTemplateFbxVector4
    结果发现还是不行

在这里插入图片描述
在这里插入图片描述

因为之前说的,unity editor 下,无论 game view, 还是 scene view
直接 Load RenderDoc 都会导致unity 闪退
然后我再使用 RenderDoc + 真机 抓帧分析,果然是没有 vertex input TEXCOORD0 zw 分量数据的
在这里插入图片描述


解决方案

于是我就有点怀疑 FBX 是不能保存 uv 超过4 分量数据的
然后百度: ‘fbx 文本 file header’ 找到这篇:

google ‘fbx ascii file header’ 找到:

经过前面 (还有很多篇)

看完 ascii 格式的 FBX 头文件后,我就知道,uv 存不了 vector4 了,那我就在猜

王者荣耀 也是使用 unity 开发的,难不成他们 TEXCOORD[N] 保存超过 2 个以上的分量数据都是使用 unity Mesh 的方式来保存的吗?


验证

  • 试一下 unity mesh 能否成功 - OK
  • 测试一下 *.obj 格式能否将 uv 保存超过 2 个分量以上的数据 - OK,但是AB打包可能不会打进去(目录中注意的部分会有讲到)

保存为 Unity Mesh 结果 - OK

先构建uv数据
在这里插入图片描述
然后设置数据
在这里插入图片描述
然后 shader 打印
在这里插入图片描述

之前的z是全黑色,w全白色,现在都有对应的强度了,OK,说明 unity mesh 还是OK的
想要了解 unity mesh 如何保存数据,我们可以将 AssetDatabase.CreateAsset 之后的 Mesh.asset 文件用文本编辑器打开,瞄一下就好啦
在这里插入图片描述


保存为 *.obj 文件结果 - not OK,但是可以 DIY importer

首先我们用 blender 简单整一个 cube,将 uv 展好,如下
在这里插入图片描述

然后导出 *.obj 放到 unity 里面瞄一下,如下图
在这里插入图片描述
然后我们直接给 obj 里面的 vt 增加 字段数据的分量,看看 unity 有否变化,然后发现是没有变化的
在这里插入图片描述

然后我们发现修改不了 原始的 .obj 里面在 library 下的 mesh cache 信息 (.fbx) 同样如此

比如下面的代码,我将问题写在注释了

        var assetObj = AssetDatabase.LoadAssetAtPath<Object>(assetPath); // assetObj == null
        var modelPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath); // modelPrefab == null
        var mesh_filter = modelPrefab.GetComponentInChildren<MeshFilter>(); // 所有导致 modelPrefab 出现空引用的 BUG

完整如下


public class AssetsImporterExt : AssetPostprocessor
{
   
   
    private void OnPreprocessModel()
    {
   
   
        var mi = assetImporter as ModelImporter;
        if (mi == null) return;

        // assetPath == "Assets/Test/Test_uv.obj"
        var assetPath = assetImporter.assetPath;
        var assetObj = AssetDatabase.LoadAssetAtPath<Object>(assetPath); // assetObj == null
        var modelPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath); // modelPrefab == null
        var mesh_filter = modelPrefab.GetComponentInChildren<MeshFilter>(); // 所有导致 modelPrefab 出现空引用的 BUG
        if (mesh_filter == null) return;
        var mesh = mesh_filter.sharedMesh;
        var uvs = new List<Vector4>();
        mesh.GetUVs(0, uvs);
        var ext = System.IO.Path.GetExtension(assetPath).ToLower();
        if (ext == ".obj")
        {
   
   
            var spliter = new string[] {
   
    " " };
            
### 如何在Unity导出Mesh Filter相关的资源或数据 在Unity中,`MeshFilter` 是一种组件,用于存储网格几何体的数据。要导出 `MeshFilter` 的相关内容,通常需要将其关联的 `Mesh` 数据提取并保存为外部文件格式(如 FBX 或 OBJ)。以下是关于如何实现这一过程的具体方法: #### 使用内置功能导出 Unity 提供了直接导出模型的功能。可以通过右键点击场景中的对象,在上下文菜单中选择 **Export Package** 将其打包成 `.unitypackage` 文件[^1]。然而,这种方式并不适合单独导出特定的 `Mesh` 资源。 另一种更灵活的方式是利用 Unity 编辑器脚本手动处理 `Mesh` 数据。可以编写自定义脚本来访问 `MeshFilter` 组件,并将其中的顶点、UV 和法线等信息写入到支持的文件格式中。 #### 自定义脚本导出 下面是一个简单的 C# 脚本示例,展示如何从 `MeshFilter` 获取网格数据并将它们保存为 OBJ 文件格式: ```csharp using UnityEngine; using System.IO; public class ExportMesh : MonoBehaviour { public void SaveMeshAsOBJ(Mesh mesh, string filePath) { using (StreamWriter writer = new StreamWriter(filePath)) { foreach (Vector3 vertex in mesh.vertices) { writer.WriteLine($"v {vertex.x} {vertex.y} {vertex.z}"); } foreach (Vector2 uv in mesh.uv) { writer.WriteLine($"vt {uv.x} {uv.y}"); } int[] triangles = mesh.triangles; for (int i = 0; i < triangles.Length; i += 3) { writer.WriteLine( $"f {triangles[i + 2] + 1}/{triangles[i + 2] + 1}" + $" {triangles[i + 1] + 1}/{triangles[i + 1] + 1}" + $" {triangles[i] + 1}/{triangles[i] + 1}" ); } } } private void OnGUI() { if (GUILayout.Button("Export Selected Object")) { GameObject selectedObject = Selection.activeGameObject; if (selectedObject != null && selectedObject.GetComponent<MeshFilter>() != null) { Mesh mesh = selectedObject.GetComponent<MeshFilter>().sharedMesh; string path = EditorUtility.SaveFilePanel("Save OBJ File", "", "untitled.obj", "obj"); if (!string.IsNullOrEmpty(path)) { SaveMeshAsOBJ(mesh, path); } } } } } ``` 上述代码实现了以下功能: - 访问选定游戏对象上的 `MeshFilter` 并获取其共享的 `Mesh` 数据。 - 将该 `Mesh` 的顶点位置 (`vertices`)、纹理坐标 (`uvs`) 及三角形索引 (`triangles`) 写入指定路径下的 OBJ 文件[^2]。 #### 处理 UV 扩展分量 如果希望扩展 UV 到更高维度(例如 Vector4),则需要注意某些文件格式可能不原生支持此特性。在这种情况下,可以选择将额外的信息编码至其他属性字段或者创建自定义解析逻辑来兼容这些需求。 #### 远程调试与验证 当完成本地测试后,可进一步通过远程连接工具确认导出效果是否满足预期。对于移动平台开发人员来说,这一步尤为重要。例如针对 iOS/Android 设备,可通过 USB 接口建立通信链路,并借助 Profiler 工具分析性能表现以及渲染状态[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值