基于 ThreeJS 的 DTE 库使用 -

三葉Leaves Author

本文是我在某中厂任职时的学习笔记,用到 DTE 库是基于 ThreeJS 实现的闭源的公司内部库,替代方案和说明可见 DTE 库的开源替代方案(ThreeJS 等)

我们上一课学习的 ShaderMesh,展示的是某一特定时刻模型上的数据分布。这就像一张照片,记录了瞬间的状态。

但很多仿真分析,比如流体流动、结构应力变化、热量传导,都是一个随时间变化的动态过程。我们需要看的不是一张照片,而是一部电影

这部“电影”就是由一系列连续的“照片”组成的。每一张“照片”,我们称之为一帧(Frame)FrameMesh 就是DTE引擎中专门用来播放这部“数据电影”的播放器。

FrameMesh 的工作原理

FrameMesh 的逻辑是在 ShaderMesh 的基础上再加一个维度——时间

它需要两样核心素材:

  1. 基础模型几何(Base Geometry):mesh.json

    • 这个和我们上一课用的 JsonLoader 加载的 meshData 是一回事。它定义了模型的静态形状。电影的“演员”和“场景”是不会变的。

    • 这个文件通常比较大,因为它包含了所有的顶点坐标。

  2. 帧数据序列(Frame Data Sequence):0.json, 1.json, 2.json, …

    • 这些是纯数据文件。它们只包含每一帧时,模型各个顶点上的物理量数值(比如 Velocity 的值)。它们不包含模型的形状信息,因为形状在 mesh.json 里已经定义过了。所以这些文件通常很小,加载速度快。

    • 变的是演员的“表情”和“动作”(数据)。

FrameMesh 的工作流程就是:

  1. 先加载好基础的模型形状 meshData。

  2. 再去加载一连串的帧数据 frameData。

  3. 当你命令它播放时(或跳转到某一帧时),它会:

    • 取出那一帧的数据。

    • 把这些数据“喂”给底层的着色器(Shader)。

    • 像 ShaderMesh 一样,根据数据和色带,实时计算出模型表面的颜色,并渲染出来。

因为这个过程切换数据非常快,我们肉眼看去,就形成了流畅的动画效果。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// my-script.js

// --- 之前的代码 ---
const containerDiv = document.getElementById('my-3d-container');
const container3D = new CommonContainer({ domId: containerDiv });
const { scene, camera, renderer } = container3D;
camera.position.set(0, 20, 50); // 调整一个合适的观察角度

// --- 本课新代码 ---

const loader = new JsonLoader();

// --- 任务1: 加载基础模型几何 ---
const p1 = new Promise((resolve, reject) => {
// 假设模型文件在 frame1/mesh.json
loader.loadDataMesh(`frame1/mesh.json`, (meshData) => {
console.log("基础模型几何 (meshData) 加载成功!");
resolve(meshData);
});
});

// --- 任务2: 加载帧数据序列 ---
// 准备一个包含所有帧数据文件URL的数组
const frameUrls = [];
// 假设有51帧,从 0.json 到 50.json
for (let i = 0; i <= 50; i++) {
frameUrls.push(`frame1/${i}.json`);
}

const p2 = new Promise((resolve, reject) => {
loader.loadCustomFrame(frameUrls, (frameData) => {
console.log(`共 ${frameData.length} 帧数据 (frameData) 加载成功!`);
resolve(frameData);
});
});


// --- 等待两个任务都完成后,再进行组装 ---
Promise.all([p1, p2]).then(([meshData, frameData]) => {

// 此时,meshData 和 frameData 都已经拿到了!
console.log("所有资源加载完毕,开始创建 FrameMesh。");

// 和 ShaderMesh 一样,我们要指定根据哪个属性来渲染
const attrName = 'Velocity'; // 假设还是用速度
const userData = meshData.userData; // 从基础模型里获取元数据

// 1. 创建 FrameMesh 实例
const frameMesh = new FrameMesh(meshData, frameData, {
name: attrName,
minValue: userData[attrName].MinValue, // 注意文档里是大写的 M
maxValue: userData[attrName].MaxValue, // 注意文档里是大写的 M
});

// 2. 添加到场景
scene.add(frameMesh);
console.log("FrameMesh 已添加到场景!");

// --- 现在,我们可以控制播放了! ---

// 比如,让它跳转到第 10 帧
frameMesh.goto(10);
console.log("已跳转到第 10 帧。");

// 你可以创建一个播放器UI,或者用 setInterval 来实现自动播放
let currentFrame = 0;
const totalFrames = frameData.length;

setInterval(() => {
frameMesh.goto(currentFrame);
currentFrame = (currentFrame + 1) % totalFrames; // 循环播放
}, 100); // 每100毫秒切换一帧,即 10 FPS

});

代码解读与重点:

  • Promise.all([p1, p2]): 这是一个非常重要的JavaScript技巧。它会等待数组里所有的Promise (p1和p2) 都完成后,才会执行 .then()。这确保了我们不会在模型还没加载完的时候就去尝试创建 FrameMesh。

  • frameData: 这是一个数组,数组的每一项都代表一帧的数据。frameData[10] 就包含了第10帧时所有顶点的速度值。

  • new FrameMesh(…): 它的构造函数需要三个核心参数:meshData(演员长啥样),frameData(演员的每一步剧本),以及一个配置对象(告诉他按哪个剧本演,以及表演的夸张程度min/max)。

  • frameMesh.goto(v): 这是核心控制方法,就像遥控器的“跳转”按钮。你给它一个帧号,它立刻把模型的显示更新到那一帧的状态。

  • frameMesh.addFrame(…): 文档里还提到了这个方法。它允许你在FrameMesh创建之后,动态地给它追加更多的帧数据。比如,你可以先加载前100帧,播放起来,然后在后台继续加载101-200帧,加载完再addFrame()进去,实现“边下边播”的效果。

本课小结

  • 我们理解了静态云图(ShaderMesh)动态云图(FrameMesh)的区别:后者增加了一个时间维度

  • 我们掌握了FrameMesh的工作流程:分离基础几何帧数据,先加载,后组装。

  • 我们掌握了FrameMesh的核心API:goto(frameIndex)用于播放控制,addFrame()用于动态追加数据。

  • 标题: 基于 ThreeJS 的 DTE 库使用 -
  • 作者: 三葉Leaves
  • 创建于 : 2025-07-31 00:00:00
  • 更新于 : 2025-08-13 16:31:27
  • 链接: https://blog.oksanye.com/c9cc56dc1227/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论