直接看效果
调整前 | 调整后 |
---|---|
调整后模型可以全部展示,不会超出显示区域,有一些比较小的模型也是可以放大显示的
直接贴代码
js
function fitOnScreen() {
const box = new THREE.Box3().setFromObject(bldg);
const boxSize = box.getSize(new THREE.Vector3()).length();
const boxCenter = box.getCenter(new THREE.Vector3());
frameArea(boxSize * 1.5, boxSize, boxCenter);
controls.maxDistance = boxSize * 10;
controls.target.copy(boxCenter);
controls.update();
}
function frameArea(sizeToFitOnScreen, boxSize, boxCenter) {
const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
const halfFovY = THREE.MathUtils.degToRad(camera.fov * 0.5);
const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
const direction = new THREE.Vector3()
.subVectors(camera.position, boxCenter)
.multiply(new THREE.Vector3(1, 0, 1))
.normalize();
camera.position.copy(direction.multiplyScalar(distance).add(boxCenter));
camera.near = boxSize / 100;
camera.far = boxSize * 100;
camera.updateProjectionMatrix();
camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);
}
首页声明,上面代码是我在three.js官网demo里面复制,这里只是做代码解读
上面的代码是一个用于调整相机位置以适应场景中对象的函数。
- 函数
fitOnScreen
用于计算场景中对象的包围盒大小,并根据大小和中心点调整相机和控制器的位置。 - 函数
frameArea
用于根据给定的尺寸,计算相机的位置和截锥体的近平面和远平面。
fitOnScreen
1. 使用Box3对象获取场景中所有对象的包围盒。
js
// 计算包围盒大小及中心点
const box = new THREE.Box3().setFromObject(scene);
// 添加包围盒的辅助对象
const boxHelper = new THREE.BoxHelper(bldg, 0xffff00);
scene.add(boxHelper);
2. 计算包围盒的尺寸和中心点
js
const boxSize = box.getSize(new THREE.Vector3()).length();
const boxCenter = box.getCenter(new THREE.Vector3());
getSize
函数用于获取物体的尺寸(即包围盒的宽度、高度和深度),并返回一个新的 Vector3
向量,向量的三个分量分别表示包围盒在X、Y和Z轴上的尺寸。
然后,.length()
函数用于计算向量的长度(模)。对于 Vector3
向量,长度可以通过计算其三个分量的平方和的平方根来获取。即 length = √(x^2 + y^2 + z^2)
。
在给定的代码中,getSize(new THREE.Vector3())
返回的是一个 Vector3
向量,表示包围盒的尺寸。然后,.length()
函数应用于该向量,计算并返回包围盒尺寸的长度,即包围盒的对角线的长度。这个长度可以用于表示包围盒的大小或用于其他计算任务。
对于一些gemoetry可以使用其它方法处理
js
geometry.computeBoundingBox();
let { min, max } = geometry.boundingBox;
// 获取物体的高度差
let uHeight = max.y - min.y;
let uWidth = max.y - min.x;
3. 调用frameArea
函数,传入包围盒的尺寸以及中心点。
这里先不展开解释
4. 设置控制器的maxDistance属性,限制相机与场景中心的最大距离。
controls.maxDistance = boxSize * 10;
通俗点,就是不能让用户把场景模型缩得太小
5. 将控制器的target
属性设置为包围盒的中心点,并且更新控制器。
js
// 控制器将以 boxCenter 为中心进行旋转和缩放操作。
controls.target.copy(boxCenter);
// 摄像机的变换发生任何手动改变后调用
controls.update();
frameArea
传入三个参数
- sizeToFitOnScreen 需要适应的屏幕大小
- boxSize 模型大小
- boxCenter 模型中心坐标
1. 根据计算出的尺寸,将其一半设置为适应屏幕的一半尺寸
2. 使用相机的垂直视角一半以及尺寸计算相机与目标之间的距离
js
// 一半,看下面
const halfSizeToFitOnScreen = sizeToFitOnScreen * 0.5;
// 角度转弧度
const halfFovY = THREE.MathUtils.degToRad(camera.fov * 0.5);
// 求得相机到模型的距离
const distance = halfSizeToFitOnScreen / Math.tan(halfFovY);
3. 计算一个单位向量,指向相机在xz平面上的方向。
js
const direction = new THREE.Vector3()
.subVectors(camera.position, boxCenter)
.multiply(new THREE.Vector3(1, 0, 1))
.normalize();
new THREE.Vector3()
创建一个新的三维向量对象.subVectors(camera.position, boxCenter)
计算两个向量的差值,即从包围盒中心指向相机位置的向量
.multiply(new THREE.Vector3(1, 0, 1))
其实就是把y轴置零,x z轴不变.normalize();
得到一个表示方向的单位向量
向量归一化,就是等比例缩放向量的xyz三个分量,缩放到向量长度.length()
为1。
js
const dir = new THREE.Vector3(1, 1, 0);
dir.normalize(); //向量归一化
console.log('dir',dir);
//Vector3(√2/2, √2/2, 0) Vector3(0.707, 0.707, 0)
4. 根据距离和中心点,设置相机的位置
camera.position.copy(direction.multiplyScalar(distance).add(boxCenter));
direction.multiplyScalar(distance)
单位向量乘以距离add(boxCenter)
起点平移到boxCenter的位置camera.position.copy()
最后,我们将相机的位置设置为新计算得到的向量。使用copy
函数可以将新向量的值复制到camera.position
中,从而将相机移动到新的位置。
5. 计算适当的近平面和远平面的值
js
camera.near = boxSize / 100;
camera.far = boxSize * 100;
6. 更新相机的投影矩阵
camera.updateProjectionMatrix();
更新摄像机投影矩阵。在任何参数被改变以后必须被调用。
7. 将相机的视角对准包围盒的中心。
camera.lookAt(boxCenter.x, boxCenter.y, boxCenter.z);
外层控制器调整target也会调整相机焦点的
总结
其实也没什么好总结的,个人理解,这代码只要针对加载的模型有大有小,要动态调整相机及控制器,达到比较好的显示效果。