附加格式加载器
本文档介绍 Three.js 中除 glTF 外的各种 3D 资源格式加载器。这些加载器支持从传统和专用格式导入模型、场景和点云。关于 glTF 导入/导出(推荐的现代格式),请参阅 GLTF 导入与导出。
附加格式加载器位于 examples/jsm/loaders/,支持从支持动画的场景格式(FBX、Collada)到简单网格格式(OBJ、STL)再到点云数据(PCD、PLY、VTK)等多种格式。
格式类别与能力
Three.js 支持跨多个格式类别的加载器,每个类别具有不同的能力:
| 格式 | 加载器类 | 几何体 | 材质 | 纹理 | 动画 | 灯光/相机 | 说明 |
|---|---|---|---|---|---|---|---|
| FBX | FBXLoader | ✓ | ✓ | ✓ | ✓ | ✓ | 完整场景格式,行业标准 |
| Collada (.dae) | ColladaLoader | ✓ | ✓ | ✓ | ✓ | ✓ | 基于 XML,Khronos 标准 |
| VRML (.wrl) | VRMLLoader | ✓ | ✓ | ✓ | ✗ | ✓ | Web3D 传统格式 |
| OBJ | OBJLoader | ✓ | 通过 MTL | 通过 MTL | ✗ | ✗ | 简单网格格式 |
| STL | STLLoader | ✓ | ✗ | ✗ | ✗ | ✗ | CAD/3D 打印 |
| PLY | PLYLoader | ✓ | 顶点颜色 | ✗ | ✗ | ✗ | 点云/网格 |
| 3MF | ThreeMFLoader | ✓ | ✓ | ✓ | ✗ | ✗ | 3D 打印标准 |
| AMF | AMFLoader | ✓ | ✓ | ✗ | ✗ | ✗ | 3D 打印(XML) |
| PCD | PCDLoader | 点 | 顶点颜色 | ✗ | ✗ | ✗ | 点云数据 |
| VTK | VTKLoader | ✓ | 顶点颜色 | ✗ | ✗ | ✗ | 科学可视化 |
| NRRD | NRRDLoader | 体数据 | ✗ | ✗ | ✗ | ✗ | 医学成像体素 |
| XYZ | XYZLoader | 点 | ✓ | ✗ | ✗ | ✗ | 简单点云 |
| KMZ | KMZLoader | ✓ | ✓ | ✓ | ✓ | ✓ | 压缩 Collada |
FBXLoader 架构
FBXLoader 是最全面的附加格式加载器,支持带动画、材质、纹理、灯光、相机和骨骼动画的完整场景层次结构。它处理 7.0+ 版本的 ASCII 和二进制 FBX 格式。
FBX 解析管线
FBX 解析管线:load() → parse() → FBXTreeParser
加载器使用 FileLoader 将原始数据作为 ArrayBuffer 加载,然后用 isFbxFormatBinary() 和 isFbxFormatASCII() 辅助函数确定格式。解析器构建包含所有 FBX 节点的全局 fbxTree 对象,FBXTreeParser 然后通过其解析方法顺序处理。最终输出存储在类型为 Group 的全局 sceneGraph 变量中。
FBX 对象类型与 Three.js 映射
FBX 格式在 fbxTree.Objects.Model 中包含多种对象类型,FBXTreeParser.parseModels() 根据 node.attrType 字段将其转换为 Three.js 对象:
FBX node.attrType → Three.js 对象映射
FBX 材质系统
fbxTree.Objects.Material 中的 FBX 材质具有 ShadingModel 属性,决定 Three.js 材质类型。parseMaterial() 方法提取材质参数,parseParameters() 方法处理纹理连接:
FBX 材质解析:materialNode.ShadingModel → MeshLambertMaterial/MeshPhongMaterial
FBX 变形器与骨骼动画
fbxTree.Objects.Deformer 中的 FBX 变形器由 parseDeformers() 处理,返回包含 skeletons 和 morphTargets 字典的结构。deformerNode.attrType 决定变形器类型:
FBX 变形器处理:parseDeformers() → bindSkeleton()
FBX 动画系统
FBX 动画由 AnimationParser.parse() 解析,从 fbxTree.Objects.AnimationCurveNode 和相关节点中的动画数据构建 AnimationClip 对象。解析器使用 parseAnimStack()、parseAnimationLayers() 和 parseAnimationCurveNodes() 构建动画层次结构:
FBX 动画管线:AnimationParser.parse() → AnimationClip
OBJLoader 与 MTLLoader
OBJ 格式是简单的 ASCII 网格格式,存储顶点位置、法线、UV 坐标和面定义。材质在单独的 MTL 文件中定义。
OBJ 解析状态机
OBJLoader.parse() 使用 ParserState 对象累积顶点数据并构建对象。解析器逐行读取,根据行前缀分配到状态方法:
OBJ 逐行解析器:ParserState 方法
MTLLoader 材质创建
MTLLoader.parse() 逐行读取 MTL,构建 materialsInfo 字典。它返回 MaterialCreator 实例,在调用 create(materialName) 时惰性创建材质:
MTL 解析:MaterialCreator.create() → MeshPhongMaterial
ColladaLoader 架构
Collada(.dae)是基于 XML 的格式,支持带动画、运动学和物理的完整场景图。ColladaLoader 是最复杂的加载器之一。
Collada 解析管线
ColladaLoader.parse() 使用 DOMParser 解析 XML,然后为每个库部分调用 parseLibrary() 和 buildLibrary() 等辅助函数。结果包括 scene(Group)、animations 数组和 kinematics 对象:
Collada XML 处理:parseLibrary() → buildLibrary() → 结果
Collada 库系统
Collada 将内容组织到库中,独立解析然后交叉引用:
| 库 | XML 元素 | 解析函数 | Three.js 输出 |
|---|---|---|---|
| Geometries | <library_geometries> | parseGeometry() | BufferGeometry |
| Materials | <library_materials> | parseMaterial() | MeshLambertMaterial / MeshPhongMaterial |
| Effects | <library_effects> | parseEffect() | 材质参数 |
| Images | <library_images> | parseImage() | Texture |
| Animations | <library_animations> | parseAnimation() | AnimationClip |
| Cameras | <library_cameras> | parseCamera() | PerspectiveCamera / OrthographicCamera |
| Lights | <library_lights> | parseLight() | DirectionalLight / PointLight / SpotLight |
| Visual Scenes | <library_visual_scenes> | parseVisualScene() | Group 层次结构 |
点云加载器
Three.js 为点云数据格式提供多个加载器,每个返回包含位置和可选颜色/法线属性的 BufferGeometry 的 Points 对象。
点云格式比较
点云加载器都返回包含顶点位置和可选颜色/法线属性的 BufferGeometry 的 Points 对象:
点云加载器:parse() → Points
PCD 头部与数据解析
PCDLoader.parse() 首先调用 parseHeader() 提取 PCD 元数据,然后使用 PCDheader.data 确定解析路径。_getDataView() 辅助函数从二进制缓冲区读取类型化数据:
PCD 数据提取:parseHeader() → 解析 ASCII/二进制/压缩
3D 打印格式加载器
多个加载器面向 3D 打印工作流,具有不同程度的材质支持。
3D 打印格式加载器
三个加载器面向复杂度不同的 3D 打印工作流:
3D 打印格式能力
| 格式 | 加载器 | 几何体 | 材质 | 纹理 | 输出 |
|---|---|---|---|---|---|
| STL | STLLoader | 三角网格 | ✗ | ✗ | BufferGeometry(无材质) |
| 3MF | 3MFLoader | 三角网格 | ✓(PBR) | ✓ | 带 MeshStandardMaterial 的 Group |
| AMF | AMFLoader | 三角网格 | ✓(基础) | ✗ | 带 MeshPhongMaterial 的 Group |
STL 格式:STLLoader.parse() 用 isBinary() 检测 ASCII 与二进制,然后调用 parseASCII() 或 parseBinary()。二进制 STL 可能在属性字节计数字段中包含“Magics”颜色数据。
3MF 格式:3MFLoader.parse() 使用 JSZip 提取 3D/3dmodel.model XML 文件,解析资源(basematerials、texture2d、objects),并用材质数组构建网格。支持 pbmetallicdisplayproperties 用于 PBR 金属/粗糙度。
AMF 格式:AMFLoader.parse() 处理纯 XML 和 ZIP 压缩的 AMF 文件。解析 <object> → <mesh> → <vertices> 和 <volume> → <triangle> 结构及可选的 <material> 定义。
3MF 材质与纹理系统
3MFLoader 解析 XML 资源并用 buildBasematerialsMeshStandardSet() 或 buildMaterialsMeshPhong() 构建材质。buildTexture() 方法从 ZIP 归档加载纹理:
3MF 资源处理:XML 资源 → 材质数组
VRMLLoader 场景图
VRMLLoader 解析 VRML 2.0 文件,使用带节点和字段的层次场景图:
VRML 节点类型
VRMLLoader.parse() 使用 Chevrotain 解析器库将 VRML 2.0 语法词法和解析为 AST(抽象语法树)。parseTree() 函数遍历 AST 并根据 node.name 为每个节点调用 buildNode():
VRML 解析:Chevrotain → AST → buildNode() → 场景
VTKLoader 科学可视化
VTKLoader 支持用于科学和医学数据可视化的 VTK(Visualization Toolkit)格式。它处理 ASCII 和二进制 POLYDATA:
VTK POLYDATA 结构
VTKLoader.parse() 根据文件内容调用 parseASCII() 或 parseBinary()。解析器使用状态机变量(inPointsSection、inPolygonsSection 等)跟踪正在读取的部分:
VTK 基于部分的解析:parseASCII/parseBinary() → BufferGeometry
NRRDLoader 医学成像
NRRDLoader 加载常用于医学成像和科学体可视化的 NRRD(Nearly Raw Raster Data)格式文件。与网格加载器不同,它返回包含 3D 体素数据的 Volume 对象而非表面几何体。
NRRD 格式结构
NRRD 文件由文本头部和原始二进制体数据组成。头部指定维度、数据类型、编码和空间变换:
NRRD 头部字段
| 字段 | 描述 | 示例 |
|---|---|---|
| type | 数据类型(uint8、int16、float 等) | type: unsigned char |
| dimension | 维度数 | dimension: 3 |
| sizes | 每维的大小 | sizes: 256 256 128 |
| encoding | 数据编码(raw、gzip、bzip2) | encoding: gzip |
| endian | 字节序(little、big) | endian: little |
| space directions | 空间变换向量 | space directions: (1,0,0) (0,1,0) (0,0,1) |
NRRD 解析管线
NRRDLoader.parse() 将文件分割为头部和数据部分,用 _fieldFunctions 中的字段特定函数解析头部,然后根据编码类型解码数据:
NRRD 解析:parseHeader() → 解压缩 → Volume 对象
Volume 对象与坐标系
Volume 类存储 3D 体素数据并提供访问值和提取 2D 切片的方法。它维护两个坐标系:
IJK 坐标系:体数据数组的整数索引(0 到 xLength-1 等)
RAS 坐标系:由 space directions 头部定义的真实世界空间坐标(右-前-上)
volume.matrix 属性从 IJK 变换到 RAS 坐标,volume.extractSlice(axis, index) 返回用于渲染的 VolumeSlice 对象:
// Volume 结构
class Volume {
xLength: number; // IJK 中的宽度
yLength: number; // IJK 中的高度
zLength: number; // IJK 中的深度
data: TypedArray; // 体素值
spacing: Vector3; // 物理间距
matrix: Matrix4; // IJK 到 RAS 变换
getData(i, j, k): number;
extractSlice(axis, index): VolumeSlice;
}VolumeSlice 渲染
VolumeSlice 将体的 2D 横截面表示为带动态生成纹理的 Mesh。updateGeometry() 方法在 RAS 空间中定位切片平面,repaint() 提取体素数据到画布纹理:
VolumeSlice 架构:画布纹理 → PlaneGeometry 网格
NRRD 使用示例
import { NRRDLoader } from 'three/addons/loaders/NRRDLoader.js';
const loader = new NRRDLoader();
const volume = await loader.loadAsync('brain.nrrd');
// 创建穿过体的切片
const sliceZ = volume.extractSlice('z', Math.floor(volume.zLength / 2));
scene.add(sliceZ.mesh);
// 访问体素数据
const value = volume.getData(128, 128, 64); // IJK 坐标
console.log(`体素值在 (128,128,64): ${value}`);
// Volume 属性
console.log(`Volume 维度: ${volume.xLength} x ${volume.yLength} x ${volume.zLength}`);
console.log(`RAS 维度: ${volume.RASDimensions.join(' x ')}`);通用加载器模式
所有 Three.js 加载器遵循继承自 Loader 基类的通用架构模式:
加载器基类集成
所有加载器扩展 Loader 基类,提供 setPath()、setResourcePath()、setCrossOrigin() 和 setRequestHeader() 等通用功能。典型模式为:
通用加载器模式:load() → FileLoader → parse()
坐标系转换
许多格式使用不同的坐标系(Z-up 与 Y-up)。加载器通过变换处理:
| 格式 | 原生坐标系 | 转换策略 |
|---|---|---|
| FBX | Y-up(原生) | 无需转换 |
| Collada | Z-up 或 Y-up(在 <up_axis> 中指定) | 如 Z-up 则应用 90° X 旋转 |
| OBJ | Y-up | 无需转换 |
| VRML | Y-up | 无需转换 |
| STL | Z-up(约定) | 建议手动旋转:object.rotation.set(-Math.PI/2, 0, 0) |
| 3MF | Z-up(3D 打印标准) | 建议手动旋转 |
材质默认处理
当材质缺失或不支持时,加载器使用默认材质:
// FBXLoader 默认材质
const material = new MeshPhongMaterial({
name: Loader.DEFAULT_MATERIAL_NAME,
color: 0xcccccc
});
// OBJLoader 默认材质
const material = new MeshPhongMaterial();
// STLLoader - 仅几何体,无默认材质
// 应用程序必须提供材质使用示例
加载带动画的 FBX
import { FBXLoader } from 'three/addons/loaders/FBXLoader.js';
const loader = new FBXLoader();
const fbx = await loader.loadAsync('model.fbx');
scene.add(fbx);
// 访问动画
if (fbx.animations && fbx.animations.length > 0) {
const mixer = new THREE.AnimationMixer(fbx);
const action = mixer.clipAction(fbx.animations[0]);
action.play();
}加载带 MTL 材质的 OBJ
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
import { MTLLoader } from 'three/addons/loaders/MTLLoader.js';
// 先加载材质
const mtlLoader = new MTLLoader();
const materials = await mtlLoader.loadAsync('model.mtl');
materials.preload();
// 用材质加载 OBJ
const objLoader = new OBJLoader();
objLoader.setMaterials(materials);
const object = await objLoader.loadAsync('model.obj');
scene.add(object);加载点云数据
import { PCDLoader } from 'three/addons/loaders/PCDLoader.js';
const loader = new PCDLoader();
const points = await loader.loadAsync('pointcloud.pcd');
// Points 对象包含带位置和颜色的几何体
points.geometry.center();
points.geometry.rotateX(Math.PI); // Z-up 转 Y-up
scene.add(points);加载器能力与局限性
格式特定局限性
FBXLoader:
- 需要 FBX 版本 >= 7.0(二进制为 6400+)
- 不支持变形法线 examples/jsm/loaders/FBXLoader.js58-61
- 仅支持 Lambert 和 Phong 材质 examples/jsm/loaders/FBXLoader.js536-548
- 正交相机转换为 Object3D 占位符 examples/jsm/loaders/FBXLoader.js1137-1140
ColladaLoader:
- 仅支持完整 Collada 规范的子集 examples/jsm/loaders/ColladaLoader.js44-48
- 仅实现矩阵动画变换类型 examples/jsm/loaders/ColladaLoader.js516-527
- 坐标系变化时顶点数据不转换 examples/jsm/loaders/ColladaLoader.js48-51
VRMLLoader:
- 无动画支持
- 许多节点类型未实现(Inline、LOD、Switch、Script 等) examples/jsm/loaders/VRMLLoader.js745-782
STLLoader:
- 无材质支持(仅几何体)
- 二进制格式可能有字节序问题 examples/jsm/loaders/STLLoader.js16-19
- “Magics”颜色格式仅适用于二进制 examples/jsm/loaders/STLLoader.js17-18
VTKLoader:
- 仅支持 POLYDATA 数据集格式 examples/jsm/loaders/VTKLoader.js12-16
- 不支持其他格式(结构化点、结构化网格等)
NRRDLoader:
- 不支持 Bzip2 压缩 examples/jsm/loaders/NRRDLoader.js230-234
- 返回
Volume对象,非标准BufferGeometryexamples/jsm/loaders/NRRDLoader.js95 - 渲染需要
VolumeSliceexamples/jsm/misc/VolumeSlice.js18-27
性能考量
- 二进制格式(二进制 FBX、二进制 STL、二进制 PLY)比 ASCII 加载更快
- 压缩格式(3MF、KMZ、压缩 PCD)需要通过 fflate 进行解压缩开销
- 大点云(PCD、PLY、XYZ)可创建非常大的几何体 - 考虑 LOD 或剔除
- 复杂场景(FBX、Collada)含多个对象应使用场景图遍历优化