Three.js中有两种不同的相机类型:正交相机和透视相机。注意,Three.js还提供了一些非常特殊的相机,用于创建可以使用3D眼镜或VR设备查看的场景。本书我们不会再详细讨论这些相机,因为它们的工作方式与本章介绍的相机完全相同。如果你对这些相机感兴趣,Three.js提供了几个标准示例:
❑ Anaglyph效果 :https://threejs.org/examples/#webgl_effects_anaglyph
❑ 视差屏障 :https://threejs.org/examples/#webgl_effects_parallaxbarrier
❑ 立体效果 :https://threejs.org/examples/#webgl_effects_stereo
如果你想了解简单的VR相机,你可以使用THREE.StereoCamera创建并渲染并排的3D场景(标准立体效果),使用平行屏障(正如3DS所提供的),或者提供不同视图以不同颜色渲染的合成效果。另外Three.js还对WebVR标准提供了一些实验性的支持,许多浏览器都支持WebVR标准(更多信息参见https://webvr.info/developers/)。因此只需要少量代码即可使用WebVR创建VR体验。只需设置renderer.vr.enabled=true,Three.js会自动处理剩余的WebVR支持工作。Three.js网站提供了几个示例,演示了如何使用该属性以及Three.js对WebVR其他特性的支持:https://threejs.org/examples/。
本章我们将重点介绍标准的透视和正交相机。我们将通过观察一些示例来解释这些相机之间的区别。
本章配套示例中有一个chapter2/cameras。当你打开这个示例时,你会看到如图2.13所示的内容。
图2.13 透视相机视图
图2.13称为透视视图,是最自然的视图。透视相机模拟了真实世界的透视效果,对象距离相机越远,在渲染时就显得越小,与人类视角观察到的效果非常相似。如果我们将相机更改为Three.js支持的另一种类型——正交相机,你会在同一场景中看到如图2.14所示的视图:
图2.14 正交相机视图
使用正交相机,所有的立方体都被渲染成相同的大小,对象与相机之间的距离不会影响渲染效果。正交相机通常用于2D游戏,例如旧版的《文明》和《模拟城市4》,如图2.15所示。
图2.15 正交相机在《模拟城市4》游戏中的应用
我们先看一下透视相机THREE.PerspectiveCamera。在示例中,你可以设置一些属性来定义通过相机镜头所看到的内容:
❑ fov:视野(Field of View,FOV)是从相机位置看到场景的部分。人类的视野能达到180度,而一些鸟类甚至达到了360度。但由于普通的计算机屏幕并不能完全填满我们的视野,所以常常选择较小的值。在游戏中通常会选择60到90度的视野范围。推荐默认值:50。
❑ aspect:aspect属性表示渲染输出区域的水平尺寸和垂直尺寸之间的比例。在示例中,由于我们使用整个窗口来渲染输出,因此直接使用窗口的宽高比作为aspect属性的值。aspect属性决定了水平视野和垂直视野之间的差异。推荐默认值:window.innerWidth /window.innerHeight。
❑ near:near属性定义了相机距离场景的最近距离。通常我们会将near属性设置为一个很小的值,以便直接从相机位置渲染场景中的所有内容。推荐默认值:0.1。
❑ far:far属性定义了相机能够看到的最远距离,即相机距离场景的最远距离。如果将far值设置得太低,会导致场景中距离相机较远的部分无法被渲染,从而出现部分场景内容被裁剪的情况;如果将far值设置得太高,虽然可以渲染出更远的场景内容,但可能会影响渲染性能。推荐默认值:100。
❑ zoom:通过调整zoom属性,用户可以灵活控制场景的放大或缩小,以获得所需的视角和渲染效果。使用小于1的值会缩小场景,使用大于1的值则会放大场景。注意:如果你指定一个负值,则场景将被上下颠倒渲染。推荐默认值:1。
图2.16清楚地展示了这些属性如何共同作用来决定你看到的内容。
图2.16 透视相机的属性
相机的fov属性决定了水平视野。相机的aspect属性决定了垂直视野。相机的near属性决定了近裁剪面的位置,far属性决定了远裁剪面的位置。在近裁剪面和远裁剪面之间的区域的渲染如图2.17所示。
图2.17 相机的近裁剪面和远裁剪面对渲染场景的影响
我们需要使用不同的属性来配置正交相机。由于正交相机会将所有对象渲染为相同大小,因此它不关心宽高比或场景的视野。正交相机定义了一个立方体区域,该区域内的对象将被渲染,而超出该区域的对象将不会被渲染。因此正交相机需要以下属性:
❑ left:定义了正交相机立方体区域左侧边界。如果你将这个值设置为-100,则位于相机左侧100单位距离之外的对象将不会被渲染。
❑ right:right属性的工作方式类似于left属性,但这次是在屏幕的另一侧。超出右侧的任何对象都不会被渲染。
❑ top:这是要渲染的顶部位置。
❑ bottom:这是要渲染的底部位置。
❑ near:基于相机的位置,近裁剪面以内的场景将被渲染。
❑ far:基于相机的位置,远裁剪面以内的场景将被渲染。
❑ zoom:通过它你可以对场景进行缩放。使用小于1的值会缩小场景,使用大于1的值则会放大场景。注意,如果你指定一个负值,场景将被上下颠倒渲染。默认值为1。
这些属性共同定义了相机渲染的场景范围,如图2.18所示。
图2.18 正交相机的属性
就像透视相机一样,你可以精确地定义要渲染的场景区域,如图2.19所示。
图2.19 正交相机的裁剪区域
至此我们讲述了Three.js支持的各种相机。你已经学会了如何对它们进行配置,并且可以使用它们的属性来渲染场景的不同部分。不过我们还没有讲述如何控制相机查看场景的哪一部分。我们将在2.3.2节中讲述这一块内容。
到目前为止,你已经了解了如何创建相机以及相机各种属性的含义。第1章提到过,你需要将相机看向场景中的某个位置。通常,相机看向场景的中心:位置为(0, 0, 0)。然而,我们可以很容易地改变相机需要看向的坐标点:
你可以在chapter2/cameras示例指定相机需要看向的坐标点,如图2.20所示。注意,在OrthographicCamera设置中更改lookAt并不会改变立方体的尺寸。
图2.20 更改正交相机的lookAt属性
lookAt函数既可以将相机对准场景中的特定位置,也可以将相机跟随场景中的一个对象移动。你只需要将lookAt函数指向具体网格对象的position属性即可,因为每个THREE.Mesh对象都有position属性(值为一个THREE.Vector3对象)。你只需要一行代码:camera.lookAt(mesh.position)。通过在每一帧渲染之前调用该函数,相机的视角会实时更新为所跟随对象的位置。
在配置相机时,通过菜单调整参数通常是有用的,然而,在某些情况下,可能需要直观地看到相机视角实际覆盖的场景范围,以便更准确地调整相机的配置。Three.js允许你通过可视化相机的视锥体(相机所显示的区域)来实现这一点。为了实现这一点,我们只需向场景中添加一个额外的相机和一个相机辅助对象。要查看实际效果,请打开chapter-2/debug-camera.html示例,如图2.21所示。
图2.21 显示相机的视锥体
在上图中,你可以看到透视相机视锥体的轮廓。如果你在菜单中更改属性,则还可以看到视锥体也会随之变化。你可以通过以下方式可视化这个视锥体:
我们还添加了一个 switchCamera 按钮,通过它你可以在外部相机(俯瞰场景)和场景中的主相机之间进行切换。这是一个很好的确定相机正确设置的方法,如图2.22所示。
在Three.js切换相机非常简单,你只需要告诉Three.js你想通过不同的相机渲染场景即可。
图2.22 切换相机