Three.js中的矩陣

提起矩陣,很容易讓人想起咱們曾經學不會的線性代數和離散數學,可是做爲圖形開發中的核心部分,它表明着每一次的運動和變換,就像魚不能脫離水同樣,矩陣並非一個能夠避之不談的話題。javascript

好消息是,Three.js幫助咱們把許多矩陣運算封裝成了一些頂層的方法,並提供了一個優秀的數學庫,咱們不太須要知道如何計算,只須要知道如何用,就能夠獲得絕大部分咱們想要的東西。java

這篇文章將要介紹的就是,如何在不瞭解內部結構的狀況下在Three.js中使用矩陣和向量。api

從一個例子開始

在講解一些枯燥的概念前先舉一個小例子,來簡要說明一下爲何咱們要使用矩陣方法。bash

這是咱們最終要完成的效果。markdown

首先,咱們要建立三個幾何體:app

var box_geometry = new THREE.BoxGeometry();
var sphere_geometry = new THREE.SphereGeometry(0.5, 32, 32);
var cylinder_geometry = new THREE.CylinderGeometry(0.1, 0.1, 0.5);

var material = new THREE.MeshLambertMaterial({color: new THREE.Color(0.9, 0.55, 0.4)})
複製代碼

這三個幾何體分別是盒子、球和圓柱體。工具

而後去建立三個網格,並將它們置入場景。spa

var box = new THREE.Mesh(box_geometry, material);
var sphere = new THREE.Mesh(sphere_geometry, material);
sphere.position.y += 1;
var cylinder = new THREE.Mesh(cylinder_geometry, material);
cylinder.position.y += 1.75;
scene.add(box);
scene.add(sphere);
scene.add(cylinder);
複製代碼

這段代碼將生成以下場景:code

雖然不那麼美觀,但做爲示例已經足夠了,如今我但願這堆物體尺寸減半。一般我會把物體的scale屬性減半,像這樣:orm

box.scale.multiplyScalar(0.5); 
sphere.scale.multiplyScalar(0.5);
cylinder.scale.multiplyScalar(0.5);
複製代碼

和想象中的有些誤差。個人本意是讓這一組物體進行一個總體的縮放,並不想讓它們彼此偏離,爲了修正這件事,我須要根據其餘對象的縮放從新計算每一個對象的位置。但這並非一件很難解決的問題,three.js提供了一種優雅的方式,來處理這個問題。咱們能夠定義一個空對象,而後將三個對象放在其中,而後將比例應用於父對象。

var pile = new THREE.Object3D();
pile.scale.multiplyScalar(0.5);
pile.add(box);
pile.add(sphere);
pile.add(cylinder);
scene.add(pile);
複製代碼

接下來咱們作一點更有趣的事。

我將在這個物體組合裏添加旋轉,讓咱們嘗試圍繞球體表面旋轉的那個圓柱體,就像他將要滑落同樣。

它變成了這樣,很明顯,這不是我想要的東西。咱們在這裏有兩個作法可供選擇:第一,經過數學計算算出圓柱相對於球體的正確位置;第二,建立另外一個Object3D,將圓柱和球放進去並旋轉。這聽上去挺複雜的,並且也很不酷。

因此,咱們能夠嘗試本身去計算矩陣。

首先,我須要將屬性maxtrixAutoUpdate設置爲false,而後我就不能再經過position,scale和rotation去修改矩陣。

box.matrixAutoUpdate = false;
sphere.matrixAutoUpdate = false;
cylinder.matrixAutoUpdate = false;
複製代碼

如今,我將用applyMatrix方法來解決這個問題。具體作法是:爲每一個對象建立一個Matrix4,而後咱們將矩陣與該矩陣相乘以應用後續操做。

var sphere_matrix = new THREE.Matrix4().makeTranslation(0.0, 1.0, 0.0); 
sphere.applyMatrix(sphere_matrix);
var cylinder_matrix = sphere_matrix.clone(); cylinder_matrix.multiply(new THREE.Matrix4().makeTranslation(0.0, 0.75, 0.0)); 
cylinder.applyMatrix(cylinder_matrix);
複製代碼

這幾步下來,可讓咱們解鎖不少知識,來看看這裏發生了什麼。

首先,咱們把盒子單獨留下,由於它不須要動。

接着,我建立了一個平移矩陣並把它應用到了球對象上。

最後,在圓柱體上,我clone了球的矩陣信息,並在此基礎上又建立了一個新的平移矩陣,圓柱體將移動1.75。

理解了上面幾步,你就會知道最後一步該作什麼了。

只須要一行代碼,做用在球上:

sphere_matrix.multiply(new THREE.Matrix4().makeRotationZ(-Math.PI * 0.25));
複製代碼

達成了想要的效果,很酷。

示例中用到的方法

在上面的示例中,我將球和圓柱體分別沿y軸移動了必定的距離,並使用了makeTranslation這個方法。這個方法的做用是建立了一個平移矩陣。緊接着,我又使用到了applyMatrix的方法。這個方法的做用是把平移矩陣做用在球和圓柱體上。

那麼什麼是平移矩陣?它又是如何完成一次平移呢?

Three.js中最多見的一種4x4的矩陣,被稱爲變換矩陣,它所表示的變換類型包括平移、旋轉和縮放。

用一個簡單的數學題來講明變換矩陣:

有一個起始點,用向量來表示即爲Vector3(20,20,0);如今,我要把它移動到另外一個位置,Vector3(30,60,0)。

接下來,我設置一個平移矩陣,來表示向量依照什麼方式去移動。

t = |1 0 0 10|
    |0 1 0 40|
    |0 0 1 0 |
    |0 0 0 1 |
複製代碼

最後,用起始的向量去乘以變換矩陣的向量。

|20|   |1 0 0 10|   |30|
|20| x |0 1 0 40| = |60|
|0 |   |0 0 1 0 |   |0 |
|1 |   |0 0 0 1 |   |1 |
複製代碼

變換公式以下:

transformedVector = vector * transformationMatrix

最終的變換向量 = 原始向量 * 變換矩陣

用咱們上面例子中的方法來還原這個公式,即:

var vector = new THREE.Vector3(20, 20, 0);
var matrix = new THREE.Matrix4();
matrix.makeTranslation(10, 40, 0);
vector.applyMatrix4(matrix);
複製代碼

除了平移,Three的API中還提供了rotation和scale,scale變化很簡單,它將使用makeScale(x, y, z)這個方法來表示縮放。

而旋轉則相對複雜許多,Three.js提供如下旋轉方法:

matrix.makeRotationX(angle);
matrix.makeRotationY(angle);
matrix.makeRotationZ(angle);
matrix.makeRotationAxis(axis, angle);
matrix.makeRotationFromEuler(euler);
matrix.makeRotationFromQuaternion(quaternion);
複製代碼

前三個方法分別表明的是繞X、Y、Z三個軸旋轉,無需贅述。

第四個方法是前三個方法的整合版,第一個參數表示的是表明xyz的THREE.Vector3,第二個參數是旋轉的弧度。下面兩行代碼是等價的:

matrix.makeRotationX(Math.PI);
matrix.makeRotationAxis(new THREE.Vector3(1, 0, 0), Math.PI);
複製代碼

第五個方法表示圍繞x、y和z軸的旋轉,這是表示旋轉最經常使用的方式;第六個方法是一種基於軸和角度表示旋轉的替代方法。

最後,Three.js api提供了一種方法來建立表示平移,旋轉和縮放的組合的矩陣 -- matrix.compose:

var translation = new THREE.Vector3();
var rotation = new THREE.Quaternion();
var scale = new THREE.Vector3();
var matrix = new THREE.Matrix4();
matrix.compose(translation, rotation, scale);
複製代碼

矩陣相乘

矩陣乘法的意義在於疊加。

上圖表示了三個變化:旋轉、縮放和移動。

經過按次序相乘,三個變化矩陣能夠得出一個最終的變化矩陣:

combinedMatrix = rotationMatrix * scaleMatrix * translationMatrix;
複製代碼

Three.js裏提供了兩種矩陣相乘的方法:

  1. matrix.multiply(otherMatrix)
  2. matrix.multiplyMatrices(matrixA, matrixB)

第一種方法表示將矩陣乘以另外一個矩陣;而第二種方法表明的是將矩陣設置爲matrixA * matrixB的結果。

咱們在示例中也使用到了第一個方法:將圓柱體的矩陣乘以新的平移矩陣,和將球的矩陣乘以一個旋轉矩陣。

須要注意的是,乘法交換律不適用於矩陣乘法,矩陣乘法是具備次序的,先旋轉再移動和先移動再旋轉的結果是徹底不一樣的。

矩陣的逆

在數字的運算裏,除法至關因而乘法的「撤銷」操做:

4 x 5 = 20
20 / 5 = 4
複製代碼

可是在矩陣計算裏,這個守則一樣是不適用的。咱們不能用向量去除一個矩陣,咱們只能用向量去乘以一個矩陣的逆矩陣,來完成「撤銷」的操做。

變化後的向量 = 原始向量 * 變化矩陣;
逆矩陣 = 變化矩陣.inverse();
原始向量 = 變化後的向量 * 逆矩陣;
複製代碼

逆矩陣表示的是相反的變換

Three.js裏提供了一種計算逆矩陣的方法:

var matrix = new THREE.Matrix4();
var inverseMatrix = new THREE.Matrix4();
matrix.getInverse(inverseMatrix);
複製代碼

除此以外,逆矩陣還應用在3D場景中處理相機對象的時候。

最後

矩陣在3D世界裏是一種十分強大的工具,它可以將任意變換都表示爲一種類似的結構,並採用相同的計算過程。而實際上,矩陣的世界遠遠比這裏介紹的內容更多,但願經過這些簡要的介紹,可讓咱們進入到一個更深的領域,並遊刃有餘的利用他處理圖形開發中更復雜的場景。

相關文章
相關標籤/搜索