拖一个 .glb 进工程,直接实例化就能跑——但 imported 目录里其实躺着好几份产物:一份节点树、若干 mesh 资源、可能还有动画库。理解这套结构的意义在于做拆分决策:多人共用的模型资产怎么组织,动画从哪来,以及为什么官方推荐工作流是"DCC 里导出 glTF,别折腾 FBX"。
两条解析路径,一个终点
glTF:一等公民。 modules/gltf 是引擎内置模块,纯 C++ 解析器,无外部依赖,.gltf(JSON + 外部 bin)和 .glb(二进制打包)都直接读。它是官方推荐管线的原因很实际:规范开放、字段映射干净(节点/网格/蒙皮/动画都有标准表达)、没有版本碎片化问题。
FBX:兼容义务。 FBX 是 Autodesk 的私有格式,闭源、版本多、语义曲折。Godot 4.3 之前靠外部转换器(FBX2glTF)先把 FBX 转成 glTF 再进管线,多一个进程就多一批"转换后和 DCC 里不一样"的归因难题;4.3 起换成内置的 ufbx 库直接解析,路径短了,但格式的天然复杂度还在——嵌套的变换枢纽(pivot)、多套单位制、非标准轴向,都是翻译损耗的高发区。能出 glTF 就出自 DCC,FBX 只留给拿不到 glTF 的旧资产。
两条路径的终点是同一个中间结构:ImporterScene(节点树骨架)+ ImporterMesh(网格数据)。ImporterMesh 是导入期的中间态——它带着 LOD 数组、各 surface 的顶点数据、材质槽,导入完成后被编译成运行时的 ArrayMesh 存进 .godot/imported/ 的产物文件。
产物结构:一份树,多份资源
导入一个角色模型的产物布局:
角色.glb
├── 节点树(Node3D 层级:骨架、MeshInstance3D、碰撞)
├── ImporterMesh → 编译为 ArrayMesh(每个 mesh 资源独立寻址)
├── 材质(内嵌 glTF 材质的翻译结果)
└── 动画(AnimationClip 集,可指定进 AnimationLibrary)
运行时 instantiate 这份场景,节点树直接激活,MeshInstance3D 通过 mesh 属性引用共享的 mesh 资源。拆分策略的核心问题就在这:mesh 数据要不要从场景里提出来单独存?
- 一个模型只在一个场景用:默认即可,mesh 随场景走,无决策成本。
- 多个场景共享同一模型(同一张桌子摆二十个房间):把 mesh 提出来(导入面板 "Extract" 相关选项,或按路径手动保存为
.res),场景里用load()引用同一份。共享的是顶点数据,实例间的差异只在 Transform——配合 MultiMesh(GPU 实例化那篇)走得更远。 - 只要网格不要场景(程序化生成关卡、自定义组装):导入模式从 "Scene" 切到只取 mesh 资源。
动画是独立的拆分维度:导入时可指定把动画导出为 AnimationLibrary 资源——同一套攻击动画给不同骨架的角色复用(重定向那篇的现实),就靠库层共享、骨骼层映射。
三个默认就踩的坑
单位。 glTF 规范是米,FBX 常见厘米(且 FBX 文件里自己记着单位)。Godot 导入时按源单位换算到引擎的米制——通常正确,但 DCC 导出设置和单位实际不符时(Blender 场景单位调成 0.01 之类),模型进来大一百倍或矮一半。判断方法:拖一个 1.8 m 的胶囊体对比,比例不对先查 DCC 导出设置,别在导入面板乱调 scale 掩盖病因。
坐标系。 glTF 是 Y-up、右手系,Godot 是 Y-up、左手系。导入时做 Z 翻转换算,glTF 的 +Z 前进变成引擎的 -Z 前进。绝大多数情况翻译正确,代价是"模型在 DCC 里朝 +Z,进了引擎朝 -Z"的认知差——给模型定朝向约定时(比如角色朝向),以引擎里为准反推 DCC。
材质。 glTF 的 metallic-roughness 工作流翻译最顺(PBR 参数一一对应);FBX 常见 specular-glossiness 材质,转换要近似,金属度贴图经常整个翻译丢。导入后第一件事是检查材质球的 metallic 通道——FBX 进来的模型材质发灰发白,八成是这张图的翻译问题,手动接回 ORM 通道即可。
导入期的决策清单
模型进工程按这个顺序过一遍:先定拆分(场景独享 / mesh 提取共享 / 纯 AnimationLibrary),再核对三个坑(单位、朝向、材质金属度),最后看一眼 LOD——ImporterMesh 保留了 DCC 导出的 LOD 链,导入设置里确认 LOD 数量和切换距离,别等性能那篇的 profiler 报 polycount 超标再回来补。