第 12 章:vtkMapper——从数据到可绘制的表面

学习目标

本章沿着第 9–10 章建立的 vtkPolyData 继续走向屏幕。读完后,你应该能选择 setInputData 或 setInputConnection,解释 Mapper、Actor 各自负责什么,并能检查表面不显示或颜色不符合预期时究竟是哪一层出了问题。

12.1 把 Mapper 放回两条管线

在 Three.js 中,你通常把几何和材质一起交给 Mesh。vtk.js 则把“数据从哪里来”与“这个数据如何成为可绘制对象”分开:

vtkPolyData ──> vtkMapper ──> vtkActor ──> vtkRenderer
   数据          映射与选择      场景对象       视图

vtkMapper 面向表面数据。它接收 vtkPolyData,为渲染后端准备几何,并决定是否根据数据中的标量数组给表面着色。它本身没有放置位置,也不会自行加入场景。vtkActor 持有 Mapper,提供变换、可见性及 vtkProperty。第 13 章专门解释后两者。

这里的“映射”含有两层意思:将数据集转换为可绘制的图元,以及将选中的标量经颜色表转换为颜色。不要把它理解为只负责颜色的工具。

12.2 已有数据:setInputData

第 4 阶段示例手工构造了 81 个点、128 个三角形的 vtkPolyData。将这个已经存在的数据对象交给 Mapper:

const mapper = vtkMapper.newInstance();
mapper.setInputData(surface);

这意味着 Mapper 的输入就是 surface。你可以用 mapper.getInputData() 检查当前数据。接着建立场景对象:

const actor = vtkActor.newInstance();
actor.setMapper(mapper);
renderer.addActor(actor);
renderer.resetCamera();
renderWindow.render();

上面三行解决的是不同的问题:setMapper 连接 Actor 与 Mapper;addActor 让 Renderer 管理它;resetCamera 调整相机以看见数据。只有第一行不会使东西自动出现在画布上。第 4 阶段示例的完整代码在这里。

12.3 算法输出:setInputConnection

若数据由 Source 或 Filter 计算出来,常用的是输出端口连接:

const sphereSource = vtkSphereSource.newInstance({ radius: 1 });
const mapper = vtkMapper.newInstance();
mapper.setInputConnection(sphereSource.getOutputPort());

两种输入方式的要点如下:

情况 连接方式 后续变化
手工创建、已经持有 vtkPolyData setInputData(polyData) 数据对象变化后由修改时间通知管线
来自 Source 或 Filter setInputConnection(algorithm.getOutputPort()) 保留算法之间的连接;上游参数变化时可重新计算输出

例如修改 sphereSource.setRadius(1.5) 后,下一次管线更新能够从 Source 取得新球体。若先取出一次输出,再把那一刻的数据交给 setInputData,就不能把这一步当作与 Source 的持续连接。复杂的 Source → Filter → Mapper 管线将在第 24–25 章展开。

12.4 一个 Mapper 可以选择哪些数据

第 10 章展示了 Point Data 与 Cell Data。Mapper 必须决定采用哪一种关联,以及是否启用标量着色:

mapper.setScalarVisibility(true);
mapper.setScalarModeToUsePointData();
mapper.setScalarRange(0, 100);

setScalarModeToUsePointData() 使用点属性集合中的标量;setScalarModeToUseCellData() 使用单元属性集合。默认标量模式会先找活动点标量,再找活动单元标量,但显式指定能让示例更容易读。若有多个点数组,需要按名称挑选,第 14 章会解释 setScalarModeToUsePointFieldData() 与 setColorByArrayName()。

setScalarVisibility(false) 会关闭按数组着色。这时可以观察 Actor 的单色外观。它不会从数据集中删除数组;重新打开后,Mapper 仍可使用原来的数据。

12.5 “数据变了”与“画面变了”

Mapper 的输入、标量模式、颜色范围改变后,通常还需要调用 renderWindow.render() 让画面更新。若直接写入 vtkDataArray.getData() 返回的 TypedArray,应按第 8 章说明调用数组的 dataChange(),以更新数值范围缓存与修改状态。只调用 render() 并不会自动发现底层内存被直接改写。

反过来,旋转 Actor 或更改它的不透明度,并没有改变 vtkPolyData 的点和单元。排错时先问“我改的是数据、Mapper 还是 Actor”,能避免在错误对象上寻找原因。

12.6 常见故障定位

现象 首先检查
完全没有表面 Mapper 是否有输入;Actor 是否设置 Mapper 并加入 Renderer;相机是否已取景
有表面但颜色单一 getScalarVisibility();数据集是否有选中的标量;标量模式是否匹配数组关联
改了上游算法却没见变化 是否用 setInputConnection 保留了端口连接;是否重新渲染
数值全挤在颜色表一端 getScalarRange() 与数组真实的 getRange() 是否相配

Mapper 不保证输入数据一定有效。若 vtkCellArray 中有越界点 ID,或 Point Data 元组数与点数不符,应先回到第 9–10 章修正数据结构。

本章小结

vtkMapper 是数据管线与可见场景之间的桥。setInputData 接收现成数据,setInputConnection 保留上游算法连接;标量设置决定数据值如何参与表面着色。下一章把 Mapper 包装成真正可摆放、可改变外观的 Actor。