AI 3D生成工具的快速迭代让模型生产从小时级压缩到分钟级,但不同工具默认导出的OBJ、GLB、PLY等格式在进入Blender、Maya、Unity或Unreal时,经常出现材质丢失、缩放异常、法线翻转、轴向错位等问题。这些问题大多不是文件损坏,而是中间格式对几何、材质、单位、坐标系的语义表达不一致。把转换过程脚本化,可以在不改变原工具习惯的前提下,让资产自动适配下游管线。

一、格式不兼容的根源是语义差异
OBJ格式简单直接,只保存顶点、面、法线和基础材质名称,但几乎不描述PBR金属度、粗糙度、透明度等现代渲染属性。GLB/glTF虽然完整定义了PBR材质、场景层级和动画,可传统DCC软件对它的导入支持参差不齐,尤其是一些老版本Maya或3ds Max会丢失材质节点。FBX是Autodesk的私有格式,在动画和骨骼方面表现稳定,但版本混乱、封闭性强,批处理时很难不依赖官方SDK。USD擅长场景组装、实例化和变体管理,影视行业用得很多,游戏引擎的支持也在快速补齐,但学习曲线较陡。
更隐蔽的问题是单位和坐标轴。例如 Tripo 默认以米为单位输出GLB,而部分工具导出OBJ时按厘米写入数值,进入Blender后模型尺寸可能放大100倍。Y-up和Z-up的差异同样致命,从Maya到Unreal如果不在导入时做轴向变换,模型会侧躺甚至法线反转。材质管线差异则更常见:AI生成的GLB往往带有baseColorTexture、metallicRoughnessTexture和normalTexture,但FBX导入后只剩一个空材质槽,贴图连接全部断裂。
这意味着解决格式不兼容不能只靠选择某一个万能格式,而是要在生产链路上确定一个稳定的中间格式,并在进入下游前用脚本统一单位、轴向和材质语义。中间格式承担翻译角色,脚本则负责消除方言差异。
二、中间格式选择:glTF、FBX、USD、Alembic 的边界
glTF/GLB 是目前最适合AI 3D工作流的开放中间格式。它以JSON描述场景结构,二进制缓冲存储几何和动画数据,PBR材质定义清晰,Unity、Unreal、Three.js、Babylon.js 都有一流支持。缺点是复杂骨骼动画和非网格数据支持有限,不过在AI生成静态模型或简单带动画的场景中已经足够。FBX 的优势在于传统DCC软件覆盖极广,Maya、3ds Max、MotionBuilder、Blender 都能稳定读写,适合后续要做手工绑定或动画编辑的情况。USD 的价值在于场景组装和团队协作,材质变体、实例引用、层级覆盖是其强项,适合影视和大型游戏项目。Alembic 主要用于几何缓存和动画交换,不适合材质编辑。
一个简单的选择策略如下:如果下游是实时引擎或Web,优先GLB;如果下游是传统DCC做动画,选FBX;如果是多资产场景和团队并行,上USD;如果只传递动画缓存,才考虑Alembic。OBJ可以作为临时交换,但不建议作为主要中间格式,因为它丢失太多语义信息。
| 格式 | 几何精度 | 材质兼容 | 动画支持 | 工具链覆盖 | 适用场景 |
|---|---|---|---|---|---|
| glTF/GLB | 高 | PBR完整 | 基础动画 | 实时引擎、Web | AI模型默认交换 |
| FBX | 高 | 一般 | 骨骼、变形 | 传统DCC软件 | 动画后续加工 |
| USD | 高 | 支持变体 | 复杂层级 | 影视、引擎逐步支持 | 场景组装与协作 |
| Alembic | 高 | 不支持材质编辑 | 几何缓存 | DCC与引擎 | 动画缓存交换 |
在实际管线中,并不需要死守单一格式。常见做法是AI工具导出GLB,经过脚本预处理后,根据下游需要分别导出FBX给动画师、导出USD给场景整合、导出GLB给引擎或Web。中间格式的选择取决于那一跳的语义损失是否可接受。
三、转换脚本实战:用 Trimesh 批量处理静态网格
Trimesh 是Python生态里处理三角网格非常顺手的库,支持OBJ、PLY、STL、GLB、GLTF等格式的读写,还内置了单位缩放、变换、法线修复等常用操作。下面的脚本可以扫描一个目录,把AI工具输出的原始文件统一缩放到米,修正坐标轴,再导出为GLB。
import trimesh
import os
import numpy as np
from pathlib import Path
INPUT_DIR = "raw_ai_output"
OUTPUT_DIR = "converted"
UNIT_SCALE = {
"cm": 0.01,
"mm": 0.001,
"m": 1.0,
}
def guess_unit(file_name):
if "tripo" in file_name.lower():
return "m"
return "cm"
def convert_file(src, dst_folder):
mesh = trimesh.load(src, force="mesh")
if isinstance(mesh, trimesh.Scene):
combined = mesh.dump(concatenate=True)
else:
combined = mesh
scale = UNIT_SCALE[guess_unit(os.path.basename(src))]
combined.apply_scale(scale)
rot = np.array([
[1, 0, 0, 0],
[0, 0, -1, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
])
combined.apply_transform(rot)
out_name = Path(src).stem + ".glb"
combined.export(os.path.join(dst_folder, out_name))
print("已转换", out_name)
def batch_convert():
os.makedirs(OUTPUT_DIR, exist_ok=True)
for root, _, files in os.walk(INPUT_DIR):
for f in files:
if f.lower().endswith((".obj", ".ply", ".stl", ".glb")):
full = os.path.join(root, f)
convert_file(full, OUTPUT_DIR)
if __name__ == "__main__":
batch_convert()
这个脚本的核心逻辑是先判断文件是否包含场景信息,如果是 Scene 对象就合并为一个网格,避免丢失实例变换。接着根据文件名猜测来源工具,应用对应的单位缩放。旋转矩阵把Y轴映射到Z轴,适用于从Blender、Maya到Unreal这类Z-up引擎的转换。导出GLB时,基础颜色和UV坐标通常能保留,但Metallic/Roughness贴图如果原始文件没有正确写入,可能会变成纯白或纯黑材质,需要在后续Blender环节补节点连接。
Trimesh对静态网格处理很好,但一旦涉及骨骼、蒙皮、材质节点或复杂FBX字段,它的能力就捉襟见肘。这时更可靠的方式是用Blender的Python接口做批量导入导出。Blender可以后台运行脚本,不打开界面,适合服务器或CI环境。下面的脚本批量导入GLB,做统一缩放后导出FBX。
import bpy
import sys
import os
input_dir = sys.argv[-2]
output_dir = sys.argv[-1]
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
for f in os.listdir(input_dir):
if f.endswith('.glb') or f.endswith('.gltf'):
path = os.path.join(input_dir, f)
bpy.ops.import_scene.gltf(filepath=path)
for obj in bpy.context.scene.objects:
if obj.type == 'MESH':
obj.scale = (0.01, 0.01, 0.01)
bpy.ops.object.transform_apply(scale=True)
out = os.path.join(output_dir, f.replace('.glb', '.fbx').replace('.gltf', '.fbx'))
bpy.ops.export_scene.fbx(filepath=out, use_selection=False)
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
运行方式为:blender --background --python convert_blender.py -- raw_ai_output converted_fbx。Blender导入GLB时会自动识别PBR材质并转换为Principled BSDF节点,导出FBX时虽然不能完全保留材质,但至少材质槽和基础色不会全部丢失。对于动画资产,这个流程还能保留骨骼和蒙皮信息,比纯Trimesh方案完整得多。
四、AI模型常见问题与自动修复
AI生成的模型普遍存在几个问题:三角面数量巨大,一次性生成几十万甚至上百万面,直接进引擎会拖慢渲染;法线方向不一致,部分面朝内导致光照异常;重复顶点多,边界不封闭;PBR贴图虽然存在,但UV通道可能重复或错乱。这些问题可以在转换脚本中顺手修复。
- 重复顶点和退化面:合并距离过近的顶点,删除零面积面。
- 法线翻转:根据相邻面朝向统一法线,或对封闭网格重新生成法线。
- 面数过高:使用二次误差度量简化到目标面数。
- 单位缩放:统一到米或厘米,避免下游二次调整。
- 坐标轴混乱:按来源工具应用90度旋转矩阵。
下面是一个修复脚本片段,可以在批量转换前调用。
def repair_mesh(path):
mesh = trimesh.load(path, force="mesh", process=False)
mesh.merge_vertices()
trimesh.repair.fix_normals(mesh)
mesh.update_faces(mesh.nondegenerate_faces())
if len(mesh.faces) > 200000:
mesh = mesh.simplify_quadric_decimation(200000)
return mesh
材质修复要复杂一些。GLB标准使用Metallic/Roughness工作流,而传统FBX或Blender早期材质多用Specular/Glossiness。Blender导入GLB时会自动创建Principled BSDF节点,但有时贴图连接会丢失。可以在Blender脚本里遍历材质节点,检查baseColorTexture、metallicRoughnessTexture和normalTexture是否存在,如果为空就手动创建Image Texture节点并连接。这个过程可以用Python实现,核心是找到Principled BSDF的输入端口,把对应贴图接到Base Color、Metallic、Roughness和Normal上。
UV通道错乱也不少见,尤其是AI工具生成的模型可能只有一套UV,却把AO、粗糙度、法线贴图全部塞进同一个UV空间,导致贴图重叠。如果源数据无法重新生成,可以在Blender里复制UV通道,再对每张贴图单独调整偏移和缩放。更彻底的办法是重新展UV,但那已经超出格式转换的范畴。
五、把转换脚本接入自动化流水线
格式转换不应该每次手动执行。一旦选定了中间格式和修复策略,就可以把脚本挂在AI生成工具的输出目录上,用文件监听或定时任务自动触发。例如Python的watchdog库可以监控文件夹变化,新文件落地后立即执行转换,并把结果拷贝到DCC工程目录或引擎资源目录。
对于服务器或团队协作场景,建议把Blender脚本做成命令行工具,配合CI系统跑批量任务。大量模型转换时注意内存占用,Blender脚本每次处理一个文件后要清空场景,否则内存会持续增长。也可以使用多进程并行处理,但Blender本身不建议多实例同时运行,最好用队列方式控制并发。
总结下来,AI 3D多工具协作的格式不兼容不是靠某一种格式能完全解决的,关键是选对中间格式,并用脚本把单位、轴向、材质语义统一在下游使用之前。先把静态网格的批量转换跑通,再逐步补充材质修复和动画链路,整个工作流会稳定很多。