原文位址: WebGL之物體選擇
使用WebGL将圖形繪制到畫布後,如何與外部進行互動?這其中最關鍵的就是如何實作物體的選擇。比如滑鼠點選後判斷是否選中了某個圖形或圖形的某個部分。
本節實作的效果: WebGL選中物體
《WebGL程式設計指南》中提出了一個原理很簡單的解決方案,步驟如下:
滑鼠按下時物體重繪為紅色或其他能區分的顔色
讀取滑鼠點選處像素的顔色
使用物體原來的顔色進行重繪,以恢複物體本來顔色
判斷第2步讀取到的顔色是否與預設的顔色值相等,相等則表示點選中物體
可以說這是個非常容易實作的方案,不過要為每個物體分别設定不同的區分顔色卻是個隐患,同時也不夠友好。
這是使用最廣泛也最精确的一種方案了,Three.js 中的光線投射器 (Raycaster) 就實作了這種方案,可以看裡面的源代碼。
它的基本原理: 從視點出發的光線首先投射到近截面,最後投射到遠截面,結合滑鼠點選的位置 (x, y) 和視圖投影矩陣 (viewProjection)。可以得出由近截面坐标 (x1, y1, z1) 和遠截面坐标 (x2, y2, z2) 組成的射線向量。然後我們就可以将物體坐标構成的面逐個與這個向量進行對比。這涉及到線性代數中的向量,點積,叉積,矩陣等概念,比較複雜。主要分兩個步驟:
建立物體的包圍盒,判斷射線是否穿過該物體包圍盒
判斷射線是否穿過該物體的某個三角形面,如果經過即可判斷選中了該物體
下面就分步實作光線投射算法的上面兩個步驟
包圍盒算法原理如下:
首先用視圖投影模型矩陣 (mvp) 對圖形坐标進行變換,得到在螢幕中的繪制坐标[x,y,z] 周遊每個坐标得出一個由最大最小xy坐标 [xmax, xmin, ymax, ymin] 構成的二維包圍盒 滑鼠位置 (x, y) 與包圍盒邊界進行比較,如果坐标處于盒子邊界之内,那麼就可判斷選中了該物體
核心代碼如下:
但是包圍盒算法判斷地不是很精準,在物體形狀不是很規則或物體間靠攏的比較緊時表現得尤其明顯。
我們知道WebGL圖形是由三角形構成的,那麼進一步判斷射線是否相交該物體某個三角形面就會非常精确了。
數學原理如下:
三角形内的任意一點都可以用它相對于三角形的頂點的位置來定義: T(u,v) = (1 - u - v)V0 + uV1 + vV2 其中 u >= 0, v >= 0, u + v <= 1 ,稱為重心坐标 射線可以用參數方程表示為: T(t) = P + td 其中P為起始點,d為方向向量 是以計算直線與三角的交點的等式為: P + td = (1-u-v)V0 + uV1 + vV2 整理後最終得到一個齊次線性方程組,其中[t u v] 為1 x 3 的矩陣,(t,u,v) 是它的解 [-d V1-V0 V2-V0] [t u v] = [P-V0] 根據克萊姆法則求解,其中T = P - V0, E1 = V1 - V0, E2 = V2 - V0,( [(T x E1) • E2] [(d x E2) • T] [(T x E1) • d] ) 為 3 x 3 矩陣,等式最終可以寫成如下: (t,u,v) = 1/((d x E2) • E1) ( [(T x E1) • E2] [(d x E2) • T] [(T x E1) • d] )
具體實作代碼如下: