three.js 模型适应窗口调整显示大小代码解读

直接看效果

调整前 调整后

调整后模型可以全部展示,不会超出显示区域,有一些比较小的模型也是可以放大显示的

直接贴代码

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();
  1. new THREE.Vector3()创建一个新的三维向量对象
  2. .subVectors(camera.position, boxCenter) 计算两个向量的差值,即从包围盒中心指向相机位置的向量
  1. .multiply(new THREE.Vector3(1, 0, 1)) 其实就是把y轴置零,x z轴不变
  2. .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));

  1. direction.multiplyScalar(distance) 单位向量乘以距离
  2. add(boxCenter) 起点平移到boxCenter的位置
  3. 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也会调整相机焦点的

总结

其实也没什么好总结的,个人理解,这代码只要针对加载的模型有大有小,要动态调整相机及控制器,达到比较好的显示效果。

相关推荐
Asort3 分钟前
JavaScript 从零开始(六):控制流语句详解——让代码拥有决策与重复能力
前端·javascript
无双_Joney22 分钟前
[更新迭代 - 1] Nestjs 在24年底更新了啥?(功能篇)
前端·后端·nestjs
在云端易逍遥23 分钟前
前端必学的 CSS Grid 布局体系
前端·css
ccnocare25 分钟前
选择文件夹路径
前端
艾小码25 分钟前
还在被超长列表卡到崩溃?3招搞定虚拟滚动,性能直接起飞!
前端·javascript·react.js
闰五月26 分钟前
JavaScript作用域与作用域链详解
前端·面试
泉城老铁30 分钟前
idea 优化卡顿
前端·后端·敏捷开发
前端康师傅30 分钟前
JavaScript 作用域常见问题及解决方案
前端·javascript
司宸31 分钟前
Prompt结构化输出:从入门到精通的系统指南
前端