前言
本篇文章就和大家来说一下相机轨道控制器的封装,本篇将详细展开说明,也会对前面俩篇文章进行补充说明。最近有接个外包,如果成功谈下来,之后文章更新频率就会下去,在这里和大家提前知会一下。
明确正交相机轨道控制器和透视相机轨道控制器的区别
首先我们先对之前所学的正交相机轨道和透视相机轨道做一个差异的对比:
旋转轨道
正交相机和透视相机的旋转轨道都是一样的,都是使用球坐标,让相机视点围绕目标点
做的旋转。
位移轨道
- 正交相机的位移轨道是
鼠标从canvas画布到近裁剪面
,再到世界坐标系的位移量的转换,最后这个位移量会同时作用于相机的目标点和视点。 - 透视相机的位移轨道是
鼠标从canvas画布到目标平面
,再到世界坐标系的位移量的转换,最后这个位移量会同时作用于相机的目标点和视点。
缩放轨道
- 正交相机的缩放轨道是通过对其
可视区域的宽高尺寸的缩放
实现的。 - 透视相机的缩放轨道是通过
相机视点在视线上的位移
实现的。
整体的原理就是这样,接下来就可以封装轨道控制器了。
封装 OrbitControls
接下来,我们根据上俩篇文章的知识封装一个自己的 OrbitControls 控制器
导入定义一些基础配置
1 | import { |
封装类
接下来我们开始封装我们自己的 OrbitControls 控制器
初始化类
首先我们定义一个 class,在原型上直接将默认的一些配置项和传递进来的配置项都给 this,这样在类中,我们可以直接通过 this.xxx 获取到这些属性。
1 | export default class OrbitControls { |
更新球坐标和世界坐标与本地坐标
初始化的时候,我们就需要先将世界坐标和球坐标进行更新了,这里我们需要明确一个概念,平移,缩放,旋转这些本质上都是向量的线性变化,什么意思呢,就是这一系列操作之后,都可以找到最后的向量,由这些过程依次执行得到的向量,累加起来得到。
1 | export default class OrbitControls { |
更新球坐标
这个方法使用当前相机的位置和目标点来更新球坐标对象spherical。
它首先克隆相机的位置向量,
然后减去目标点向量,
得到从目标点指向相机的向量,
最后用这个向量设置球坐标
1 | // 更新球坐标 |
更新相机的位置和视图矩阵。
这个方法用于更新相机的位置和视图矩阵。
首先,根据平移偏移量panOffset平移目标点和相机的位置。想象一下,当用户进行平移操作时,相机和目标点一起在三维空间中移动。
然后,根据球坐标对象spherical计算出相机的偏移量,并将其加到目标点上,得到相机的新位置。这样可以根据球坐标来调整相机的位置,实现旋转和缩放后的位置更新。
接着,让相机看向目标点,并更新相机的世界矩阵。这样可以确保相机始终朝着目标点进行拍摄。
最后,更新球坐标对象spherical,使其与相机的新位置相对应,并重置平移偏移量为零。
1 | update() { |
定义鼠标事件
接下来我们定义鼠标事件,这里就很好理解
鼠标按下事件
根据鼠标按下事件得到是平移还是旋转,左键旋转,右键平移
dragStart设置为当前按下鼠标的点
1 | // 鼠标按下事件 |
鼠标移动事件
移动时一直设置最终的位置值,根据状态判断是平移还是旋转,传递偏移量
1 | // 鼠标移动事件 |
鼠标抬起事件
1 | // 鼠标指针抬起,重置状态 |
滚轮滚动事件
这里处理缩放,deltaY<0就是向上滚轮,也就是缩小,
比如我们之前默认值设置的是0.95,
那就是滚一段就是缩放成0.95,
如果是向下,那就是1/0.95,
这个值是大于1的,也就是放大
1 | // 滚动事件 |
定义滚动缩放事件
我们先来说最简单的缩放逻辑
正交相机的缩放
我们说过正交相机没有近大远小的效果,所有物体的大小在屏幕上都是固定的。它将相机的zoom属性乘以缩放比例dollyScale,然后调用updateProjectionMatrix方法更新相机的投影矩阵。这样可以调整相机的视野大小,从而实现缩放效果。
1 | dollyOrthographicCamera(dollyScale) { |
透视相机的缩放
这个方法用于透视相机的缩放操作。它通过改变球坐标对象spherical的半径来实现缩放,将半径乘以缩放比例dollyScale。
1 | dollyPerspectiveCamera(dollyScale) { |
接下来我们解释一下,为什么这段代码可以成立
一、球坐标与透视相机的关系
球坐标通常由半径、水平角度(方位角)和垂直角度(仰角)组成。球坐标被用来表示相机相对于目标点的位置和方向。
对于透视相机,相机到目标点的距离在很大程度上影响了相机所看到的场景的大小和透视效果。当相机靠近目标点时,场景看起来会更大,物体显得更大,透视效果更强烈;当相机远离目标点时,场景看起来会更小,物体显得更小,透视效果减弱。
二、通过改变半径实现缩放的原理
当用户进行缩放操作时,传入一个缩放比例dollyScale。
代码this.spherical.radius *= dollyScale
;将球坐标的半径乘以这个缩放比例。如果dollyScale大于 1,半径增大,相机远离目标点,场景看起来会变小,实现了缩小效果;如果dollyScale小于 1,半径减小,相机靠近目标点,场景看起来会变大,实现了放大效果。
三、结合实际场景理解
想象你正在用一个放大镜观察一个物体。当你把放大镜远离物体时,你看到的物体变小了;当你把放大镜靠近物体时,你看到的物体变大了。在这个轨道控制器中,相机就像那个放大镜,目标点就是被观察的物体,而球坐标的半径就相当于放大镜与物体之间的距离。通过改变这个距离(半径),就可以实现对透视相机所观察到的场景的缩放效果。
定义左键旋转事件
这个方法用于处理相机的旋转操作。
它根据鼠标移动的距离和画布高度计算出在球坐标上的旋转角度增量deltaT和deltaP。
然后根据rotateDir的值确定是否在 x 轴和 y 轴上进行旋转,如果是,则相应地更新球坐标对象spherical的theta(水平旋转角度)和phi(垂直旋转角度)。
同时,对phi进行了限制,以防止相机翻转。最后调用update方法更新相机的位置和视图矩阵。
1 | rotate({ x, y }) { |
定义右键平移事件
正交相机平移方法
这个方法用于正交相机的平移操作。
首先计算出相机在水平和垂直方向上的尺寸,以及鼠标移动距离与画布尺寸的比例。
然后根据这些比例计算出相机在水平和垂直方向上的位移量。
接着根据screenSpacePanning的值确定相机平移的方向向量,
最后将位移量转换为世界坐标,并将其累加到panOffset中。
最后调用update方法更新相机的位置和视图矩阵。
1 | panOrthographicCamera({ x, y }) { |
透视相机平移方法
这个方法用于透视相机的平移操作。
首先计算出一些与相机和画布相关的参数,包括视线长度、视椎体垂直夹角的一半、目标平面的高度以及目标平面与画布的高度比。
然后根据鼠标移动的距离和这些参数计算出在目标平面上的位移量。
接着根据screenSpacePanning的值确定相机平移的方向向量,
最后将位移量转换为世界坐标,并将其累加到panOffset中。
最后调用update方法更新相机的位置和视图矩阵。
1 | panPerspectiveCamera({ x, y }) { |
暴露一个投影视图矩阵方法
这个方法用于获取相机的投影视图矩阵。它通过将相机的投影矩阵和世界逆矩阵相乘,得到投影视图矩阵,并返回这个矩阵。
1 | getPvMatrix() { |
整体代码
1 | import { |
实例化
1 | /* 透视相机 */ |
结语
本篇文章到这里就结束了,更多内容敬请期待,债见~