Skip to content

场景序列化与加载

目的与范围

本文档介绍 Three.js 的原生 JSON 序列化与反序列化系统,用于场景图,能够保存和加载完整的 3D 场景,包括对象层次结构、几何体、材质、纹理、动画和骨骼装备。该系统主要通过各类的 toJSON() 方法和 ObjectLoader 类实现。

有关加载 GLTF 或 FBX 等外部 3D 文件格式的信息,请参阅 GLTF 导入与导出附加格式加载器。有关运行时几何体生成的信息,请参阅 程序化几何体生成


序列化架构

toJSON 模式

Three.js 在其核心类中采用一致的 toJSON(meta) 模式。每个可序列化类实现此方法以将其状态转换为 JSON 兼容对象。meta 参数是序列化过程中累积所有引用资产的共享对象,以避免重复。

具有 toJSON 的关键类:

  • Object3D - 场景图节点与变换
  • BufferGeometry - 顶点数据与属性
  • Material - 表面属性与着色器参数
  • Texture - 图像引用与采样参数
  • AnimationClip - 关键帧动画数据
  • Skeleton - 用于蒙皮的骨骼层次结构
SVG
100%

JSON 场景格式

元数据结构

每个序列化场景包含一个描述格式版本和生成器的元数据头:

字段类型描述
versionnumber格式版本(4.7)
typestring场景为 "Object",单个资产为 "Geometry"、"Material" 等
generatorstring标识序列化类(例如 "Object3D.toJSON")

根结构

完整的场景序列化生成分层 JSON 结构:

SVG
100%

Object3D 序列化

每个 Object3D 序列化以下属性:

属性条件描述
uuid总是对象引用的唯一标识符
type总是类名(例如 "Mesh"、"Scene"、"Group")
name非空时用户指定名称
matrix总是表示局部变换的 16 元素数组
up总是上向量(默认 [0,1,0])
pivot设置时旋转/缩放的枢轴点
castShadow为 true 时对象是否投射阴影
receiveShadow为 true 时对象是否接收阴影
visible为 false 时可见性标志
frustumCulled为 false 时剔除标志
renderOrder非零时自定义渲染排序
userData非空时用户定义数据字典
layers总是层掩码值
matrixAutoUpdate为 false 时自动更新标志
children有子节点时子对象数据数组

特殊对象类型

InstancedMesh

序列化实例特定数据:

{
  "type": "InstancedMesh",
  "count": number,
  "instanceMatrix": { ... },
  "instanceColor": { ... } // 可选
}

BatchedMesh

序列化批处理渲染数据,包括绘制范围、几何体信息和实例信息:

Scene

序列化场景特定属性,如背景、环境、雾和光照强度:

BufferGeometry 序列化

几何体序列化顶点数据和属性:

属性描述
uuid唯一标识符
type"BufferGeometry" 或子类
name可选名称
userData用户数据
data含索引、属性、变形属性、组的属性数据

Material 序列化

材质序列化所有与着色器相关的属性、混合模式以及纹理引用(通过 UUID):

Texture 序列化

纹理序列化采样参数并引用其图像源:

属性描述
uuid唯一标识符
name可选名称
imagemeta.images 中的图像 UUID
mapping纹理坐标映射类型
channelUV 通道索引
offset, repeat变换参数
wrap环绕模式(S, T)
format, type数据格式
minFilter, magFilter采样滤波器

ObjectLoader 管线

加载流程

ObjectLoader 类通过多阶段管线编排反序列化:

SVG
100%

解析器方法

每种资产类型都有专用的解析器方法:

方法用途依赖项
parseShapes()为拉伸重构 Shape 对象
parseAnimations()重构 AnimationClip 对象
parseGeometries()使用 BufferGeometryLoader 重构几何体Shapes
parseImages()通过 ImageLoader 加载图像或反序列化数据
parseTextures()重构纹理Images
parseMaterials()通过 MaterialLoader 重构材质Textures
parseObject()递归重构场景图上述全部
parseSkeletons()重构 Skeleton 对象对象树(用于骨骼查找)

资产引用管理

基于 UUID 的去重

Three.js 使用 UUID 确保每个资产仅序列化一次,即使被多次引用。meta 对象在序列化期间充当共享缓存:

SVG
100%

辅助函数模式:

序列化使用 serialize() 辅助函数检查现有条目:

图像处理

图像可通过两种方式序列化:

  1. URL 引用:指向外部图像文件的字符串 URL
  2. Data URL 或类型化数组:嵌入的图像数据

反序列化期间,ImageLoader 处理 URL 加载,而数据数组直接重构:

反序列化过程

几何体重构

parseGeometries() 方法使用 BufferGeometryLoader 重新创建几何体实例:

SVG
100%

材质重构

parseMaterials() 方法使用 MaterialLoader,处理材质特定属性:

对象层次结构重构

parseObject() 方法递归构建场景图:

SVG
100%

骨骼绑定

整个对象树重构后,必须将骨骼绑定到其骨头:

  1. 解析骨骼:从 JSON 创建 Skeleton 实例,在对象树中查找骨骼引用
  2. 绑定骨骼:通过 UUID 引用将每个 SkinnedMesh 与其骨骼关联
  3. 绑定光源目标:解析光源目标引用(用于方向光/聚光灯)

纹理与图像加载

同步与异步解析

ObjectLoader 提供同步(parse())和异步(parseAsync())方法。关键区别在于图像加载:

  • 同步:使用带回调的 LoadingManager,图像在后台加载
  • 异步:使用 awaitImageLoader.loadAsync(),在返回前等待所有图像

同步路径:

异步路径:

纹理解析

parseTextures() 方法创建纹理实例并配置采样参数:

SVG
100%

使用示例

序列化场景

// 假设 'scene' 是包含网格、灯光等的 Scene 对象
const json = scene.toJSON();

// 结果包含所有按 UUID 去重的资产
console.log(json.geometries); // { uuid1: {...}, uuid2: {...} }
console.log(json.materials);  // { uuid1: {...}, uuid2: {...} }
console.log(json.object);     // 带子节点的根场景对象

加载场景

const loader = new ObjectLoader();

// 异步加载
const scene = await loader.loadAsync('path/to/scene.json');

// 或使用回调
loader.load('path/to/scene.json', (object) => {
  scene.add(object);
});

实现细节

类型系统集成

序列化系统依赖类型标志在反序列化期间区分对象类:

  • isObject3DisBufferGeometryisMaterialisTexture 标志用于类型检查
  • type 字符串属性用于精确类标识(例如 "Mesh"、"PerspectiveCamera")

矩阵序列化

变换序列化为表示 4x4 矩阵的 16 元素数组:

object.matrix = [
  m11, m12, m13, m14,
  m21, m22, m23, m24,
  m31, m32, m33, m34,
  m41, m42, m43, m44
];

反序列化期间,使用 fromArray() 重构矩阵:

BufferAttribute 序列化

顶点属性以类型化数组数据序列化:

{
  "itemSize": 3,
  "type": "Float32Array",
  "array": [x1, y1, z1, x2, y2, z2, ...],
  "normalized": false
}

局限性与注意事项

  1. Shader 材质:自定义 ShaderMaterialRawShaderMaterial 序列化 uniforms 但不序列化 JavaScript 函数(例如 onBeforeCompile
  2. 节点材质:原生 JSON 格式不支持 WebGPU 节点材质;改用 NodeMaterialLoader
  3. 外部引用:外部图像/资源的 URL 在加载时必须可访问
  4. 函数引用userData 对象不能包含函数;它们将无法正确序列化
  5. 大型几何体:将大型顶点缓冲区作为 JSON 数组嵌入效率低下;生产环境考虑使用 GLTF 等二进制格式