ThreeJs入门28-computeBoundingSphere源码解析

1,839 阅读3分钟

「这是我参与2022首次更文挑战的第32天,活动详情查看:2022首次更文挑战

示例代码采用three.js-r73版本: cdnjs.cloudflare.com/ajax/libs/t…

上一节,我们通过THREE.BufferGeometry高性能地创建了160万个三角形,其中我们给geometry设置了computeBoundingSphere方法,这个方法是用来实现包围盒的,具体怎么做的呢,让我们来看看源码吧。

computeBoundingSphere

  • 如果我们不使用这个方法,我们显示效果上是没有什么变化的,因为它不涉及具体的显示。
  • 这个方法可以用来计算碰撞检测,检测两个物体是否相撞(以后有时间具体实战下)

计算立方体包围盒:

image.png 假设我们有一个球形物体,我们需要计算包围这个球体的立方体,我们需要找到最里面和最外面的点的坐标,然后通过平移的方式,画出立方体。所以我们需要找到所有点的最大xyz坐标和最小xyz坐标,也就找到了那两个点

计算球体包围盒

上面是计算立方体包围盒的方法,那计算球体包围盒应该怎么做呢? image.png

  • 假设我们有这么一个物体(中间乱线),外面的圆时球体包围盒,想计算这个包围盒需要先找到中心点,然后找到半径,就可以计算了。

查看源码实现

  • 我们找到threejs源码->src/core/BufferGeometry.js->computeBoundingSphere方法

初始化定义

  • 首先是定义了一个立方体和一个向量,返回了一个函数供调用
	computeBoundingSphere: function () {

		var box = new THREE.Box3(); // 在3D空间中表示一个盒子或立方体
		var vector = new THREE.Vector3(); // 创建一个向量
		// 返回了一个函数
		return function () {
    	....
    }
  }

判断是否有球形包围盒

  • 然后判断是不是有球形包围盒,如果没有创建一个球体
  • 把设置的坐标点进行存储
// 如果 球体边界 是空,创建一个球体,也就是包围我们物体的球体
if (this.boundingSphere === null) {

  this.boundingSphere = new THREE.Sphere(); // 球体

}

var positions = this.attributes.position.array; // 获取位置数组

计算立方体包围盒

  • 如果有坐标点,开始计算立方体包围盒
  • 先清空包围盒,进行初始化
  • 设置中心点,默认和球体中心点一样
/* -----计算立方体包围盒----- */
box.makeEmpty(); // 清空包围盒

var center = this.boundingSphere.center; // 获取中心点,默认是0

for (var i = 0, il = positions.length; i < il; i += 3) {
  // 矩阵 偏移量
  vector.fromArray(positions, i); // 由顶点数组创建向量
  box.expandByPoint(vector);  // 存储最小和最大的向量值,让盒子包含这个向量值,也就是取向量中最大和最小xyz值进行存储

}

box.center(center); // 设置中心点
  • vector.fromArray,这个方法可以通过传递的数组,加上偏移量,设置一个向量
    • 由于我们遍历的时候是i+3,所以每三个字段组成一个点,刚好赋值给vector
fromArray: function ( array, offset ) {

  if ( offset === undefined ) offset = 0;

  this.x = array[ offset ];
  this.y = array[ offset + 1 ];
  this.z = array[ offset + 2 ];

  return this;

},
  • box.expandByPoint这个方法会将传入的向量点进行比较,保留向量最大的xyz值,和最小的xyz值,作为最大向量和最小向量
    • 这里调用的min( point )``max( point )Vector3上最大最小方法,它会返回最大最小xyz值
	expandByPoint: function ( point ) {

		this.min.min( point );
		this.max.max( point );

		return this;

	},

计算球体包围盒

  • 初始化最大半径
  • 通过取离中心点最远的距离可以得到最大半径
  • 如果没有计算出半径,程序会报错
/* ------计算球体包围盒----- */
var maxRadiusSq = 0; // 最大半径

for (var i = 0, il = positions.length; i < il; i += 3) {

  vector.fromArray(positions, i);
  // 取离中心点最远的距离
  maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(vector));

}

this.boundingSphere.radius = Math.sqrt(maxRadiusSq); // 开根号,得到半径
// 如果没有计算出半径,就会报错
if (isNaN(this.boundingSphere.radius)) {

  console.error('THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this);

}
  • 以上就是我们对于computeBoundingSphere源码的解析,通过这个方法我们就可以设置模型的立方体包围盒盒球体包围盒了。