​ 目前,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);
}