第 6 章:vtkRenderer 与 vtkCamera

学习目标

本章把“场景中有什么”和“从哪里看”分开。读完后,你应该能添加可见对象、取得活动相机、手动设置视角,理解重置相机与重置裁剪范围的区别,并能说明透视投影与平行投影各适合观察什么。

6.1 Renderer 管理可见内容

在第 2 阶段示例里,<code>vtkGenericRenderWindow</code> 已经创建了一个 Renderer:

const renderer = genericRenderWindow.getRenderer();

随后两条 Source 管线各自生成球体和立方体,两个 Actor 被加入同一个 Renderer:

renderer.addActor(sphereActor);
renderer.addActor(cubeActor);
renderer.resetCamera();
renderWindow.render();

<code>renderer.addActor(…)</code> 是表面对象常用的加入方式。后面学习体渲染时,你会使用 <code>renderer.addVolume(…)</code>。Renderer 还管理背景、灯光以及活动相机;本示例的背景由 Generic 对象在初始化时设置。

一个 Actor 有没有被加入 Renderer,与它是否已经持有 Mapper 是两件事。Actor 即使有正确数据和颜色,若没有加入正在显示的 Renderer,也不会出现在这幅画面中。

6.2 相机由三个向量描述主要视角

取得当前相机:

const camera = renderer.getActiveCamera();

先抓住三个基本量:

相机从 <code>position</code> 朝 <code>focalPoint</code> 看;<code>viewUp</code> 决定画面怎样旋转。若你只改相机位置而忘记焦点,可能得到一个意外的观察方向。若 <code>viewUp</code> 与观察方向几乎平行,视角也可能不稳定。

本示例中的“斜视图”按钮显式设置这三个量:

camera.setPosition(4, 3, 6);
camera.setFocalPoint(0, 0, 0);
camera.setViewUp(0, 1, 0);
renderer.resetCameraClippingRange();
renderWindow.render();

你已经熟悉 Three.js 相机,因此这里的重点是这些操作作用于 Renderer 的活动相机,而不是作用于 Actor 的几何数据。

6.3 resetCamera 与 resetCameraClippingRange

这两个方法名字相近,但解决的问题不同。

<code>renderer.resetCamera()</code> 根据当前可见对象的边界重新取景,调整焦点、相机位置与显示尺度,使内容进入视野。它适合在添加或移除对象后调用,也适合提供“回到完整数据”按钮。应先把要观察的 Actor 加入 Renderer,再调用它。

<code>renderer.resetCameraClippingRange()</code> 主要重新计算相机的近、远裁剪范围。它不负责把相机重新对准对象。手动移动相机之后,若模型的一部分突然被近裁剪面或远裁剪面切掉,可以考虑调用它。

操作 主要作用 典型时机
<code>resetCamera()</code> 重新取景,使可见对象进入画面 初次加入对象、点击“显示全部”
<code>resetCameraClippingRange()</code> 更新近、远裁剪范围 手动改相机后出现裁剪问题

不要把 <code>resetCamera()</code> 当成每次鼠标操作后的例行调用。它会覆盖用户刚调好的视角。默认交互样式本身会管理常规相机操作及所需渲染。

6.4 透视与平行投影

透视投影具有近大远小的效果,适合直观观察三维形状。平行投影中,物体尺寸不随与相机的距离按透视规律缩小,在医学影像、工程图和对齐比较中经常有用。

本示例提供了切换按钮:

camera.setParallelProjection(!camera.getParallelProjection());
renderer.resetCameraClippingRange();
renderWindow.render();

平行投影下,<code>parallelScale</code> 控制视图的纵向尺度;它不是“镜头焦距”。<code>resetCamera()</code> 会基于可见对象重新设置合适的显示尺度。如果切换投影后画面显得过大或过小,先检查当前相机尺度与位置,再决定是否重置相机。

注意:切换投影不会更改 Source、Mapper 或 Actor。球和立方体仍是同一份数据,只是投影到屏幕的方法变了。

6.5 Renderer 的边界与相机的边界

可以把 Renderer 理解成“这幅画面要显示哪些对象,并用哪个相机看它们”,但它不是数据处理器。裁剪数据、生成等值面或修改点上的物理值,不应通过 Camera 完成。

同样,<code>actor.setPosition(…)</code> 与 <code>camera.setPosition(…)</code> 在屏幕上都可能让物体左右移动,却表达不同的操作:

这一点在同屏显示多个数据集时尤其重要。若只想移动一个对象,不应移动相机;若想从另一侧观察全部对象,不应逐个移动 Actor。

6.6 排查视角问题的顺序

画面里找不到对象时,先确认它已加入 Renderer,再看相机:

  1. <code>renderer.getActors()</code> 是否包含预期 Actor?
  2. 添加对象后是否调用过 <code>resetCamera()</code>?
  3. 相机的 <code>position</code> 与 <code>focalPoint</code> 是否合理?
  4. 是否只是裁剪范围太窄?必要时调用 <code>resetCameraClippingRange()</code>。
  5. 对象是否被设置为不可见,或尺寸远小于当前观察范围?

第 2 阶段示例下方实时显示相机位置、焦点与投影方式。你可以先点“斜视图”,再用鼠标旋转,观察这些数值如何变化。

本章小结

Renderer 管理画面中的对象与活动相机。Camera 决定观察位置、目标方向和投影方式。<code>resetCamera()</code> 用于重新取景,<code>resetCameraClippingRange()</code> 主要处理近远裁剪。下一章说明鼠标事件怎样经过 Interactor 和交互样式改变相机。