参考资料:threejs中文网
three.js四元数Quaternion
接着上节课欧拉角Euler
的讲解,本节课给大家介绍threejs一个新的类,四元数Quaternion
。
四元数Quaternion
和欧拉角Euler
一样,可以用来计算或表示物体在3D空间中的旋转姿态角度。
Three.js对四元数的数学细节和算法进行了封装,提供了一个四元数相关的类,平时写一些姿态角度的代码,可以使用Quaternion
辅助。本节课,咱们就结合具体的threejs代码科普这个抽象的四元数概念,有了具体代码辅助,这样更容易使用四元数表示物体的姿态角度。
实例化Quaternion
实例化类Quaternion
,你可以一个四元数对象Quaternion
。
const quaternion = new THREE.Quaternion();
四元数方法.setFromAxisAngle()
.setFromAxisAngle()
是四元数的一个方法,可以用来辅助生成表示特定旋转的四元数。
.setFromAxisAngle(axis, angle)
生成的四元数表示绕axis旋转,旋转角度是angle。
.setFromAxisAngle()
可以生成一个四元数,绕任意轴,旋转任意角度,并不局限于x、y、z轴。
const quaternion = new THREE.Quaternion();
// 旋转轴new THREE.Vector3(0,0,1)
// 旋转角度Math.PI/2
// 绕z轴旋转90度
quaternion.setFromAxisAngle(new THREE.Vector3(0,0,1),Math.PI/2);
接下来用这个生成的四元数进行旋转计算。
需要旋转的A点坐标
// A表示3D空间一个点的位置坐标
const A = new THREE.Vector3(30, 0, 0);
为了方便观察,可以把旋转A点的位置用一个小球Mesh可视化表示出来
// 黄色小球可视化坐标点A
const Amesh = createSphereMesh(0xffff00,2);
Amesh.position.copy(A);
group.add(Amesh);
// 创建小球mesh
function createSphereMesh(color,R) {
const geometry = new THREE.SphereGeometry(R);
const material = new THREE.MeshLambertMaterial({
color: color,
});
const mesh = new THREE.Mesh(geometry, material);
return mesh;
}
四元数旋转A点坐标
threejs三维向量Vector3
具有一个方法.applyQuaternion(quaternion)
,该方法的功能就是通过参数quaternion
对Vector3进行旋转,比如Vector3表示A点的xyz坐标,执行A.applyQuaternion(quaternion)
,相当于通过quaternion
表示的四元数旋转A。
const quaternion = new THREE.Quaternion();
// 绕z轴旋转90度
quaternion.setFromAxisAngle(new THREE.Vector3(0,0,1),Math.PI/2);
// 通过四元数旋转A点:把A点绕z轴旋转90度生成一个新的坐标点B
const B = A.clone().applyQuaternion(quaternion);
console.log('B',B);//查看旋转后坐标
你可以创建一个小球可视化查看B点位置
// 红色小球可视化坐标点B
const Bmesh = createSphereMesh(0xff0000,2);
Bmesh.position.copy(B);
group.add(Bmesh);
四元数Quaternion
表示物体姿态
Three.js模型对象都有一个属性.quaternion
,.quaternion
的属性值就是四元数对象Quaternion
。你可以通过物体.quaternion
属性改变物体的姿态角度。
创建一个四元数
创建一个绕x轴旋转90度的四元数
// 四元数表示姿态角度
const quaternion = new THREE.Quaternion();
// 旋转轴new THREE.Vector3(1,0,0)
// 旋转角度Math.PI/2
quaternion.setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI / 2);
.quaternion
属性
改变物体的四元数属性.quaternion
,也就是改变物体的姿态角度。
.copy()
是四元数的一个方法,A.copy(B)
表示把A四元数设置为B四元数的值,或者说把B的值复制给A。
const quaternion = new THREE.Quaternion();
quaternion.setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI / 2);
//quaternion表示旋转角度复制给物体.quaternion
fly.quaternion.copy(quaternion);
总结:物体角度属性.rotation
和四元数属性.quaternion
three.js模型对象的角度.rotation
和四元数.quaternion
属性都是用来表示物体姿态角度的,只是表达形式不同而已,.rotation
和.quaternion
两个属性的值,一个改变,另一个也会同步改变。
const quaternion = new THREE.Quaternion();
quaternion.setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI / 2);
fly.quaternion.copy(quaternion);
// 四元数属性改变后,查看角度属性(欧拉角)变化
// .quaternion改变,.rotation同步改变
console.log('角度属性',fly.rotation.z);
四元数乘法运算.multiply()
下面给大家介绍四元数的乘法运算,Three.js四元数Quaternion
提供了多个用于四元数乘法运算的方法,比如.multiply()
、.multiplyQuaternions()
、.premultiply()
,这些方法本质上都一样,只是语法细节不同,本节课以.multiply()
为例给大家讲解。
四元数乘法.multiply()
含义
两个四元数分别表示一个旋转,如果相乘,会得到一个新的四元数,新四元数表示两个旋转的组合旋转。
// 在物体原来姿态基础上,进行旋转
const q1 = new THREE.Quaternion();
q1.setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI / 2);
fly.quaternion.multiply(q1);
// 在物体上次旋转基础上,进行旋转
const q2 = new THREE.Quaternion();
q2.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
fly.quaternion.multiply(q2);
// 在物体上次旋转基础上,进行旋转
const q3 = new THREE.Quaternion();
q3.setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI / 2);
fly.quaternion.multiply(q3);
四元数乘法顺序
四元数乘法不满足交换律,q1.clone().multiply(q2)
和q2.clone().multiply(q1)
表示的旋转结果不同。
// 在物体原来姿态基础上,进行旋转
const q1 = new THREE.Quaternion();
q1.setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI / 2);
fly.quaternion.multiply(q1);
// 在物体上次旋转基础上,进行旋转
const q2 = new THREE.Quaternion();
q2.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
fly.quaternion.multiply(q2);
先变换q1,后变换q2,和上面代码效果一样
const q1 = new THREE.Quaternion();
q1.setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI / 2);
const q2 = new THREE.Quaternion();
q2.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
const newQ= q1.clone().multiply(q2);
fly.quaternion.multiply(newQ);
先变换q2,后变换q1,和上面代码效果不一样,q2.clone().multiply(q1)
与q1.clone().multiply(q2)
的表示旋转过程顺序不同
// 先变换q2,后变换q1,和上面代码效果不一样,
// q2.clone().multiply(q1)与q1.clone().multiply(q2)表示的旋转过程顺序不同
const q1 = new THREE.Quaternion();
q1.setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI / 2);
const q2 = new THREE.Quaternion();
q2.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
const newQ= q2.clone().multiply(q1);
fly.quaternion.multiply(newQ);
.multiply()
与.copy()
总结
A.multiply(B)
表示A乘以B,结果赋值给A,在A的基础上旋转B。
A.copy(B)
表示用B的值替换A的值,A表示的旋转会被B替换。
可以先通过欧拉角改变物体的姿态,先物体一个初始的角度状态。
//改变物体欧拉角,四元数属性也会同步改变
fly.rotation.x = Math.PI/2;
创建一个四元数表示一个旋转过程。
const quaternion = new THREE.Quaternion();
quaternion.setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI / 2);
执行fly.quaternion.copy(quaternion)
,参数quaternion
表示的旋转会完全覆盖已有的旋转fly.quaternion
。无论物体原来的姿态角度是什么样,都会被参数quaternion
表示新的姿态角度覆盖。
//quaternion表示旋转角度复制给物体.quaternion
fly.quaternion.copy(quaternion);
.quaternion.multiply(quaternion)
表示在自身已有旋转的基础上,增加参数quaternion表示的旋转。
fly.quaternion.multiply(quaternion);
四元数表示两个向量旋转
3D空间中有两个向量,一个向量向另外一个向量旋转,这个过程可以用一个四元数表示。
下面给大家介绍一个四元数Quaternion
的方法.setFromUnitVectors(a, b)
,计算参数a到b的旋转。
已知条件
已知飞行原来的飞行方向是a表示的方向,需要把飞机飞行方向旋转到向量b表示的方向。
const model = new THREE.Group();
loader.load("../飞机.glb", function (gltf) {
const fly = gltf.scene
model.add(fly);
fly.position.set(10, 10, 0);//相对世界坐标系坐标原点偏移
const axesHelper = new THREE.AxesHelper(10);
fly.add(axesHelper);//用一个坐标轴可视化模型的局部坐标系(本地坐标系)
const a = new THREE.Vector3(0, 0, -1);//飞机初始姿态飞行方向
// 飞机姿态绕自身坐标原点旋转到b指向的方向
const b = new THREE.Vector3(-1, -1, -1).normalize();
})
箭头可视化飞机旋转前后的方向
// 可视化飞机方向
const a = new THREE.Vector3(0, 0, -1);//飞机初始姿态飞行方向
const O = fly.position.clone();//飞机位置坐标箭头起点
model.add(new THREE.ArrowHelper(a, O, 30, 0xff0000));
// 飞机姿态绕自身坐标原点旋转到b指向的方向
const b = new THREE.Vector3(-1, -1, -1).normalize();
model.add(new THREE.ArrowHelper(b, O, 30, 0x00ff00));
.setFromUnitVectors(a, b)
生成四元数旋转飞机
四元数Quaternion
的方法.setFromUnitVectors(a, b)
可以通过两个向量参数a和b,创建一个四元数,表示从向量a表示的方向旋转到向量b表示的方向。(参数a, b是单位向量)
//飞机初始姿态飞行方向a
const a = new THREE.Vector3(0, 0, -1);
// 飞机姿态绕自身坐标原点旋转到b指向的方向
const b = new THREE.Vector3(-1, -1, -1).normalize();
// a旋转到b构成的四元数
const quaternion = new THREE.Quaternion();
//注意两个参数的顺序
quaternion.setFromUnitVectors(a, b);
// quaternion表示的是变化过程,在原来基础上乘以quaternion即可
fly.quaternion.multiply(quaternion);
练习题:人旋转
已知人原来的正前方是a表示的方向,需要把人正前方旋转到向量b表示的方向。
这个旋转过程,可通过.setFromUnitVectors(a, b)
//人正前方从向量a表示方向旋转到向量b表示方向
const q = new THREE.Quaternion();
q.setFromUnitVectors(a,b);//a转向b
person.quaternion.multiply(q);
原文链接:https://juejin.cn/post/7317621632566951971 作者:郭隆邦Web3D可视化