拾取模型的原理及其在THREE.JS中的代碼實現

 

1. Three.js中的拾取 

 

 

 

1.1. 從模型轉到屏幕上的過程說開

 

  因爲圖形顯示的基本單位是三角形,那就先從一個三角形從世界座標轉到屏幕座標提及,例如三角形abchtml

 

 

 

 

 

 

 

 

 

乘以模型視圖矩陣就進入了視點座標系,其實就是相機所在的座標系,以下圖:web

 

 

 

進入視點座標系後,再乘以投影矩陣,就會變換到一個立方體內,以下圖:微信

 

 

 

這個時候整個三角形就位於中心位於座標系原點,邊長爲2的立方體內,在這個立方體內,三角形要計算光照,要裁剪,而後乘以視口矩陣,最後轉到屏幕上。優化

 

 

 

 

 

轉到屏幕上後,三角形的全部點的Z座標就是深度座標,必定在(0, 1)這個區間內,那麼哪些點的Z座標是0呢,在投影座標系中,必定是投影視景體的前剪切平面上的點,而投影視景體的後剪切平面上的點的Z座標就是1webgl

 

1.2. 思路來了

 

   根據以上三角形轉換到屏幕座標上的過程能夠分析出,鼠標在屏幕上點擊的時候,能夠獲得二維座標p(x, y),再加上深度座標的範圍(0, 1), 就能夠造成兩個三位座標A(x, y, 0), B(x, y, 1),  因爲它們的Z軸座標是01,則轉變到投影座標系的話,必定分別是前剪切平面上的點和後剪切平面上的點,也就是說,在投影座標系中,A點必定在能看見的全部模型的最前面,B點必定在能看見的全部的模型的最後邊,假設視口矩陣的逆矩幀,投影矩陣的逆矩陣,模型視圖矩陣的逆矩陣爲M, N, P,則 P * N * M * A = A1,  P * N * M * B = B1, 在世界座標系中,點A1B1就能夠造成一個射線,此射線和模型再求交,就能選中模型。以下圖是在視點座標系中的情形。注意,求交能夠在視點座標系或者世界座標系計算均可以,但通常會在世界座標座標系中計算。spa

 

 

 

1.3. 拾取的優化,射線和AABB包圍盒求交

 

    若是射線和全部的模型求交,顯然不是一個好辦法,通常狀況下會進行一些優化,好比先和模型的包圍盒求交,若是和模型的包圍盒不相交的話,就放過去,不然就接着往下進行,和模型的全部三角面片求交。3d

 

      那麼什麼是包圍盒呢?在計算機圖形學與計算幾何領域,一組物體的包圍體就是將物體組合徹底包容起來的一個封閉空間。將複雜物體封裝在簡單的包圍體中,就能夠提升幾何運算的效率。一般簡單的物體比較容易檢查相互之間的重疊。其中有一種包圍盒叫作AABB, AABB的全稱是axis aligned bounding box,就是咱們經常提到軸向包圍盒,這個盒子的邊是平行於x/y/z軸的。 全部的2d3d物體都是由點組成的,因此只要找出這些物體的最大值點和最小值點,那麼就可使用這兩個點表示該物體的AABB包圍盒了。
       檢測碰撞的時候咱們只須要檢測這些物體的AABB(即他們的最大值點和最小值點)是否相交,就能夠判斷是否碰撞了。orm

 

 

 

 

 

 

 

1.4. 射線和三角形相交

 

     判斷射線和包圍盒是否求交後,就輪到判斷是否和三角形求交了,最早想到的是 首先判斷射線是否與三角形所在的平面相交,若是相交,再判斷交點是否在三角形內。判斷射線是否與平面相交, 判斷點是否在三角形內.htm

 

1.5. THREE.JS中求交的代碼實現

 

  three.js中的一個案例,名字叫webgl_interactive_lines.html,能夠選中一根線,並顯示一個小球。根據以上的思路,代碼註釋以下:blog

 

//鼠標點擊的屏幕座標轉換到視點座標系

 

var vector = new THREE.Vector3( mouse.x, mouse.y, 1 ).unproject( camera );

 

 //在視點座標系中造成射線

 

 raycaster.set( camera.position,vector.sub( camera.position ).normalize() );

 

 //射線和模型求交,選中一系列直線

 

var intersects = raycaster.intersectObjects( parentTransform.children, true);

 

if ( intersects.length > 0 ) {

 

if ( currentIntersected !== undefined )

 

 {

 

 currentIntersected.material.linewidth = 1;

 

 }

 

   //第一個直線

 

currentIntersected = intersects[ 0 ].object;

 

currentIntersected.material.linewidth = 5;

 

    //把球設爲可見,而且位置移到鼠標點擊的屏幕位置

 

sphereInter.visible = true;

 

    sphereInter.position.copy( intersects[ 0 ].point );

 

}

 

歡迎加微信 nuonuodi_1, 交流更多的技術問題

相關文章
相關標籤/搜索