三維空間中主要有兩種幾何變換,一種是位置的變換,位置變換和二維空間的是一樣的。假設一點P(X1,Y1,Z1) 移動到Q(X2,Y2,Z2)只要簡單的讓P點的坐標值加上偏移值就可以了。但是三維空間的旋轉變換就不能簡單的使用二維空間的變換了。下面詳細介紹一下三維空間的旋轉。
三維空間的旋轉:
二維空間的旋轉可以看作是圍繞點的旋轉,只有一個自由度。而三維空間的旋轉是圍繞一條線旋轉的。當旋轉的軸是Z軸時,旋轉可以看作是在二維平面XY平面的旋轉,旋轉的中心點是P(x=0,y=0)。按照右手法則,讓拇指指向Z軸的正方向,四指指向為旋轉的正方向。按照Y軸和X軸的旋轉也類似。按照不同的軸的旋轉可以進行組合。比如,先按照Z軸旋轉45度,再按照Y軸旋轉45度。但是每一個朝向都可以看成是物體在原始位置處圍繞某一個軸轉動了一個角度形成的。
三維空間的旋轉有多種方式,如旋轉矩陣,歐拉角,四元數:
1 .歐拉角(Euler Angle)
歐拉角這種旋轉方式是最直觀的,因為這種方式是將旋轉表示為物體按坐標系的三個軸X(1,0,0) ,Y(0,1,0),Z(0,0,1)的旋轉組合成的。這里首先要明確兩個概念,1參考系:類似於物理中的參考系,是靜止不動的,比如北極星,不管在那里,那就是北。2坐標系:坐標系是固定於物體的,隨着物體的轉動而發生變化,最簡單的例子就是左右,人所說的左邊一直是根據人所面對的方向來決定的。在下圖中藍色為參考系的三個軸,而紅色是物體的坐標系的三個軸。雖然說歐拉角表示的旋轉是有多個沿坐標軸的旋轉組合而成的。但是旋轉的順序不同旋轉就不同,所以,歐拉角中旋轉的順序要注明。這里的三個角是zxz順規的歐拉角。物體先繞Z軸旋轉了α°,然后物體坐標系的x軸的位置變化到了圖中N的位置,然后繞這個N軸(X軸)旋轉β°,最后再沿Z軸旋轉γ°。
歐拉角的旋轉雖然是最直觀的,但是卻存在一個非常致命的問題--萬向節鎖,大致是指。按某一個旋轉了90°的時候會使另外兩個軸重合,從而喪失一個自由度。無法很好的追蹤物體。具體的可以看一下這個視頻萬向節鎖。講得非常清楚,這里也就不再贅述了。
歐拉角旋轉應用於很多第一人稱3D游戲中,雖然萬向節鎖是無法避免的,但可以將觸發的概率減至最小。只有當人物視線完全朝上或者朝下的時候才會觸發萬向節鎖。
2 .旋轉矩陣(Rotation Matrix)
三維空間的旋轉使用旋轉矩陣的方式表示比較容易,旋轉矩陣是一個3x3的矩陣。可以看作物體沿空間中的某一個軸(x,y,z)旋轉了θ°。三維的旋轉矩陣可以表示為:
其中:c=cos(θ) , s=sin(θ),t=1-cos(θ)
假設物體在初始位置的旋轉矩陣為A ,經過旋轉之后的旋轉矩陣為A` ,那么A,A`以及R之間的關系可以表示為 A` = AR。
3 .四元數(Quaternion)
四元數是一種高階復數,四元數q表示為:q =(x,y ,z ,w)=xi+y j+z k+w 可以將四元數寫成一個向量和一個實數的組合。q =(v ⃗ +w)=((x,y ,z ),w) ,四元數也可用矩陣表示。
可以證明這個矩陣和旋轉矩陣是一個矩陣。
在數學上四元數來表示的旋轉要好一些,但是在程序設計里,旋轉矩陣更方便。在Three.js中使用四元數的方式也是將四元數換成了旋轉矩陣使用的。
Three.JS中的旋轉實現
three.js支持上面所說的三種旋轉方式。
1.歐拉角
three.js中場景里的物體都屬於Object3D這個類。這個類中有表示物體朝向的一個屬性–rotation,這是一個歐拉類型的值,有三個軸旋轉的角度,單位是π,還有一個旋轉的順序組成。要使物體旋轉可以改動這個rotation的值。比如讓物體按Y軸正方向旋轉45°可以寫成 object3D.rotation.y+=0.25*Math.PI。這里的旋轉順序默認使用的是‘XYZ’順序。另外Object3D 還提供了幾個方法來進行旋轉,rotateX(angle),rotateY(angle),rotateZ(angle),分別是按照X,Y,Z軸的正反向旋轉一個角度。但具體使用的時候和 object3D.rotation.y+=0.25*Math.PI 這種方式還是有一些區別的。另外還有一個方法是rotateOnAxis(axis,angle) 可以指定旋轉的軸,這里的axis是一個Vector3類型的值。
2.矩陣
three.js里的矩陣和之前介紹的不太一樣,因為three.js里的矩陣式一個4x4的復合的矩陣(Transform Matrix),將位置信息放在了矩陣的最后一行。可以很容易的從這個矩陣里分解出旋轉矩陣(Rotation Matrix),和位置矩陣(Translation Matrix)。Matrix4的主要方法有:
mutiply():矩陣的乘法。
transpose() :矩陣轉置。
getInverse(m) :求逆矩陣。
makeRotationFromEuler(euler) :通過一個歐拉類型的值來設置矩陣的值。
makeRotationFromQuaternion(q):通過一個四元數類型的值來設置矩陣。
makeRotationonAxis(axis,theta):按一個軸旋轉θ°,然后設置矩陣的值。
makeRotationonAxis(axis,theta) 的源碼為:
makeRotationAxis: function ( axis, angle ) {
var c = Math.cos( angle );
var s = Math.sin( angle );
var t =
1
- c;
var x = axis.x, y = axis.y, z = axis.z;
var tx = t * x, ty = t * y;
this
.set(
tx * x + c, tx * y - s * z, tx * z + s * y,
0
,
tx * y + s * z, ty * y + c, ty * z - s * x,
0
,
tx * z - s * y, ty * z + s * x, t * z * z + c,
0
,
0
,
0
,
0
,
1
);
return
this
;
}
|
通過源碼,可以看出,調用這個方法就可以得到一個旋轉矩陣了。然后,讓物體的矩陣乘旋轉矩陣就可以得到物體轉動后的矩陣了。
下面是一段我使用旋轉矩陣來旋轉物體的代碼。
var rotWorldMatrix;
function rotateAroundWorldAxis(object, axis, radians) {
rotWorldMatrix =
new
THREE.Matrix4();
rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);
rotWorldMatrix.multiply(object.matrix);
// pre-multiply
object.matrix = rotWorldMatrix;
object.rotation.setFromRotationMatrix(object.matrix);
}
|
通過這樣的方法,就可以讓物體沿參考系中的確定的一個軸進行旋轉了。
3.四元數
沒有使用四元數,所以沒有研究,在object3D中設置rotation可以通過Matrix ,也可以通過Quaternion ,不過setFromRotationQuaternion的內部也是用的Matrix。
參考資料
https://www.fastgraph.com/makegames/3drotation/
http://www.essentialmath.com/GDC2012/GDC2012_JMV_Rotations.pdf