WebGL之物體選擇

原文地址: WebGL之物體選擇

使用WebGL將圖形繪制到畫布后,如何與外部進行交互?這其中最關鍵的就是如何實現物體的選擇。比如鼠標點擊后判斷是否選中了某個圖形或圖形的某個部分。

本節實現的效果: WebGL選中物體

如何實現選中物體

顏色區分法

《WebGL編程指南》中提出了一個原理很簡單的解決方案,步驟如下:

  1. 鼠標按下時物體重繪為紅色或其他能區分的顏色

  2. 讀取鼠標點擊處像素的顏色

    gl.readPixels(x,y,width,height,format,type,pixels)
    復制代碼
  3. 使用物體原來的顏色進行重繪,以恢復物體本來顏色

  4. 判斷第2步讀取到的顏色是否與預設的顏色值相等,相等則表示點擊中物體

可以說這是個非常容易實現的方案,不過要為每個物體分別設置不同的區分顏色卻是個隱患,同時也不夠友好。

光線投射法

這是使用最廣泛也最精確的一種方案了,Three.js 中的光線投射器 (Raycaster) 就實現了這種方案,可以看里面的源代碼。

它的基本原理: 從視點出發的光線首先投射到近截面,最后投射到遠截面,結合鼠標點擊的位置 (x, y) 和視圖投影矩陣 (viewProjection)。可以得出由近截面坐標 (x1, y1, z1) 和遠截面坐標 (x2, y2, z2) 組成的射線向量。然后我們就可以將物體坐標構成的面逐個與這個向量進行對比。這涉及到線性代數中的向量,點積,叉積,矩陣等概念,比較復雜。主要分兩個步驟:

  1. 創建物體的包圍盒,判斷射線是否穿過該物體包圍盒
  2. 判斷射線是否穿過該物體的某個三角形面,如果經過即可判斷選中了該物體

下面就分步實現光線投射算法的上面兩個步驟

包圍盒

包圍盒算法原理如下:

首先用視圖投影模型矩陣 (mvp) 對圖形坐標進行變換,得到在屏幕中的繪制坐標[x,y,z]

遍歷每個坐標得出一個由最大最小xy坐標 [xmax, xmin, ymax, ymin] 構成的二維包圍盒

鼠標位置 (x, y) 與包圍盒邊界進行比較,如果坐標處于盒子邊界之內,那么就可判斷選中了該物體

核心代碼如下:

canvas.addEventListener('mousemove', function(e) {//坐標轉換為webgl表示區間const pos = util.windowToWebgl(tCanvas,e.clientX,e.clientY);const ps = [];Polygons.forEach((p,i)=>{//重置狀態p.select = false;//mvp矩陣const matrix = m4.translate(viewProjection, p.pos);let xmax, ymax, xmin, ymin, zmax, zmin;//包圍盒邊界//遍歷頂點獲取包圍盒的邊界for(let j = 0; j < p.position.length; j = j+3){//對坐標進行矩陣轉換const s = m4.transformPoint(matrix, p.position.slice(j,j+3));if(j == 0){xmax = s[0];xmin = s[0];ymax = s[1];ymin = s[1];zmax = s[2];zmin = s[2];continue;}if(s[0]>xmax) xmax = s[0];if(s[0]<xmin) xmin = s[0];if(s[1]>ymax) ymax = s[1];if(s[1]<ymin) ymin = s[1];if(s[2]>zmax) zmax = s[2];if(s[2]<zmin) zmin = s[2];}// 射線處于包圍盒內if(pos.x >= xmin && pos.x <= xmax && pos.y >= ymin && pos.y <= ymax){p.coord = [(xmax+xmin)/2,(ymax+ymin)/2,(zmax+zmin)/2];ps.push(p);}});if(!ps.length) return;//獲取最靠近視點的圖形const sel = ps.length == 1? ps[0]: ps.sort((a,b)=> a.coord[2] - b.coord[2])[0];sel.select = true;
},false);
復制代碼

射線與三角形相交

但是包圍盒算法判斷地不是很精準,在物體形狀不是很規則或物體間靠攏的比較緊時表現得尤其明顯。

我們知道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] )

具體實現代碼如下:

// 射線處于包圍盒內
if(pos.x >= xmin && pos.x <= xmax && pos.y >= ymin && pos.y <= ymax){p.coord = [(xmax+xmin)/2,(ymax+ymin)/2,(zmax+zmin)/2];const P = [pos.x,pos.y,0.5];//射線起始點const d = [0,0,1];//射線方向for(let j = 0; j < p.position.length; j = j + 9){//三角形頂點const V0 = m4.transformPoint(matrix, p.position.slice(j,j+3));const V1 = m4.transformPoint(matrix, p.position.slice(j+3,j+6));const V2 = m4.transformPoint(matrix, p.position.slice(j+6,j+9));const T = v3.subtract(P,V0);const E1 = v3.subtract(V1,V0);const E2 = v3.subtract(V2,V0);const M = v3.cross(d,E2);const det = v3.dot(M,E1);if(det == 0) continue;const K = v3.cross(T,E1);const t = v3.dot(K,E2)/det;const u = v3.dot(M,T)/det;const v = v3.dot(K,d)/det;//射線與三角形相加if(u >= 0 && v >= 0 && u+v<=1 ){ps.push(p);break;}}
}
復制代碼

轉載于:https://juejin.im/post/5cecfaa3e51d4510727c8010

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/448729.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/448729.shtml
英文地址,請注明出處:http://en.pswp.cn/news/448729.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

中國歷史上影響最大的10首詩

中國是詩歌的國度&#xff0c;有許多詩都有很大的影響。這里所謂影響“最大”(而不是“最好”)的十首詩&#xff0c;除了要寫得好之外&#xff0c;還必須通俗易懂、易記。 第一首&#xff1a;李白的《靜夜思》 床前明月光&#xff0c;疑是地上霜。 舉頭望明月&#xff0c;低頭…

XML建模

建模分兩步&#xff1a;1、以面向對象的編程思想&#xff0c;描述xml資源文件。 2、將xml文件中內容封裝進model實體對象。 導入文件&#xff1a;config.xml <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE config[<!ELEMENT config (acti…

Docker 方式安裝部署 rocketMQ 、部署 圖形化界面控制臺、rocketMQ 控制臺

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 直接上官網&#xff0c;找到工程&#xff0c;clone 到本地&#xff0c;地址&#xff1a;https://github.com/apache/rocketmq-extern…

迭代器(iterator)

Date: 2019-05-23 Author: Sun 為何要引入迭代器&#xff1f; ? 通過列表生成式&#xff0c;我們可以直接創建一個列表&#xff0c;但是&#xff0c;受到內存限制&#xff0c;列表容量肯定是有限的&#xff0c;而且創建一個包含100萬個元素的列表&#xff0c;不僅占用很大的存…

初識python之函數基礎

課堂筆記&#xff1a; 1、什么是函數&#xff1f;函數相當于工具&#xff0c;需要事先準備好&#xff0c;在需要用時再使用。2、如何使用函數&#xff1f;函數必須先定義、后調用。3、函數的語法:# def 函數名(參數1,參數2...):# """# 注釋# 函數的說明# 水…

java 的幾種對象 (PO,VO,DAO,BO,POJO) 解釋

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 一、PO:persistant object 持久對象,可以看成是與數據庫中的表相映射的java對象。最簡單的PO就是對應數據庫中某個表中的一條記錄&#x…

【隨想】每日兩題Day.22

題目&#xff1a;102. 二叉樹的層序遍歷 給你二叉樹的根節點 root &#xff0c;返回其節點值的 層序遍歷 。 &#xff08;即逐層地&#xff0c;從左到右訪問所有節點&#xff09;。 示例 1&#xff1a; 輸入&#xff1a;root [3,9,20,null,null,15,7] 輸出&#xff1a;[[3],[…

幫助子女成功的十大路徑

美國全國家長協會(National PTA)建議指出&#xff1a;作為家長您對你子女的成功起著非常重要的影響作用&#xff0c;并舉出幫助子女成功的十種路徑。 1、與子女溝通 如果我們盡早地與子女溝通&#xff0c;提供給他們信息與行為準則&#xff0c;獲得子女的信任&#xff0c;在…

shell關閉指定進程

例如要關閉jupyter-notebook這個進程&#xff1a; ps -ef | grep jupyter-notebook | grep -v grep | cut -c 9-15 | xargs kill -9 說明&#xff1a;管道符“|”用來隔開兩個命令&#xff0c;管道符左邊命令的輸出會作為管道符右邊命令的輸入。 “ps -ef” 查看所有進程  …

垃圾回收算法與垃圾回收器

Java與C等語言最大的技術區別&#xff1a;自動化的垃圾回收機制&#xff08;GC&#xff09; 為什么要了解GC和內存分配策略 1、面試需要 2、GC對應用的性能是有影響的&#xff1b; 3、寫代碼有好處 棧&#xff1a;棧中的生命周期是跟隨線程&#xff0c;所以一般不需要關注 堆&a…

提高孩子睡眠質量 學業事半功倍

睡眠如同大腦的食物。在睡眠期間&#xff0c;許多重要的身體機能靜靜地發生著作用。省略睡眠是有害的&#xff0c;如果一個嚴重缺覺的人開著車&#xff0c;他會臉色蒼白、喜怒無常、反應遲鈍&#xff0c;可能是致命的危險。缺少睡眠讓青少年很難與人相處&#xff0c;學業表現不…

實體類(VO,DO,DTO)的劃分

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 從領域建模中的實體劃分、項目中的實際應用情況兩個角度&#xff0c;對這幾個概念進行簡析。 得出的主要結論是&#xff1a;在項目應用…

IIS新建站點服務器,localhost能登錄但是IP訪問登錄不了。

IIS服務器新建站點之后&#xff0c;瀏覽頁面&#xff0c;服務器本地是可以登錄&#xff0c;但是localhost換成IP就無法訪問。其他站點IP卻可以訪問。 1.如果瀏覽直接失敗&#xff0c;說明端口號需要更換。 2.如果出現IP不能訪問&#xff0c;localhost能訪問&#xff0c;需要在高…

eclipse問題_Alt+/不給提示,只補充代碼問題的解決方案

今天用eclipse敲代碼的時候遇到的問題 我還以為是沖突什么的 還重新裝了軟件 最后才發現原來是快捷鍵設置的問題 解決方案&#xff1a; 1&#xff1a;打開菜單window→Preferences&#xff0c;然后在窗口的左側樹選擇General->Keys 2&#xff1a;在下圖中的5框的地方輸入“w…

領域驅動設計:淺析 VO、DTO、DO、PO 概念、區別、用處

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 本篇文章主要討論一下我們經常會用到的一些對象&#xff1a;VO、DTO、DO和PO。 由于不同的項目和開發人員有不同的命名習慣&#xff0c…

動腦的生活教育

心理學家華生曾經說過&#xff1a;“如果給我一打孩子&#xff0c;我可以把他們變成律師、醫師、科學家&#xff0c;或是強盜、土匪。”華生認為&#xff0c;教育孩子就如同馬戲團的馴獸師訓練野獸一樣&#xff0c;是“刺激”與“反應”的聯結&#xff0c;不需要任何的“思考”…

前端知識點回顧之重點篇——CORS

CORS&#xff08;cross origin resource sharing&#xff09;跨域資源共享 來源&#xff1a;http://www.ruanyifeng.com/blog/2016/04/cors.html 它允許瀏覽器向跨源服務器&#xff0c;發出XMLHttpRequest請求&#xff0c;從而克服了AJAX只能同源使用的限制。 簡介 CORS需要瀏覽…

案例:隱秘而低調的內存泄露(OOM)

內存泄露測試的整個過程如下&#xff1a;在手機里啟動被測APP并打開DDMS。在DDMS中選中【com.example.android.hcgallery】之后單擊按鈕【show heap updates】&#xff0c;然后切換到標簽頁【VM Heap】&#xff0c;再單擊按鈕【Cause GC】。不斷操作APP&#xff0c;并觀察Heap。…

員工價值——如何體現自己價值,如何被自己的領導認可

到公司工作快三年了&#xff0c;比我后來的同事陸續得到了升職的機會&#xff0c;我卻原地不動&#xff0c;心里頗不是滋味。終于有一天&#xff0c;冒著被解聘的危險&#xff0c;我找到老板理論。 “老板&#xff0c;我有過遲到、早退或亂章違紀的現象嗎&#xff1f;”我問。 …

java: PO,VO,TO,BO,DAO,POJO 解釋

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 O/R Mapping 是 Object Relational Mapping&#xff08;對象關系映射&#xff09;的縮寫。通俗點講&#xff0c;就是將對象與關系數據庫綁…