歐拉角
歐拉角是一種表示三維空間中旋轉(zhuǎn)的方法,它由三個(gè)角度組成,通過設(shè)定物體繞 指定順序 的軸進(jìn)行旋轉(zhuǎn),可以直接對(duì)物體的 .rotation 屬性進(jìn)行操作。
rotation 屬性是一個(gè)歐拉角對(duì)象,表示物體的旋轉(zhuǎn)角度。歐拉角由三個(gè)角度組成,分別是繞 x 軸的旋轉(zhuǎn)角度、繞 y 軸的旋轉(zhuǎn)角度和繞 z 軸的旋轉(zhuǎn)角度。
// 創(chuàng)建一個(gè)立方體
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
scene.add(cube);
let euler = new THREE.Euler(Math.PI / 2, 0, 0);
cube.rotation.copy(euler);
console.log(cube.rotation);
Euler(x, y, z, order): 創(chuàng)建一個(gè)歐拉角對(duì)象,其中 x、y、z 分別表示繞 x 軸、y 軸和 z 軸的旋轉(zhuǎn)角度,order 表示旋轉(zhuǎn)順序。
換算: 歐拉角不能直接使用度數(shù),需要把度數(shù)轉(zhuǎn)換為弧度值,弧度 = (Math.PI / 180) * 度數(shù)。
簡(jiǎn)潔寫法: cube.rotation.set(x, y, z)。
注意: 歐拉角不能直接賦值,需要使用 .copy() 方法進(jìn)行賦值。
// 創(chuàng)建一個(gè)立方體
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
scene.add(cube);
let angle = 0;
function animate() {
angle += 1;
cube.rotation.x = (angle * Math.PI) / 180;
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
可以使用 THREE.Euler() 、 .set() 來統(tǒng)一操作 .rotation,也可以通過 .x 、 .y 、 .z 來分別操作。

方法
rotateX(angle): 繞 x 軸旋轉(zhuǎn) angle 弧度。
rotateY(angle): 繞 y 軸旋轉(zhuǎn) angle 弧度。
rotateZ(angle): 繞 z 軸旋轉(zhuǎn) angle 弧度。
區(qū)別: rotateX() 、 rotateY() 、 rotateZ() 多次使用,會(huì)疊加旋轉(zhuǎn)角度,而 set() 每次使用,都會(huì)覆蓋旋轉(zhuǎn)角度。
四元數(shù)
四元數(shù)是一種表示三維空間中旋轉(zhuǎn)的方法,它由四個(gè)部分組成,分別是 w、x、y、z。其中 w 是實(shí)部,x、y、z 是虛部。
為什么使用四元數(shù): 歐拉角在旋轉(zhuǎn)過程中會(huì)出現(xiàn)萬向節(jié)死鎖問題,而四元數(shù)不會(huì)。
萬向節(jié)死鎖
原理: 當(dāng)其中兩個(gè)旋轉(zhuǎn)軸重合時(shí),就會(huì)發(fā)生萬向節(jié)死鎖。這會(huì)致使失去一個(gè)自由度,進(jìn)而難以預(yù)測(cè)和控制物體的旋轉(zhuǎn)。在歐拉角的表示里,通常是當(dāng)繞其中一個(gè)軸旋轉(zhuǎn) ±90 度時(shí),另外兩個(gè)軸會(huì)重合,從而出現(xiàn)萬向節(jié)死鎖。
例如: 假設(shè)一個(gè)物體繞 Y 軸旋轉(zhuǎn) 90 度,此時(shí) X 軸和 Z 軸就會(huì)重合。這時(shí)候,不管是繞 X 軸旋轉(zhuǎn)還是繞 Z 軸旋轉(zhuǎn),產(chǎn)生的效果是一樣的,這就意味著失去了一個(gè)自由度,這種現(xiàn)象就是萬向節(jié)死鎖。
//萬向節(jié)死鎖示例
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
scene.add(cube);
//y軸旋轉(zhuǎn)90度
cube.rotation.y = Math.PI / 2;
let angle = 0;
function animate() {
angle += 1;
cube.rotation.x = (angle * Math.PI) / 180;
cube.rotation.z = (angle * Math.PI) / 180;
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
quaternion
在四元數(shù)中是通過操作quaternion來實(shí)現(xiàn)的,其中quaternion是一個(gè)四元數(shù)對(duì)象,表示物體的旋轉(zhuǎn)。
quaternion 屬性是一個(gè)四元數(shù)對(duì)象,表示物體的旋轉(zhuǎn)。四元數(shù)由四個(gè)部分組成,分別是 w、x、y、z。其中 w 是實(shí)部,x、y、z 是虛部。
1. 創(chuàng)建四元數(shù)
const quaternion = new THREE.Quaternion(x, y, z, w);
其中 x、y、z、w 分別是四元數(shù)的分量。如果不傳入?yún)?shù),則默認(rèn)為 (0, 0, 0, 1)。
2. 四元數(shù)計(jì)算公式
q = (x, y, z, w) = (sin(θ/2) * ux, sin(θ/2) * uy, sin(θ/2) * uz, cos(θ/2))
const angle = (30 * Math.PI) / 180; // 將角度轉(zhuǎn)換為弧度
const halfAngle = angle / 2;
const sinHalfAngle = Math.sin(halfAngle);
const cosHalfAngle = Math.cos(halfAngle);
const quaternion = new THREE.Quaternion(
sinHalfAngle * 1, //解釋
sinHalfAngle * 0,
sinHalfAngle * 0,
cosHalfAngle
);
繞著哪個(gè)軸旋轉(zhuǎn),哪個(gè)軸的分量就為 1,其他軸的分量就為 0。
3. 設(shè)置四元數(shù)
cube.quaternion.copy(quaternion);
簡(jiǎn)潔寫法: setFromAxisAngle(axis, angle) 從軸和角度創(chuàng)建四元數(shù)。
1. axis: 表示旋轉(zhuǎn)軸的向量,例如 new THREE.Vector3(1, 0, 0) 表示繞 x 軸旋轉(zhuǎn)。
2. angle: 表示旋轉(zhuǎn)角度,單位是弧度。
const quaternion = new THREE.Quaternion();
const axis = new THREE.Vector3(1, 0, 0); // 繞 x 軸旋轉(zhuǎn)
const angle = (Math.PI / 180) * 90; // 旋轉(zhuǎn) 90 度
quaternion.setFromAxisAngle(axis, angle);
方法
setFromEuler(euler): 從歐拉角創(chuàng)建四元數(shù)。
multiply(quaternion): 將當(dāng)前四元數(shù)與另一個(gè)四元數(shù)相乘,會(huì)改變當(dāng)前四元數(shù)。
multiplyQuaternions(a, b): 將兩個(gè)四元數(shù)相乘,返回一個(gè)新的四元數(shù)。
解決死鎖
// 解決萬向節(jié)死鎖示例
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
scene.add(cube);
//y軸旋轉(zhuǎn)90度
cube.quaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 2);
function animate() {
// 創(chuàng)建一個(gè)四元數(shù)qx
const qx = new THREE.Quaternion();
// 將qx設(shè)置為繞x軸旋轉(zhuǎn)0.01弧度的四元數(shù)
qx.setFromAxisAngle(new THREE.Vector3(1, 0, 0), 0.01);
// 將cube的旋轉(zhuǎn)四元數(shù)與qx相乘
cube.quaternion.multiplyQuaternions(qx, cube.quaternion);
// 創(chuàng)建一個(gè)四元數(shù)qz
const qz = new THREE.Quaternion();
// 將qz設(shè)置為繞z軸旋轉(zhuǎn)0.01弧度的四元數(shù)
qz.setFromAxisAngle(new THREE.Vector3(0, 0, 1), 0.01);
// 將cube的旋轉(zhuǎn)四元數(shù)與qz相乘
cube.quaternion.multiplyQuaternions(qz, cube.quaternion);
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
