目前,3D 模型的格式有成千上万种可供选择,但每一种格式都具有不同的目的、用途以及复杂性。 虽然 three.js 已经提供了多种导入工具, 但是选择正确的文件格式以及工作流程将可以节省很多时间,以及避免遭受很多挫折。某些格式难以使用,或者实时体验效率低下,或者目前尚未得到完全支持。
# ThreeJS 支持的模型格式
 推荐使用 glTF(gl 传输格式)。.GLB 和.GLTF 是这种格式的这两种不同版本, 都可以被很好地支持。由于 glTF 这种格式是专注于在程序运行时呈现三维物体的,所以它的传输效率非常高,且加载速度非常快。 功能方面则包括了网格、材质、纹理、皮肤、骨骼、变形目标、动画、灯光和摄像机。
 当 glTF 不可用的时候,诸如 FBX、OBJ 或者 COLLADA 等等其它广受欢迎的格式在 Three.js 中也是可以使用、并且定期维护的。
# glTF 加载器:GLTFLoader
 GLTFLoader 用于载入 glTF 2.0 资源的加载器。
# glTF 简介
 glTF(gl 传输格式)是一种开放格式的规范 (open format specification), 用于更高效地传输、加载 3D 内容。该类文件以 JSON(.gltf)格式或二进制(.glb)格式提供, 外部文件存储贴图(.jpg、.png)和额外的二进制数据(.bin)。一个 glTF 组件可传输一个或多个场景, 包括网格、材质、贴图、蒙皮、骨架、变形目标、动画、灯光以及摄像机。
# glTF 模型加载器
 ThreeJS 内置了 GLTF 模型加载器,用于加载 GLTF 模型资源。
GLTFLoader uses ImageBitmapLoader whenever possible. Be advised that image bitmaps are not automatically GC-collected when they are no longer referenced, and they require special handling during the disposal process. More information in the How to dispose of objects guide.
# DRACOLoader 模型解压器
 用于加载经过 Draco 压缩的图形库。
 Draco 是一个开源的库,主要用于压缩和解压缩三维模型及点云。 以客户端上解压缩为代价,显著减少压缩的图形。
 独立的 Draco 文件后缀为 .drc ,其中包含顶点坐标,法线,颜色和其他的属性, Draco 文件不包含材质,纹理,动画和节点结构 - 为了能使用这些特征,需要将 Draco 图形 嵌入到 GLTF 文件中。使用 glTF-Pipeline 可以将一个普通的 GLTF 文件转化为经过 Draco 压缩的 GLTF 文件。 当使用 Draco 压缩的 GLTF 模型时,GLTFLoader 内部会调用 DRACOLoader。
 推荐创建一个 DRACOLoader 实例并重用,可以有效避免重复创建加载多个解压器实例。
# ThreeJS 加载 GLTF 模型
 以下,使用 GLTFLoader 加载官网提供的模型数据,核心代码段如下,
| import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"; //GLTF 加载器 | |
| import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js"; | |
| //TODO: 添加光源 | |
| const light = new THREE.PointLight(0xffffff, 1000, 1000); | |
| light.position.set(15, 15, 15); | |
| scene.add(light); | |
| scene.background = new THREE.Color(0xbfe3dd); | |
| //TODO: 加载模型 | |
| const modelPath = "models/LittlestTokyo.glb"; | |
| const gltfLoader = new GLTFLoader(); | |
| const dracoLoader = new DRACOLoader(); | |
| dracoLoader.setDecoderPath("draco/"); | |
| gltfLoader.setDRACOLoader(dracoLoader); | |
| gltfLoader.load(modelPath, function (gltfData) { | |
| console.log("onload::", gltfData); | |
|   //TODO: 加载 glb 模型到场景中 | |
| const model = gltfData.scene; | |
| model.position.set(1, 1, 0); | |
| model.scale.set(0.01, 0.01, 0.01); | |
| scene.add(model); | |
| }); | 
# 模型动画:AnimationMixer
 在上述案例中,注意到该模型数据携带有一个动画,那么如何在 Web3D 页面中播放这个动画呢?
# AnimationMixer 模型动画混合器
 ThreeJS 提供了 AnimationMixer 模型动画混合器,是用于场景中特定对象的动画的播放器。当场景中的多个对象独立动画时,每个对象都可以使用同一个动画混合器。
 通过 clipAction 方法,可以传入预定义的模型动画参数,并可以获取到一个 AnimationAction 实例。
# AnimationAction 动画调度器
 AnimationAction 实例,用于调度存储在 AnimationClips 中的动画,通过调用其 play () 方法,可以主动唤起混合器激活动画。
# ThreeJS 播放模型动画
 修改上述加载 GLTF 模型的代码如下,即可播放模型动画,
| import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js"; //GLTF 加载器 | |
| import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js"; | |
| //TODO: 创建时钟 | |
| const clock = new THREE.Clock(); | |
| //TODO: 添加光源 | |
| const light = new THREE.PointLight(0xffffff, 1000, 1000); | |
| light.position.set(15, 15, 15); | |
| scene.add(light); | |
| scene.background = new THREE.Color(0xbfe3dd); | |
| //TODO: 声明动画混合器 | |
| let mixer = null; | |
| //TODO: 加载模型 | |
| const modelPath = "models/LittlestTokyo.glb"; | |
| const gltfLoader = new GLTFLoader(); | |
| const dracoLoader = new DRACOLoader(); | |
| dracoLoader.setDecoderPath("draco/"); | |
| gltfLoader.setDRACOLoader(dracoLoader); | |
| gltfLoader.load(modelPath, function (gltfData) { | |
| console.log("onload::", gltfData); | |
|   //TODO: 加载 glb 模型到场景中 | |
| const model = gltfData.scene; | |
| model.position.set(1, 1, 0); | |
| model.scale.set(0.01, 0.01, 0.01); | |
| scene.add(model); | |
|   //TODO: 激活动画 | |
| mixer = new THREE.AnimationMixer( model ); | |
| mixer.clipAction( gltfData.animations[ 0 ] ).play(); | |
| animate() | |
| }); | |
| //TODO: 修改 animate 渲染函数逻辑如下 | |
| //TODO: 渲染函数 | |
| function animate() { | |
| requestAnimationFrame(animate); | |
|   //TODO: 更新轨道控制器 | |
| orbitControls.update(); | |
|   //TODO: 更新模型动画 | |
| if(mixer){ | |
| const delta = clock.getDelta(); | |
| mixer.update(delta); | |
|   } | |
|   //TODO: 渲染 | |
| renderer.render(scene, camera); | |
| } | 
