第 2 章:从 Three.js 转换到 vtk.js
学习目标
本章使用 Three.js 经验帮助你定位 vtk.js 对象,但不要求记住一张机械的“类名翻译表”。读完后,你应该能指出为什么一个 vtk.js 可见对象通常涉及 Data、Mapper、Actor 和 Property,及其与 Three.js <code>Mesh</code> 的边界。
2.1 先看粗略对应
| Three.js 中常见的概念 | vtk.js 中相关的对象 | 类比的边界 |
|---|---|---|
| <code>BufferGeometry</code> | <code>vtkPolyData</code> | 后者除了点和多边形,还可包含线、顶点单元以及关联数据 |
| <code>BufferAttribute</code> | <code>vtkDataArray</code> | 数组通常还要挂到 Point Data 或 Cell Data 上 |
| <code>Mesh</code> | <code>vtkActor</code> + <code>vtkMapper</code> + <code>vtkProperty</code> | 显示对象、数据映射和外观被拆开 |
| <code>Scene</code> | <code>vtkRenderer</code> | Renderer 同时管理相机和相关渲染状态 |
| <code>WebGLRenderer</code> | <code>vtkRenderWindow</code> 与具体视图 | vtk.js 将窗口组织与浏览器绘制后端分层 |
| <code>OrbitControls</code> | <code>vtkRenderWindowInteractor</code> + 交互样式 | 输入事件被转成相机等操作 |
这张表只用于入门。它无法解释体数据、过滤器和科学数组;也不意味着这些对象的 API 可以按 Three.js 的方式直接替换。
2.2 为什么 Mesh 被拆成多个对象
在 Three.js 中,Mesh 把几何体和材质放到一起,再作为场景对象参与渲染。vtk.js 的表面渲染通常拆成以下层次:
vtkPolyData ──> vtkMapper ──> vtkActor ──> vtkRenderer
数据 映射 可见对象 场景与相机
<code>vtkPolyData</code> 回答“点、线、面及其数值是什么”;<code>vtkMapper</code> 回答“这份数据怎样用于绘制”;<code>vtkActor</code> 回答“这个结果放在哪里、是否显示”;<code>actor.getProperty()</code> 则保存颜色、不透明度、线框等部分外观设置。
例如,改变 <code>actor.setPosition(…)</code> 移动的是场景中的表示,不要求重新生成 <code>vtkPolyData</code> 的点坐标。改变 Source 的半径则会改变生成的数据。这种区分让你能准确判断一个操作应作用于哪一层。
Mapper 不是 Material。 它除了参与渲染,还连接数据管线,并可选择标量数组和颜色映射。把它只当作材质,会让后面的按值着色和体渲染 API 显得莫名其妙。
2.3 PolyData 与 ImageData 是两种不同的数据组织方式
<code>vtkPolyData</code> 适合显式的点及连接关系。一个三角形要有三个点坐标,还要记录这三个点构成同一个单元。它可以用于表面,也可以只包含点或线。
<code>vtkImageData</code> 适合规则的二维或三维采样网格。它不必逐个存储每个采样点的坐标,而是保存:
- 每个轴上的采样点数 <code>dimensions</code>
- 网格起点 <code>origin</code>
- 各轴的采样间距 <code>spacing</code>
- 网格方向 <code>direction</code>
- 一个或多个数据数组
对于没有旋转的简单情形,下标 <code>(i, j, k)</code> 的位置可理解为 <code>origin + (i, j, k) × spacing</code>。一般情形还要应用 <code>direction</code>;因此不能把所有 <code>vtkImageData</code> 都当成永远与世界坐标轴对齐。第 11 章会用小网格逐个验证这些量。
请特别区分数据类型与显示方式:<code>vtkImageData</code> 不等于“只能做体渲染”。它也能显示切片,或通过算法产生等值面的三角形数据。
2.4 数组的归属比数组的形状更重要
在 Three.js 中,你可能习惯说“每个顶点一个颜色”。vtk.js 还会让你明确选择:
- Point Data:每个点对应一个值或一个多分量元组。
- Cell Data:每个单元对应一个值或元组。
- Field Data:不直接按点或单元对应的附加信息。
若两个相邻三角形共享点,点数据能够在公共点上保持连续;若每个三角形各有一个单元值,两块区域可以出现明显边界。这里的关键不是数组能否塞进 GPU,而是数据的物理意义和显示解释。第 8–10 章会专门说明 <code>vtkDataArray</code>、Point Data 和 Cell Data。
2.5 从“搭场景”转换成“追数据”
阅读 vtk.js 示例时,推荐按下面顺序提问:
- 输入数据从 Reader、Source 还是手工构造而来?
- 输出是什么数据类型,有哪些数值数组?
- 中间经过哪些 Filter?
- 哪个 Mapper 接收结果?它显示哪个数组?
- 哪个 Actor 或 Volume 将结果加入 Renderer?
这一顺序有助于排查“显示出来了但颜色不对”“能看到切片却看不到体渲染”等问题。若从 Actor 开始找,往往会漏掉真正发生问题的数据层。
本章小结
Three.js 经验可以帮助你理解相机、场景、表面和 GPU 渲染,但 vtk.js 的主线是数据对象及其处理关系。下一章会把数据流与场景中的对象关系分开画出来,解释为何代码里同时存在 <code>setInputConnection</code>、<code>setMapper</code> 和 <code>addActor</code>。