Three.js 學習筆記(1)--坐標體系和旋轉
前言
JavaScript 3D library
The aim of the project is to create an easy to use, lightweight, 3D library. The library provides <canvas>, <svg>, CSS3D and WebGL renderers.(該項目的目標是創建一個易於使用,輕量級的3D庫。該庫提供了<canvas>,<svg>,CSS3D和WebGL渲染器。)
示例 - 文檔 - Wiki - 遷移 - 幫助
官網:https://threejs.org
Github:https://github.com/mrdoob/three.js
學習THREE.JS,可以從《three.js開發指南》開始,有中文電子文檔PDF。電子文檔代碼:https://github.com/josdirksen/learning-threejs。
1、3D空間的坐標體系
在threejs中,xyz軸的坐標如圖所示,X軸紅色,Y軸綠色,Z軸藍色。
http://www.cnblogs.com/silent-stranger/p/6307372.html
1.相機坐標系(Camera Space)
有一個比較特殊的物體坐標系就是相機坐標系,這個坐標系是我們的視點用來渲染看到的場景所用的坐標系。在相機坐標系中,相機一直處於相機坐標系的原點,+x的方向是向右的,+z的方向是向前的(向屏幕裏的方向,因為使用的是左手坐標系),+y的方向是向上。這裏的像上不是世界坐標的正上,而是一個相機的上方向。可以來看一下這個相機坐標系的圖片。
這裏要註意一些細節,相機的坐標是三維空間,而我們在屏幕上看到的是二維的。
因為我們是做了一個映射,把三維空間的東西映射到了一個二維的平面上,這個映射就是大家知道的“投影”了。
2、二維平面旋轉公式
1 x1=Math.cos(angle)*x-Math.sin(angle)*y; 2 y1=Math.cos(angle)*y+Math.sin(angle)*x; 3 //其中x,y表示物體相對於旋轉點旋轉angle的角度之前的坐標, 4 // x1,y1表示物體旋轉angle後相對於旋轉點的坐標
從數學上來說,此公式可以用來計算某個點繞另外一點旋轉一定角度後的坐標,
例如:A(x,y)繞B(a,b)旋轉β度後的位置為C(c,d),則x,y,a,b,β,c,d有如下關系式:
1。設A點旋轉前的角度為δ,則旋轉(逆時針)到C點後角度為δ+β
2。求A,B兩點的距離:dist1=|AB|=y/sin(δ)=x/cos(δ)
3。求C,B兩點的距離:dist2=|CB|=d/sin(δ+β)=c/cos(δ+β)
4。顯然dist1=dist2,設dist1=r所以:
r=x/cos(δ)=y/sin(δ)=d/sin(δ+β)=c/cos(δ+β)
5。由三角函數兩角和差公式知:
sin(δ+β)=sin(δ)cos(β)+cos(δ)sin(β)
cos(δ+β)=cos(δ)cos(β)-sin(δ)sin(β)
所以得出:
c=r*cos(δ+β)=r*cos(δ)cos(β)-r*sin(δ)sin(β)=x*cos(β)-y*sin(β)
d=r*sin(δ+β)=r*sin(δ)cos(β)+r*cos(δ)sin(β)=y*cos(β)+x*sin(β)
即旋轉後的坐標c,d只與旋轉前的坐標x,y及旋轉的角度β有關
從圖中可以很容易理解出A點旋轉後的C點總是在圓周上運動,圓周的半徑為|AB|,利用這點就可以使物體繞圓周運動,即旋轉物體。
3、三維空間旋轉公式
【轉】https://www.cnblogs.com/silent-stranger/p/6027266.html
二維空間的旋轉可以看作是圍繞點的旋轉,只有一個自由度。而三維空間的旋轉是圍繞一條線旋轉的。當旋轉的軸是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中使用四元數的方式也是將四元數換成了旋轉矩陣使用的。
4、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) 的源碼為:
1 makeRotationAxis: function ( axis, angle ) { 2 var c = Math.cos( angle ); 3 var s = Math.sin( angle ); 4 var t = 1 - c; 5 var x = axis.x, y = axis.y, z = axis.z; 6 var tx = t * x, ty = t * y; 7 this.set( 8 tx * x + c, tx * y - s * z, tx * z + s * y, 0, 9 tx * y + s * z, ty * y + c, ty * z - s * x, 0, 10 tx * z - s * y, ty * z + s * x, t * z * z + c, 0, 11 0, 0, 0, 1 12 ); 13 return this; 14 }
通過源碼,可以看出,調用這個方法就可以得到一個旋轉矩陣了。然後,讓物體的矩陣乘旋轉矩陣就可以得到物體轉動後的矩陣了。
下面是一段我使用旋轉矩陣來旋轉物體的代碼。
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。
坐標體系:http://www.cnblogs.com/silent-stranger/p/6307372.html
三維空間旋轉:https://www.cnblogs.com/silent-stranger/p/6027266.html
三維旋轉矩陣:https://wenku.baidu.com/view/6dac0c22915f804d2b16c17c.html
參考資料
https://www.fastgraph.com/makegames/3drotation/
http://www.essentialmath.com/GDC2012/GDC2012_JMV_Rotations.pdf
http://www.gamedev.net/page/resources/_/technical/math-and-physics/do-we-really-need-quaternions-r1199
https://threejs.org/docs/
Three.js 學習筆記(1)--坐標體系和旋轉