three.js四元数Quaternion

参考资料: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四元数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);

四元数表示两个向量旋转

three.js四元数Quaternion

3D空间中有两个向量,一个向量向另外一个向量旋转,这个过程可以用一个四元数表示。

下面给大家介绍一个四元数Quaternion的方法.setFromUnitVectors(a, b),计算参数a到b的旋转。

已知条件

已知飞行原来的飞行方向是a表示的方向,需要把飞机飞行方向旋转到向量b表示的方向。

three.js四元数Quaternion

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表示的方向。

three.js四元数Quaternion

这个旋转过程,可通过.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可视化

(0)
上一篇 2023年12月29日 下午4:21
下一篇 2023年12月29日 下午4:31

相关推荐

发表回复

登录后才能评论