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也会调整相机焦点的

总结

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

相关推荐
csdnLN21 分钟前
$.ajax() 对应事件done() 、fail()、always() 的用法
前端·javascript·ajax
甜味橘阳21 分钟前
echarts地图可视化展示
前端·javascript·echarts
bloxed1 小时前
前端文件下载多方式集合
前端·filedownload
余生H1 小时前
前端Python应用指南(三)Django vs Flask:哪种框架适合构建你的下一个Web应用?
前端·python·django
LUwantAC1 小时前
CSS(四)display和float
前端·css
cwtlw1 小时前
CSS学习记录20
前端·css·笔记·学习
界面开发小八哥1 小时前
「Java EE开发指南」如何用MyEclipse构建一个Web项目?(一)
java·前端·ide·java-ee·myeclipse
米奇妙妙wuu2 小时前
react使用sse流实现chat大模型问答,补充css样式
前端·css·react.js
傻小胖2 小时前
React 生命周期完整指南
前端·react.js
梦境之冢2 小时前
axios 常见的content-type、responseType有哪些?
前端·javascript·http