前言
在Three.js中,向量(Vector)是一个非常重要的概念,广泛应用于各种场景,几乎所有的几何、变换、物理、动画等操作都离不开向量的运算。理解向量的概念和使用方法对于掌握Three.js是非常重要的。
向量应用场景示例:
-
几何体的位置和方向
- 位置 :几何体(如
Mesh
、Sprite
等)的位置通常使用Vector3
来表示。例如,mesh.position
就是一个Vector3
对象,表示几何体在三维空间中的位置。 - 方向 :几何体的旋转和朝向也可以使用向量来表示。例如,
mesh.rotation
使用欧拉角(Euler angles)来表示旋转,但也可以通过Quaternion
来表示,而Quaternion
的计算通常涉及到向量。
- 位置 :几何体(如
-
相机
- 位置 :相机的位置(
camera.position
)是一个Vector3
对象,表示相机在三维空间中的位置。 - 目标点 :相机的目标点(
camera.lookAt
)也是一个Vector3
对象,表示相机所指向的位置。
- 位置 :相机的位置(
-
光线
- 光源位置 :光源(如
PointLight
、SpotLight
)的位置通常使用Vector3
来表示。 - 光线方向:在光线追踪或阴影计算中,光线的方向通常使用向量来表示。
- 光源位置 :光源(如
-
变换矩阵
- 平移 :平移矩阵的计算涉及到向量。例如,
Matrix4.makeTranslation(vector)
方法会根据一个Vector3
向量生成一个平移矩阵。 - 旋转 :旋转矩阵的计算也涉及到向量。例如,
Matrix4.makeRotationAxis(axis, angle)
方法会根据一个旋转轴向量生成一个旋转矩阵。
- 平移 :平移矩阵的计算涉及到向量。例如,
-
碰撞检测
- 在碰撞检测中,物体的位置、速度、方向等通常使用向量来表示。例如,
Raycaster
类用于检测射线与物体的交点,其计算涉及到向量的运算。
- 在碰撞检测中,物体的位置、速度、方向等通常使用向量来表示。例如,
-
物理引擎
- 在结合物理引擎(如
Cannon.js
、Oimo.js
)时,物体的速度、加速度、力等通常使用向量来表示。
- 在结合物理引擎(如
-
数学运算
- Three.js提供了丰富的向量运算方法,如加法、减法、点积、叉积、归一化等。这些运算在各种计算中都非常常见,例如计算法线、投影、反射等。
-
材质和着色器
- 在自定义着色器中,向量用于表示顶点位置、法线、UV坐标等。例如,在顶点着色器中,
gl_Position
是一个四维向量,表示顶点在裁剪空间中的位置。
- 在自定义着色器中,向量用于表示顶点位置、法线、UV坐标等。例如,在顶点着色器中,
数学原理
向量本身就是数学概念,three只是按照向量计算逻辑用js实现了一遍,在读源码之前理解数学逻辑对我们充分理解源码至关重要
1. 什么是向量(vector)
从点A(x1,y1,z1) 到 点B(x2,y2,z2)构成的有向线段:
俩个核心点:
- 长度(模):点A到点B的距离
- 方向 :点A到点B的方向
2. 单位向量
长度(模)为1的向量
3. 向量长度(模)
4. 向量运算
1. 加法
向量加法满足平行四边形法则和三角形法则 即: a + b =(X1+X2,Y1+Y2)
2. 减法
向量加法满足三角形法则
即: a - b =(X1-X2,Y1-Y2)
3. 点乘
4. 叉乘(向量积)
5. 点乘和叉乘应用场景(核心点⭐️)
一般在webgl计算三维空间坐标中
- 叉乘的几何意义是计算平面的法线,因为叉乘能计算出一根同时垂 直于原向量的新向量,而叉乘的数量积的几何意义可以用来计算三角面的面积;
- 点乘的几何意义是计算夹角。例如计算光源入射折射对物体表面的影响,
源码解析
源码地址:Threejs-Vector3
1. constructor
ps: threejs中的向量(vector)默认是以原点为起始点,构造函数内部的xyz是终点坐标
js
constructor( x = 0, y = 0, z = 0 ) {
Vector3.prototype.isVector3 = true;
this.x = x;
this.y = y;
this.z = z;
}
2. 向量基本运算
以下源码实现逻辑均可参照上述的数学原理,唯一的不同只是用js实现了一下
js
// 向量长度(模)
length() {
return Math.sqrt( this.x * this.x
+ this.y * this.y
+ this.z * this.z );
}
// 向量加法
add( v ) {
this.x += v.x;
this.y += v.y;
this.z += v.z;
return this;
}
// 向量减法
sub( v ) {
this.x -= v.x;
this.y -= v.y;
this.z -= v.z;
return this;
}
// 向量点乘
dot( v ) {
return this.x * v.x + this.y * v.y + this.z * v.z;
}
// 向量叉乘
cross( v ) {
return this.crossVectors( this, v );
}
crossVectors( a, b ) {
const ax = a.x, ay = a.y, az = a.z;
const bx = b.x, by = b.y, bz = b.z;
this.x = ay * bz - az * by;
this.y = az * bx - ax * bz;
this.z = ax * by - ay * bx;
return this;
}
3. 其他常用api详解
ps: set***, get***, copy***, clone*** 等简单api自行查看,不多赘述
1. distanceTo
俩点间距离公式,秒了🥲
js
distanceTo( v ) {
return Math.sqrt( this.distanceToSquared( v ) );
}
distanceToSquared( v ) {
const dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;
return dx * dx + dy * dy + dz * dz;
}
2. normailze
向量归一化:不改变向量方向且将向量的模变为1,实现逻辑就是将x,y,z按比例扩大或缩小。
js
normalize() {
return this.divideScalar( this.length() || 1 );
}
divideScalar( scalar ) {
return this.multiplyScalar( 1 / scalar );
}
multiplyScalar( scalar ) {
this.x *= scalar;
this.y *= scalar;
this.z *= scalar;
return this;
}
原理也很简单,分两步:
- 计算向量的模
- 将该向量的x,y,z坐标除以这个向量的模
3. angleTo
计算当前向量与另一个向量之间的夹角(弧度)
js
angleTo( v ) {
const denominator = Math.sqrt( this.lengthSq() * v.lengthSq() );
if ( denominator === 0 ) return Math.PI / 2;
const theta = this.dot( v ) / denominator;
// clamp, to handle numerical problems
return Math.acos( clamp( theta, - 1, 1 ) );
}
原理: 根据向量点乘运算公式,即可求出角度