从今天开始郭先生就会说一下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. 学习Linux必须掌握的一个知识-i节点

    linux文件系统是Linux系统的心脏部分,提供了层次结构的目录和文件.文件系统将磁盘空间划分为每1024个字节一组,称为块(也有用512字节为一块的,如:SCOXENIX).编号从0到整个磁盘的最 ...

  2. 手机商品分享样式(纯html+css)

    效果图: html: <!DOCTYPE html> <html lang="en"> <head>     <meta charset= ...

  3. 【Java】HashMap实现原理---数据结构

    作为一个程序猿,特别是Java后端的,应该全部人都用过HashMap,也都知道HaspMap是一个用于存储Key-Value键值对的集合.与此同时我们把每一个键值对也叫做 Entry. 而这些Entr ...

  4. 动力节点 mysql 郭鑫 34道经典的面试题二

    13.有3个表S(学生表),C(课程表),SC(学生选课表) S(SNO,SNAME)代表(学号,姓名) C(CNO,CNAME,CTEACHER)代表(课号,课名,教师) SC(SNO,CNO,SC ...

  5. MongoDB via Dotnet Core数据映射详解

    用好数据映射,MongoDB via Dotnet Core开发变会成一件超级快乐的事.   一.前言 MongoDB这几年已经成为NoSQL的头部数据库. 由于MongoDB free schema ...

  6. Python3-queue模块-同步队列

    Python3中的queue模块实现多生产者,多消费者队列,特别适用于多个线程间的信息的安全交换,主要有三个类 queue.Queue(maxsize=0) 构造一个FIFO(先进先出)的队列 que ...

  7. Nginx基本知识,nginx安装使用方法

    Nginx 是一款高性能的Web服务器软件. - 具有极高的并发性能 - 利用Nginx与Tomcat组合使用, 搭建反向代理集群 - Nginx 反向代理集群可以解决网站的高并发问题! 1.安装 Y ...

  8. 多图解释Redis的整数集合intset升级过程

    redis源码分析系列文章 [Redis源码系列]在Liunx安装和常见API 为什么要从Redis源码分析 String底层实现——动态字符串SDS 双向链表都不懂,还说懂Redis? 面试官:说说 ...

  9. 理解与使用Javascript中的回调函数 -2

    在javascript中回调函数非常重要,它们几乎无处不在.像其他更加传统的编程语言都有回调函数概念,但是非常奇怪的是,完完整整谈论回调函数的在线教程比较少,倒是有一堆关于call()和apply() ...

  10. Spring 获取单例流程(二)

    读完这篇文章你将会收获到 Spring 中 prototype 类型的 bean 如何做循环依赖检测 Spring 中 singleton 类型的 bean 如何做循环依赖检测 前言 继上一篇文章 S ...