Three.js中矩陣和向量的使用教程(附例子)

https://www.jb51.net/article/158076.htm

 /**
   * 創(chuàng)建場景對象Scene
   */
  var scene = new THREE.Scene();
  /**
   * 創(chuàng)建網(wǎng)格模型
   */
  var box_geometry = new THREE.BoxGeometry(32, 32, 32);
  var sphere_geometry = new THREE.SphereGeometry(16, 32, 32);
  var cylinder_geometry = new THREE.CylinderGeometry(8, 8, 32);

  var material = new THREE.MeshLambertMaterial({color: new THREE.Color(0.9, 0.55, 0.4)})
  var box = new THREE.Mesh(box_geometry, material);
  var sphere = new THREE.Mesh(sphere_geometry, material);
  // sphere.position.y += 32;
  var cylinder = new THREE.Mesh(cylinder_geometry, material);
  // cylinder.position.y += 64;
  scene.add(box);
  scene.add(sphere);
  scene.add(cylinder);

  // box.scale.multiplyScalar(0.5);
  // sphere.scale.multiplyScalar(0.5);
  // cylinder.scale.multiplyScalar(0.5);

  // var pile = new THREE.Object3D();
  // pile.scale.multiplyScalar(1);
  // pile.add(box);
  // pile.add(sphere);
  // pile.add(cylinder);
  // scene.add(pile);

  box.matrixAutoUpdate = false;
  sphere.matrixAutoUpdate = false;
  cylinder.matrixAutoUpdate = false;
  var sphere_matrix = new THREE.Matrix4().makeTranslation(0.0, 32, 0.0);
  sphere.applyMatrix(sphere_matrix);
  sphere_matrix.multiply(new THREE.Matrix4().makeRotationZ(-Math.PI * 0.25));
  var cylinder_matrix = sphere_matrix.clone();
  cylinder_matrix.multiply(new THREE.Matrix4().makeTranslation(0.0, 32, 0.0));
  cylinder.applyMatrix(cylinder_matrix);
  // cylinder.applyMatrix(cylinder_matrix);
  /**
   * 輔助坐標系  參數(shù)250表示坐標系大小翩瓜,可以根據(jù)場景大小去設(shè)置
   * */
  var axisHelper = new THREE.AxisHelper(250);
  scene.add(axisHelper);
  /**
   * 光源設(shè)置
   */
    //點光源
  var point = new THREE.PointLight(0xffffff);
  // point.position.set(400, 200, 300); //點光源位置
  point.position.set(0, 0, 0); //點光源位置
  scene.add(point); //點光源添加到場景中
  //環(huán)境光
  var ambient = new THREE.AmbientLight(0x444444);
  scene.add(ambient);
  // console.log(scene)
  // console.log(scene.children)
  /**
   * 相機設(shè)置
   */
  var width = window.innerWidth; //窗口寬度
  var height = window.innerHeight; //窗口高度
  var k = width / height; //窗口寬高比
  var s = 400; //三維場景顯示范圍控制系數(shù),系數(shù)越大正压,顯示的范圍越大
  //創(chuàng)建相機對象
  var camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
  camera.position.set(100, 100, 400); //設(shè)置相機位置
  camera.lookAt(scene.position); //設(shè)置相機方向(指向的場景對象)
  /**
   * 創(chuàng)建渲染器對象
   */
  var renderer = new THREE.WebGLRenderer();
  renderer.setSize(width, height);//設(shè)置渲染區(qū)域尺寸
  renderer.setClearColor(0xb9d3ff, 1); //設(shè)置背景顏色
  document.body.appendChild(renderer.domElement); //body元素中插入canvas對象
  //執(zhí)行渲染操作   指定場景锨络、相機作為參數(shù)
  renderer.render(scene, camera);

  // 例子:響應鼠標忧饭、鍵盤
  function render() {
    renderer.render(scene,camera);//執(zhí)行渲染操作
  }
  render();
  var controls = new THREE.OrbitControls(camera,renderer.domElement);//創(chuàng)建控件對象
  controls.addEventListener('change', render);//監(jiān)聽鼠標县忌、鍵盤事件

前言

提起矩陣,很容易讓人想起我們曾經(jīng)學不會的線性代數(shù)和離散數(shù)學探颈,但是作為圖形開發(fā)中的核心部分熟丸,它代表著每一次的運動和變換,就像魚不能脫離水一樣伪节,矩陣并不是一個可以避之不談的話題光羞。

好消息是,Three.js幫助我們把許多矩陣運算封裝成了一些頂層的方法架馋,并提供了一個優(yōu)秀的數(shù)學庫狞山,我們不太需要知道HowToCalc,只需要知道HowToUse叉寂,就可以得到絕大部分我們想要的東西萍启。

這篇文章將要介紹的就是,如何在不了解內(nèi)部結(jié)構(gòu)的情況下在Three.js中使用矩陣和向量。

從一個例子開始

在講解一些枯燥的概念前先舉一個小例子勘纯,來簡要說明一下為什么我們要使用矩陣方法局服。

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

首先驳遵,我們要創(chuàng)建三個幾何體:

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)})

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

然后去創(chuàng)建三個網(wǎng)格堤结,并將它們置入場景唆迁。

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);

這段代碼將生成如下場景:

雖然不那么美觀,但作為示例已經(jīng)足夠了竞穷,現(xiàn)在我希望這堆物體尺寸減半唐责。通常我會把物體的scale屬性減半,像這樣:

box.scale.multiplyScalar(0.5); 
sphere.scale.multiplyScalar(0.5);
cylinder.scale.multiplyScalar(0.5);

和想象中的有些偏差瘾带。我的本意是讓這一組物體進行一個整體的縮放鼠哥,并不想讓它們彼此偏離,為了修正這件事看政,我需要根據(jù)其他對象的縮放重新計算每個對象的位置朴恳。但這并不是一件很難解決的問題,three.js提供了一種優(yōu)雅的方式允蚣,來處理這個問題于颖。我們可以定義一個空對象,然后將三個對象放在其中嚷兔,然后將比例應用于父對象恍飘。

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

接下來我們做一點更有趣的事。

我將在這個物體組合里添加旋轉(zhuǎn)谴垫,讓我們嘗試圍繞球體表面旋轉(zhuǎn)的那個圓柱體,就像他將要滑落一樣母蛛。

它變成了這樣翩剪,很明顯,這不是我想要的東西彩郊。我們在這里有兩個做法可供選擇:第一前弯,通過數(shù)學計算算出圓柱相對于球體的正確位置;第二秫逝,創(chuàng)建另一個Object3D恕出,將圓柱和球放進去并旋轉(zhuǎn)。這聽上去挺復雜的违帆,而且也很不酷浙巫。

所以,我們可以嘗試自己去計算矩陣。

首先的畴,我需要將屬性maxtrixAutoUpdate設(shè)置為false渊抄,然后我就不能再通過position,scale和rotation去修改矩陣丧裁。

box.matrixAutoUpdate = false;
sphere.matrixAutoUpdate = false;
cylinder.matrixAutoUpdate = false;

現(xiàn)在护桦,我將用applyMatrix方法來解決這個問題。具體做法是:為每個對象創(chuàng)建一個Matrix4煎娇,然后我們將矩陣與該矩陣相乘以應用后續(xù)操作二庵。

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);

這幾步下來,可以讓我們解鎖很多知識缓呛,來看看這里發(fā)生了什么催享。

首先,我們把盒子單獨留下强经,因為它不需要動睡陪。

接著,我創(chuàng)建了一個平移矩陣并把它應用到了球?qū)ο笊稀?/p>

最后匿情,在圓柱體上兰迫,我clone了球的矩陣信息,并在此基礎(chǔ)上又創(chuàng)建了一個新的平移矩陣炬称,圓柱體將移動1.75汁果。

理解了上面幾步,你就會知道最后一步該做什么了玲躯。

只需要一行代碼据德,作用在球上:

sphere_matrix.multiply(new THREE.Matrix4().makeRotationZ(-Math.PI * 0.25));

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

示例中用到的方法

在上面的示例中棘利,我將球和圓柱體分別沿y軸移動了一定的距離,并使用了makeTranslation這個方法朽缴。這個方法的作用是創(chuàng)建了一個平移矩陣善玫。緊接著,我又使用到了applyMatrix的方法密强。這個方法的作用是把平移矩陣作用在球和圓柱體上茅郎。

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

Three.js中最常見的一種4x4的矩陣系冗,被稱為變換矩陣,它所表示的變換類型包括平移薪鹦、旋轉(zhuǎn)和縮放掌敬。

用一個簡單的數(shù)學題來說明變換矩陣:

有一個起始點惯豆,用向量來表示即為Vector3(20,20,0);現(xiàn)在涝开,我要把它移動到另一個位置循帐,Vector3(30,60,0)。

接下來舀武,我設(shè)置一個平移矩陣拄养,來表示向量依照什么方式去移動。

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)這個方法來表示縮放诚欠。

而旋轉(zhuǎn)則相對復雜許多顽染,Three.js提供以下旋轉(zhuǎn)方法:

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

前三個方法分別代表的是繞X、Y轰绵、Z三個軸旋轉(zhuǎn)粉寞,無需贅述。

第四個方法是前三個方法的整合版左腔,第一個參數(shù)表示的是代表xyz的THREE.Vector3唧垦,第二個參數(shù)是旋轉(zhuǎn)的弧度。下面兩行代碼是等價的:

matrix.makeRotationX(Math.PI);
matrix.makeRotationAxis(new THREE.Vector3(1, 0, 0), Math.PI);

第五個方法表示圍繞x液样、y和z軸的旋轉(zhuǎn)振亮,這是表示旋轉(zhuǎn)最常用的方式;第六個方法是一種基于軸和角度表示旋轉(zhuǎn)的替代方法鞭莽。

最后坊秸,Three.js api提供了一種方法來創(chuàng)建表示平移,旋轉(zhuǎn)和縮放的組合的矩陣 -- 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);

矩陣相乘

矩陣乘法的意義在于疊加澎怒。

上圖表示了三個變化:旋轉(zhuǎn)妇斤、縮放和移動。

通過按次序相乘丹拯,三個變化矩陣可以得出一個最終的變化矩陣:

combinedMatrix = rotationMatrix * scaleMatrix * translationMatrix;

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

  • matrix.multiply(otherMatrix)
  • matrix.multiplyMatrices(matrixA, matrixB)

第一種方法表示將矩陣乘以另一個矩陣;而第二種方法代表的是將矩陣設(shè)置為matrixA * matrixB的結(jié)果荸恕。

我們在示例中也使用到了第一個方法:將圓柱體的矩陣乘以新的平移矩陣乖酬,和將球的矩陣乘以一個旋轉(zhuǎn)矩陣。

需要注意的是融求,乘法交換律不適用于矩陣乘法咬像,矩陣乘法是具有次序的,先旋轉(zhuǎn)再移動和先移動再旋轉(zhuǎn)的結(jié)果是完全不同的。

矩陣的逆

在數(shù)字的運算里县昂,除法相當于是乘法的“撤銷”操作:

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世界里是一種十分強大的工具痴晦,它能夠?qū)⑷我庾儞Q都表示為一種相似的結(jié)構(gòu),并采用相同的計算過程琳彩。而實際上誊酌,矩陣的世界遠遠比這里介紹的內(nèi)容更多,希望通過這些簡要的介紹露乏,可以讓我們進入到一個更深的領(lǐng)域碧浊,并游刃有余的利用他處理圖形開發(fā)中更復雜的場景。

好了施无,以上就是這篇文章的全部內(nèi)容了辉词,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對腳本之家的支持猾骡。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瑞躺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子兴想,更是在濱河造成了極大的恐慌幢哨,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嫂便,死亡現(xiàn)場離奇詭異捞镰,居然都是意外死亡,警方通過查閱死者的電腦和手機毙替,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門岸售,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人厂画,你說我怎么就攤上這事凸丸。” “怎么了袱院?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵屎慢,是天一觀的道長瞭稼。 經(jīng)常有香客問我,道長腻惠,這世上最難降的妖魔是什么环肘? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮集灌,結(jié)果婚禮上悔雹,老公的妹妹穿的比我還像新娘。我一直安慰自己绝页,他們只是感情好荠商,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著续誉,像睡著了一般莱没。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上酷鸦,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天饰躲,我揣著相機與錄音,去河邊找鬼臼隔。 笑死嘹裂,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的摔握。 我是一名探鬼主播寄狼,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼氨淌!你這毒婦竟也來了泊愧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤盛正,失蹤者是張志新(化名)和其女友劉穎删咱,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體豪筝,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡痰滋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了续崖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敲街。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖严望,靈堂內(nèi)的尸體忽然破棺而出多艇,到底是詐尸還是另有隱情,我是刑警寧澤著蟹,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布墩蔓,位于F島的核電站,受9級特大地震影響萧豆,放射性物質(zhì)發(fā)生泄漏奸披。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一涮雷、第九天 我趴在偏房一處隱蔽的房頂上張望阵面。 院中可真熱鬧,春花似錦洪鸭、人聲如沸样刷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽置鼻。三九已至,卻和暖如春蜓竹,著一層夾襖步出監(jiān)牢的瞬間箕母,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工俱济, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嘶是,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓蛛碌,卻偏偏與公主長得像聂喇,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蔚携,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容