Chapter 07

模型加载

GLTFLoader 是 Web 3D 的标准格式加载器;Draco 压缩减少传输体积;LOD 与 Instancing 优化大场景性能

1. 3D 格式对比

格式加载器特点推荐场景
GLTF / GLBGLTFLoaderWeb 原生标准,含材质/动画/骨骼首选,几乎所有场景
OBJOBJLoader简单纯几何,无动画无材质标准简单几何体导入
FBXFBXLoaderAutodesk 格式,含动画但体积大来自 Maya/3ds Max 的资产
STLSTLLoader3D 打印常用,纯几何无材质工程/CAD 模型

2. GLTFLoader 加载 .gltf / .glb

GLTF(GL Transmission Format)是 Khronos 组织的 Web 3D 传输标准,被誉为"3D 界的 JPEG"。.gltf 是 JSON 文本格式,.glb 是二进制单文件格式(推荐,体积更小)。

import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

// ── Promise 封装(推荐)──
function loadModel(url) {
  const loader = new GLTFLoader();
  return new Promise((resolve, reject) => {
    loader.load(
      url,
      (gltf) => resolve(gltf),
      (progress) => {
        const pct = (progress.loaded / progress.total * 100).toFixed(0);
        console.log(`加载进度: ${pct}%`);
      },
      (err) => reject(err)
    );
  });
}

// 使用
const gltf = await loadModel('/models/helmet.glb');

// gltf 对象包含:
// gltf.scene     — 根 Group(包含所有 Mesh)
// gltf.scenes    — 所有场景
// gltf.animations — AnimationClip 数组
// gltf.cameras   — 相机数组

scene.add(gltf.scene);

// 调整位置/旋转/缩放
gltf.scene.position.set(0, 0, 0);
gltf.scene.scale.setScalar(0.01); // 模型单位可能是 cm,缩放到 m

// 遍历模型中的所有 Mesh
gltf.scene.traverse((child) => {
  if (child.isMesh) {
    child.castShadow    = true;
    child.receiveShadow = true;

    // 修改材质
    child.material.envMapIntensity = 1.5;
  }
});

3. Draco 压缩解码器

Draco 是 Google 开发的 3D 网格压缩算法,可将 GLTF 模型体积压缩 80-90%。压缩后的模型加载时需要 Draco 解码器:

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';

// 1. 创建 DRACOLoader,指向解码器 WASM 文件路径
// 复制 node_modules/three/examples/jsm/libs/draco/ 到 public/draco/
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/draco/');
dracoLoader.preload(); // 预加载解码器(可选,提前加载)

// 2. 将 DRACOLoader 注入 GLTFLoader
const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader);

// 之后正常加载即可,压缩与非压缩模型都支持
const gltf = await new Promise((res, rej) =>
  gltfLoader.load('/models/compressed.glb', res, undefined, rej)
);

4. OBJLoader 与 MTL 材质文件

import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
import { MTLLoader } from 'three/addons/loaders/MTLLoader.js';

// 先加载材质,再加载 OBJ
const mtlLoader = new MTLLoader();
const materials = await new Promise((res) =>
  mtlLoader.load('/models/car.mtl', res)
);
materials.preload();

const objLoader = new OBJLoader();
objLoader.setMaterials(materials);
const object = await new Promise((res) =>
  objLoader.load('/models/car.obj', res)
);
scene.add(object);

5. 模型优化:Instancing(实例化渲染)

当需要渲染大量相同几何体时(如草地、树林、建筑群),使用 InstancedMesh 一次 Draw Call 渲染所有实例:

// 一次 Draw Call 渲染 1000 棵树
const count = 1000;
const treeGeo = new THREE.ConeGeometry(0.3, 1, 8);
const treeMat = new THREE.MeshStandardMaterial({ color: 0x228822 });

const instancedMesh = new THREE.InstancedMesh(treeGeo, treeMat, count);
instancedMesh.castShadow = true;

const dummy = new THREE.Object3D();
const matrix = new THREE.Matrix4();

for (let i = 0; i < count; i++) {
  // 随机位置
  dummy.position.set(
    (Math.random() - 0.5) * 100,
    0,
    (Math.random() - 0.5) * 100
  );
  // 随机旋转和缩放
  dummy.rotation.y = Math.random() * Math.PI * 2;
  dummy.scale.setScalar(0.5 + Math.random() * 1.5);
  dummy.updateMatrix();

  // 设置第 i 个实例的变换矩阵
  instancedMesh.setMatrixAt(i, dummy.matrix);
}
instancedMesh.instanceMatrix.needsUpdate = true; // 通知 GPU 更新
scene.add(instancedMesh);

6. LOD(细节层次)

LOD 根据相机距离自动切换高/中/低精度模型,兼顾近景质量和远景性能:

const lod = new THREE.LOD();

// 为不同距离设置不同精度的模型
const highDetail  = new THREE.Mesh(new THREE.SphereGeometry(1, 64, 32), mat);
const midDetail   = new THREE.Mesh(new THREE.SphereGeometry(1, 16, 8),  mat);
const lowDetail   = new THREE.Mesh(new THREE.SphereGeometry(1, 8,  4),  mat);

lod.addLevel(highDetail, 0);   // 0~5m 内用高精度
lod.addLevel(midDetail,  5);   // 5~15m 中精度
lod.addLevel(lowDetail, 15);   // 15m 外低精度

scene.add(lod);

// ⚠️ LOD 需要在渲染循环中更新
function animate() {
  requestAnimationFrame(animate);
  lod.update(camera); // 根据相机距离自动切换 Level
  renderer.render(scene, camera);
}

7. 免费 3D 资源推荐

ℹ️

模型优化工具
gltf-transform(gltf-transform.donmccurdy.com)— 命令行工具,压缩、优化、转换 GLTF
Three.js Editor(threejs.org/editor)— 在线预览和检查模型
Blender + GLTF 导出— 免费 3D 建模软件,导出带压缩的 GLB

本章小结:Web 3D 首选 GLTF/GLB 格式 + GLTFLoader;生产环境加上 Draco 压缩减少体积;大量相同物体用 InstancedMesh;远景用 LOD 降低多边形数。这三个优化手段能让复杂场景性能提升数倍。