从今天开始郭先生就会说一下three.js 的一些数学方法了,像Box3、Plane、Vector3、Matrix3、Matrix4当然还有欧拉角和四元数。今天说一说three.js的Box3方法(Box2是Box3的二维版本,可以参考Box3)。在线案例点击博客原文

Box3在3D空间中表示一个包围盒。其主要用于表示物体在世界坐标中的边界框。它方便我们判断物体和物体、物体和平面、物体和点的关系等等。
构造器参数Box3( min : Vector3, max : Vector3 ),其参数为两个三维向量,第一个向量为Box3在3D空间中各个维度的最小值,第二个参数为Box3在3D空间中各个维度的最大值,代码如下。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2));

这个box就表示3D空间中中心点在(0,0,0),长宽高为4的包围盒。
下面我们十分详细的说说他的属性和方法。

1. Box3的属性

Box3只有三个属性。

  1. isBox3 – 用于检测当前对象或者派生类对象是否是Box3。默认为 true。
  2. .min – Vector3 表示包围盒的(x, y, z)下边界。默认值是( + Infinity, + Infinity, + Infinity )。
  3. .max – Vector3 表示包围盒的(x, y, z)上边界。默认值是( - Infinity, - Infinity, - Infinity )。

2. Box3的方法

1. set( min: Vector3, max: Vector3 )

这个比较简单,就是设置包围盒的上下边界

var box = new THREE.Box3().set(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2));//返回的包围盒和上面的包围盒相同

2. setFromArray( array: ArrayLike )

设置包围盒的上下边界使得数组 array 中的所有点的点都被包含在内

var box = new THREE.Box3().setFromArray([-2,-2,-2,2,2,2]);//返回的包围盒和上面的包围盒相同

3. setFromBufferAttribute( bufferAttribute: BufferAttribute )

设置此包围盒的上边界和下边界,以包含 attribute 中的所有位置数据,使用方法如下

var typedArray= new Float32Array(3*2); 
var array = [-2,-2,-2,2,2,2];
array.forEach((d,i)=>typedArray[i] = d);
var bufferAttribute = new THREE.BufferAttribute(typedArray,3);
var box = new THREE.Box3().setFromBufferAttribute(bufferAttribute);

这里注意BufferAttribute的第一个参数是一个类型化数组,这个放到以后再说。

4. setFromPoints( points: Vector3[] )

设置此包围盒的上边界和下边界,以包含数组 points 中的所有点。

var box = new THREE.Box3().setFromPoints([new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)]);//返回的包围盒和上面的包围盒相同

5. setFromCenterAndSize( center: Vector3, size: Vector3 )

将当前包围盒的中心点设置为 center ,并将此包围盒的宽度,高度和深度设置为大小指定 size 的值。

var box = new THREE.Box3().setFromCenterAndSize(new THREE.Vector3(0,0,0), new THREE.Vector3(4,4,4))//返回的包围盒和上面的包围盒相同

6. setFromObject( object: Object3D )

计算和世界轴对齐的一个对象 Object3D (含其子对象)的包围盒,计算对象和子对象的世界坐标变换。

var boxObject = new THREE.Mesh( new THREE.BoxGeometry(5, 5, 5), new THREE.MeshBasicMaterial({ color: 0xffaa00 }) );
var box = new THREE.Box3().setFromObject(boxObject);

把正方体网格作为参数,实际上是根据geometry.vertices的Vector3点集和computeBoundingBox()方法计算的。

7. clone()

返回一个与该包围盒子有相同下边界min 和上边界 max的新包围盒代码如下

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); var newBox = box.clone();

8. copy( box: Box3 )

将传入的值 box 中的 min 和 max 拷贝到当前对象。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2));
var newBox = new THREE.Box3().copy(box);

9. makeEmpty()

清空包围盒,下边界为( + Infinity, + Infinity, + Infinity ),上边界为( - Infinity, - Infinity, - Infinity )

10. isEmpty()

如果这个包围盒包含0个顶点,则返回true。注意,下界和上界相等的方框仍然包含一个点,即两个边界共享的那个点。
这个方法比较有意思,可以判断包围盒是否为空,体会下面的代码

new THREE.Box3(new THREE.Vector3(0,0,0), new THREE.Vector3(0,0,0)).isEmpty()//返回false new THREE.Box3(new THREE.Vector3(0,0,0), new THREE.Vector3(-1,0,0)).isEmpty()//返回true

正常情况下包围盒的上边界都是大于等于下边界的,如果某一个维度的上边界小于下边界那么这个包围盒就是空盒子

11. getCenter( target: Vector3 )

返回包围盒的中心点 Vector3。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
box.getCenter()/返回中心点Vector3 {x: 0, y: 0, z: 0}

12. getSize( target: Vector3 )

返回包围盒的宽度,高度,和深度。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
box.getSize()/返回包围盒的宽度,高度,和深度Vector3 {x: 4, y: 4, z: 4}

13. expandByPoint( point: Vector3 )

扩展这个包围盒的边界使得该点(point)在包围盒内。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
box.expandByPoint(new THREE.Vector3(4,0,0)).getCenter()//中心点已不是Vector3(0,0,0),而是Vector3(1,0,0)

通过Vector3(3,0,0)这个点扩展了原本的包围盒

14. expandByVector( vector: Vector3 )

按 vector 每个纬度的值展开这个箱子。 这个盒子的宽度将由 vector 的x分量在两个方向上展开。 这个盒子的高度将由 vector 两个方向上的y分量展开。 这个盒子的深度将由 vector z分量在两个方向上展开。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
box.expandByVector(new THREE.Vector3(0,1,2)).getSize()//新的包围盒size已变成Vector3 {x: 4, y: 6, z: 8}

15. expandByScalar( scalar: number )

按 scalar 的值展开盒子的每个维度。如果是负数,盒子的尺寸会缩小。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
box.expandByVector(1).getSize()//新的包围盒size已变成Vector3 {x: 6, y: 6, z: 6}

16. expandByObject( object: Object3D )

扩展此包围盒的边界,使得对象及其子对象在包围盒内,包括对象和子对象的世界坐标的变换。

var boxObject = new THREE.Mesh( new THREE.BoxGeometry(2,2,2), new THREE.MeshBasicMaterial({ color: 0xffaa00 }) ); 
boxObject.position.set(2,0,0);//或者boxObject.geometry.translate(2,0,0) var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); box.expandByObject(boxObject);

17. containsPoint( point: Vector3 )

当传入的值 point 在包围盒内部或者边界都会返回true。这是个比较有用的方法

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
var point1 = new THREE.Vector3(1,2,2);
var point2 = new THREE.Vector3(2,2,2);
var point3 = new THREE.Vector3(3,2,2);
box.containsPoint(point1)//返回true box.containsPoint(point2)//返回true box.containsPoint(point3)//返回false

18. containsBox( box: Box3 )

传入的 box 整体都被包含在该对象中则返回true。如果他们两个包围盒是一样的也返回true。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
var box1 = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(1,2,2));
var box2 = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2));
var box3 = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(3,2,2));
console.log(box.containsBox(box1))//返回true
console.log(box.containsBox(box2))//返回true
console.log(box.containsBox(box3))//返回false

19. getParameter( point: Vector3 )

返回一个点为这个盒子的宽度和高度的比例。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
console.log(box.getParameter(new THREE.Vector3(0,0,0),new THREE.Vector3()))//返回Vector3 {x: 0.5, y: 0.5, z: 0.5};
console.log(box.getParameter(new THREE.Vector3(1,0,0),new THREE.Vector3()))//返回Vector3 {x: 0.75, y: 0.5, z: 0.5}
console.log(box.getParameter(new THREE.Vector3(2,0,0),new THREE.Vector3()))//返回Vector3 {x: 1, y: 0.5, z: 0.5}
console.log(box.getParameter(new THREE.Vector3(3,0,0),new THREE.Vector3()))//返回Vector3 {x: 1.25, y: 0.5, z: 0.5}

这里我们只观察x方向,第一个输出x=0,刚好在包围盒的中心点,所以返回了0.5,第三个输出x=2刚好在包围盒的上边界,所以返回1,也就是100%,当然超过上边界就大于1(100%),低于下边界就小于0(0%)。

20. intersectsBox( box: Box3 )

确定当前包围盒是否与传入包围盒box 相交。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
var box1 = new THREE.Box3(new THREE.Vector3(2,2,2), new THREE.Vector3(4,4,4));
var box2 = new THREE.Box3(new THREE.Vector3(3,2,2), new THREE.Vector3(4,4,4));
console.log(box.intersectsBox(box1))//box与box1相交,边界相交也算相交
console.log(box.intersectsBox(box2))//box与box2不想交,

21. intersectsSphere( sphere: Sphere )

确定当前包围盒是否与球体 sphere 相交。
这个球体和包围和一样,都是一个3D空间。由一个中心点和半径构成,和包围盒十分类似,这里就不多赘述。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
var sphere1 = new THREE.Sphere(new THREE.Vector3(4,2,2), 1);
var sphere2 = new THREE.Sphere(new THREE.Vector3(4,2,2), 2);
var sphere3 = new THREE.Sphere(new THREE.Vector3(4,2,2), 3);
console.log(box.intersectsSphere(sphere1))//返回false
console.log(box.intersectsSphere(sphere2))//返回true
console.log(box.intersectsSphere(sphere3))//返回true

这里可以看出,他们的边界相交也算相交。

22. intersectsPlane( plane: Plane )

检测这个球与所传入的plane是否有交集。这个plane是在三维空间中无限延伸的二维平面,平面方程用单位长度的法向量和常数表示为海塞法向量Hessian normal form形式。它的构造器有两个参数,第一个是normal - (可选参数) 定义单位长度的平面法向量Vector3。默认值为 (1, 0, 0)。第二个是constant - (可选参数) 从原点到平面的有符号距离。 默认值为 0。这个plane我们日后还会讲。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
var plane1 = new THREE.Plane(new THREE.Vector3(1,0,0), 1);
var plane2 = new THREE.Plane(new THREE.Vector3(1,0,0), 2);
var plane3 = new THREE.Plane(new THREE.Vector3(1,0,0), 3);
console.log(box.intersectsPlane(plane1))//返回true
console.log(box.intersectsPlane(plane2))//返回true
console.log(box.intersectsPlane(plane3))//返回false

这里要注意平面的第二个参数是有符号的距离,所以代码中的三个平面都是在x轴的负半轴。

23. intersectsTriangle( triangle: Triangle )

确定当前包围盒是否与三角形 triangle 相交。这个三角同样是一个数学库,这里也不先说

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
var triangle1 = new THREE.Triangle(new THREE.Vector3(1,-1,1),new THREE.Vector3(1,-1,-1),new THREE.Vector3(1,0,1));
var triangle2 = new THREE.Triangle(new THREE.Vector3(2,-1,1),new THREE.Vector3(2,-1,-1),new THREE.Vector3(2,0,1));
var triangle3 = new THREE.Triangle(new THREE.Vector3(3,-1,1),new THREE.Vector3(3,-1,-1),new THREE.Vector3(3,0,1));
console.log(box.intersectsTriangle(triangle1))//返回true
console.log(box.intersectsTriangle(triangle2))//返回true
console.log(box.intersectsTriangle(triangle3))//返回false

24. clampPoint( point: Vector3, target: Vector3 )

是这个点point Clamps(处于范围内) 处于包围盒边界范围内,如果我们传一个target,那么新点就会复制到target上。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
console.log(box.clampPoint(new THREE.Vector3(3,0,0),new THREE.Vector3()))//这里返回Vector3 {x: 2, y: 0, z: 0}
console.log(box.clampPoint(new THREE.Vector3(3,3,3),new THREE.Vector3()))//这里返回Vector3 {x: 2, y: 2, z: 2}

这个结果可以知道,包围盒的这个方法把传入的任意点都转化成包围盒边界上或者包围盒内的点,如何点的某个维度不在包围盒中,那么这个维度就返回包围盒这个维度的边界的最大值或最小值。

25. distanceToPoint( point: Vector3 )

返回这个box的任何边缘到指定点的距离。如果这个点位于这个盒子里,距离将是0。这是个比较好的方法。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
console.log(box.distanceToPoint(new THREE.Vector3(2,2,2)))//返回0,因为在边界上
console.log(box.distanceToPoint(new THREE.Vector3(3,3,3)))//返回1.732(根号3),因为离这个点最近的点是new THREE.Vector3(2,2,2。

26. getBoundingSphere( target: Sphere )

通过包围盒获取包围球。得到的包围球刚好包围包围盒

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
box.getBoundingSphere(new THREE.Sphere())//center: Vector3 {x: 0, y: 0, z: 0},radius: 3.4641016151377544

中心就是包围盒的中心,半径就是中心到一个顶点的距离。

27. intersect( box: Box3 )

返回此包围盒和 box 的交集,将此框的上界设置为两个框的max的较小部分, 将此包围盒的下界设置为两个包围盒的min的较大部分。

var box1 = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
var box2 = new THREE.Box3(new THREE.Vector3(0,0,0), new THREE.Vector3(4,4,4));
console.log(box1.intersect(box2))//返回max: Vector3 {x: 2, y: 2, z: 2},min: Vector3 {x: 0, y: 0, z: 0}

28. union( box: Box3 )

在 box 参数的上边界和已有box对象的上边界之间取较大者,而对两者的下边界取较小者,这样获得一个新的较大的联合盒子。

var box1 = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
var box2 = new THREE.Box3(new THREE.Vector3(0,0,0), new THREE.Vector3(4,4,4));
console.log(box1.union(box2))//返回max: Vector3 {x: 4, y: 4, z: 4},min: Vector3 {x: -2, y: -2, z: -2}

29. applyMatrix4( matrix: Matrix4 )

使用传入的矩阵变换Box3(包围盒8个顶点都会乘以这个变换矩阵)

var matrix4 = new THREE.Matrix4().makeScale(0,1,2);//得到一个缩放矩阵 
var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2));
box.applyMatrix4(matrix4);//包围盒应用矩阵,返回max: Vector3 {x: 0, y: 2, z: 4} min: Vector3 {x: 0, y: -2, z: -4}

30. translate( offset: Vector3 )

给包围盒的上下边界添加偏移量 offset,这样可以有效的在3D空间中移动包围盒。 偏移量为 offset 大小。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
box.translate(new THREE.Vector3(1,0,0))//返回max: Vector3 {x: 3, y: 2, z: 2},min: Vector3 {x: -1, y: -2, z: -2}

31. equals( box: Box3 )

如果矩阵m 与当前矩阵所有对应元素相同则返回true。

var box = new THREE.Box3(new THREE.Vector3(-2,-2,-2), new THREE.Vector3(2,2,2)); 
var box1 = box.clone(); box.equals(box1)//box和它克隆的包围盒相等。

这是Box3的全部方法了。

3. Box3的应用案例

这里有两个相对运动的网格,我们来判断他们的相对位置,如下图。

下面是主要代码

setBox3() {
var boxGeometry = new THREE.BoxGeometry(30, 30, 30);
var sphereGoemetry = new THREE.SphereGeometry(3, 30, 20);
var sphereMaterial = new THREE.MeshBasicMaterial();
box = this.setMaterial(boxGeometry, 0x0000ff);//先生成一个立方体网格
box3 = new THREE.Box3().setFromObject(box);//根据几何体生成包围盒
sphere = new THREE.Mesh(sphereGoemetry, sphereMaterial);//在生成一个球形网格
scene.add(box);//添加到场景
scene.add(sphere);//添加到场景 this.render();
},
render() {
//让球动起来
sphere.position.y = Math.sin(time) * 16 + 8;
sphere.position.x = Math.cos(time) * 16 + 8;
time = time + 0.02;
sphereBox3 = new THREE.Box3().setFromObject(sphere);//动态生成球的包围盒(这里用了包围盒,没有用包围球,边边角角有些出入,不影响大体效果)
if(box3.containsBox(sphereBox3)) {
//如果box3包含sphereBox3
sphere.material.color = new THREE.Color(0x00ff00);
} else if(box3.intersectsBox(sphereBox3)) {
//如果box3交于sphereBox3
sphere.material.color = new THREE.Color(0xff00ff);
} else {
//如果sphereBox3在box3之外
sphere.material.color = new THREE.Color(0xffaa00);
}
renderer.render(scene, camera);
requestAnimationFrame(this.render);
}

学好three.js 的一些数学方法并不能起飞,但是遇到问题可以得心应手使用它,做到事半功倍。

转载请注明地址:郭先生的博客

three.js 数学方法之Box3的更多相关文章

  1. three.js 数学方法之Plane

    今天郭先生就来继续说一说three.js数学方法中的plane(平面).在三维空间中无限延伸的二维平面,平面方程用单位长度的法向量和常数表示.构造器为Plane( normal : Vector3, ...

  2. three.js 数学方法之Matrix3

    今天郭先生来说一说three.js的三维矩阵,这块知识需要结合线性代数的一些知识,毕业时间有点长,线性代数的知识大部分都还给了老师.于是一起简单的复习了一下.所有的计算都是使用列优先顺序进行的.然而, ...

  3. three.js 数学方法之Vector3

    今天郭先生来说一说three.js的Vector3,该类表示的是一个三维向量(3D vector). 一个三维向量表示的是一个有顺序的.三个为一组的数字组合(标记为x.y和z),可被用来表示很多事物, ...

  4. three.js 数学方法之Matrix4

    今天郭先生说一说three.js中的Matrix4,相较于Matrix3来说,Matrix4和three.js联系的更紧密,因为在4x4矩阵最常用的用法是作为一个变换矩阵.这使得表示三维空间中的一个点 ...

  5. js数学方法应用

    找出数组中最大的数 var values = [1, 2, 3, 4, 5, 6, 7, 8]; alert(Math.min.apply(Math,values))//8 这个技巧的关键是把 Mat ...

  6. js数组及常用数学方法

    数组方法 清空数组   1: arr.length=0;   2: arr=[]; arr.push()          //往数组最后一个添加元素,会待会一个返回值,就是新的数组长度arr.uns ...

  7. js数组方法大全(下)

    # js数组方法大全(下) 记录一下整理的js数组方法,免得每次要找方法都找不到.图片有点多,注意流量,嘻嘻! 本期分享 forEach() map() filer() every() some() ...

  8. js数组方法

    数组方法清空数组1: arr.length=02: arr=[]arr.push()//往数组最后一个添加元素,会待会一个返回值,就是新的数组长度arr.unshift()//往数组的第一个添加元素, ...

  9. js调用php和php调用js的方法举例

    js调用php和php调用js的方法举例1 JS方式调用PHP文件并取得php中的值 举一个简单的例子来说明: 如在页面a.html中用下面这句调用: <script type="te ...

随机推荐

  1. Beta冲刺--冲刺总结

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 Beta 冲刺 这个作业的目标 Beta冲刺--冲刺总结 作业正文 如下 其他参考文献 ... Beta冲刺 ...

  2. JVM之HotSpot虚拟机是如何发起内存回收的?

    1.背景 在上一节中,我们掌握了垃圾收集的一些算法,也弄明白了分代回收的原理, 那么HotSpot虚拟机是如何发起内存回收的? 2.如何找到GC Roots根节点(枚举根节点) 从可达性分析中GC R ...

  3. 后渗透工具Empire使用教程

    一.前言 Empire是一个PowerShell后期漏洞利用代理工具同时也是一款很强大的后渗透测神器,它建立在密码学.安全通信和灵活的架构之上.Empire实现了无需powershell.exe就可运 ...

  4. 程序员如何高效学Python,如何高效用Python挣钱

    本人在1年半之前,不熟悉Python(不过有若干年Java开发基础),由于公司要用Python,所以学习了一通.现在除了能用Python做本职工作外,还出了本Python书,<基于股票大数据分析 ...

  5. DNS篇(详解DNS)

    *文章来源:https://blog.egsec.cn/archives/601 *本文将主要说明:本文主要叙述什么是DNS.域名的层级.DNS 解析过程.DNS的缓存时间.DNS 的记录类型.DNS ...

  6. 机器学习之KNN算法(分类)

    KNN算法是解决分类问题的最简单的算法.同时也是最常用的算法.KNN算法也可以称作k近邻算法,是指K个最近的数据集,属于监督学习算法. 开发流程: 1.加载数据,加载成特征矩阵X与目标向量Y. 2.给 ...

  7. Docker环境下Java应用的最大内存和堆内存的设置

    Docker环境下Java应用的最大内存和堆内存的设置 1.  设置应用允许使用的最大内存 通过docker run(创建一个新的容器并运行)命令中设置-m来进行设置.案例如下所示. docker r ...

  8. Netty 源码解析(三): Netty 的 Future 和 Promise

    今天是猿灯塔“365篇原创计划”第三篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel 当前:Ne ...

  9. GAN网络从入门教程(二)之GAN原理

    在一篇博客GAN网络从入门教程(一)之GAN网络介绍中,简单的对GAN网络进行了一些介绍,介绍了其是什么,然后大概的流程是什么. 在这篇博客中,主要是介绍其数学公式,以及其算法流程.当然数学公式只是简 ...

  10. 在react中使用到的好用的插件

    1)antd UI组件 蚂蚁金服出品的 挺好用的 还有个移动端的antd-moblie 2) moment 日期处理类库 3)prop-types 第三方库 对组件props变量进行类型检测 4)qs ...