过年之前领了一个小需求,觉得比较有意思,所以来分享一下。
需求是这样的,在一个threejs场景中有n个点,目前只知道这n个点的坐标位置。求计算一个合适的相机位置和朝向,使用相机刚好能看到它们。「有兴趣的同学可以先思考一下」
首先看一下我们目前的有的信息
- 点的坐标
- fov
- 相机的原始位置、原始朝向点
第一步:可能很多同学都清楚,我们要借助这n个坐标点构建一个大的包围盒。这时候我们就很容易拿到最新的相机朝向点,即包围盒的中心。
第二步:跟着思路走,目前我们需要计算相机的最终位置了,而最终位置肯定是与包围盒有关,应该是在以包围盒中心为球心,以一个非常合适的距离为半径的球面上。
嗯...,那么现在我们最核心的就是思考如何计算这个合适距离。
我们来简化一下,将3d减弱到2d画个图,理想的相机距离应该是这样的
再来简化一下图形,我们只考虑一半
图中直角位置即我们的center,x便是我们此步骤最终要求的那个合适距离。由tan(2/fov) = h/x,可以得知只要知道h,x便也有了。h是什么呢,我们仔细观察一下h越大相机的开角便越大,那么自然就能看到更多的东西。我们这里的需求就是想要看到整个包围盒里面的所有东西,那么自然需要有一个最大的h。再看一图的h可以是矩形的宽高,但宽高不太确定因为我们还需要比较一下,确定最长的我们难道不可以使用对角线么。这毫无疑问最长了吧。「对角线的api工具」
那么x便很容易得出来 x = h / tan(2/fov)
第三步:此时我们需要根据计算出来的距离来计算相机的最终位置了,需求方给到我的是希望最终的朝向和原有的朝向不能发生变化(朝向的方向向量是一致的)
这其实比较简单,因为我们现在已经有了相机的最终朝向坐标(center)和距离x,这时我们只要一点从center位置出发沿着【原相机朝向坐标到原相机位置】的方向走x距离即可
这便一些简单的向量运算了
首先:原相机朝向坐标t到原相机位置p , 即p-t 。向量p减向量t的几何意义即为得到一个新的以t的终点的起点p的终点为重点的新向量。我们进行标准化拿到这个向量的方向向量。最后用这个方向向量*距离x 再加上 center向量就得到了最终的相机位置。
向量*标量:即向量拉长标量倍
向量加法的几何意义:即可以理解为从第一个向量的终点沿着第二个向量的方向走第二个向量的模
完整代码其实就几行,不过挺有意思的
ini
const box = new Box3()
box.setFromPoints(currentPos.map(item => new Vector3(item[0], item[1], item[2])))
const center = box.getCenter(new Vector3())
const size = box.getSize(new Vector3())
const halfDiagonal = size.length() * 0.5
const maxDistance = halfDiagonal / Math.tan(this.camera.fov / 2 * Math.PI / 180)
const cameraToCenterDistance = maxDistance
const directionVector = (this.camera.position.clone()).sub(this.controls.target).normalize() // 相机指向物体中心的向量
const lastPosition = directionVector.multiplyScalar(cameraToCenterDistance).add(center)
const lastLookat = center