溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Webgl&Three.js中物體拾取的示例分析

發布時間:2021-09-22 10:48:27 來源:億速云 閱讀:204 作者:小新 欄目:web開發

小編給大家分享一下Webgl&Three.js中物體拾取的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

1、引子:

在傳統的web開發中,由于存在DOM樹以及事件捕獲冒泡等機制,我們可以很方便的在某個DOM節點上注冊事件,并且執行父元素事件代理等一系列操作。但是在webgl的三維世界里,用戶使用鼠標或者touch事件,事件接收方是canvas容器,如何將這種點擊行為映射到三維世界,就需要借助三維世界的能力了,并且建立從canvas平面容器到三維世界的橋梁,進行所謂的物體拾取

2、基礎知識:

DOM與NDC坐標轉換,相機射線,觀察者模式。熟悉這部分知識的同學,可以直接跳過到下面的代碼實現。

  • a、DOM坐標和NDC坐標轉換:在這里,我們知道DOM坐標的(0,0)點在容器左上角,NDC坐標的(0,0)點在容器中心,需要進行坐標轉換。具體定義可以參考下面兩個鏈接

三維坐標變換 屏幕坐標定義

  • b、相機射線:  根據Three.js的官方定義,射線就是用來做物體拾取的機制。相比較于傳統的顏色拾取,射線拾取可以識別多個物體,得到先后順序,在使用上更加方便并且符合人的直覺。?  下面看一個官方的code example?

javascript

const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); function onMouseMove( event ) {     // 這里實現了DOM坐標系到NDC坐標系的轉換     mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;     mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1; } function render() {     // 從相機位置,發出一條射線     raycaster.setFromCamera( mouse, camera );     // 檢測相機發出射線相交的obj列表     const intersects = raycaster.intersectObjects( scene.children );     for ( let i = 0; i < intersects.length; i ++ ) {         // 將相交物體的材質顏色設置為紅色         intersects[ i ].object.material.color.set( 0xff0000 );     }     renderer.render( scene, camera ); } window.addEventListener( 'mousemove', onMouseMove, false ); window.requestAnimationFrame(render);

c、觀察者模式:與dom事件機制類似,觀察者模式非常是適合做這種注冊-觸發機制的。

3、具體實現:

考慮到每次遍歷intersects數組非常的不方便,特別是當場景中有實際上百個Object3D的時候。所以這里我們定義一個全局的對象存儲注冊事件,然后修改Object3D的原型鏈,增加on和on和on和off方法,來實現類似于DOM元素的事件注冊和銷毀。

const globalEvent = {click: {}} Object.assign(Object3D.prototype, {     $on(eventType, cb) {        if(globalEvent.hasOwnProperty(eventType)) {             globalEvent[eventType][this.id] = {                 object3d: this,                 callback: cb             };        } else { // error warn}     }     $off(eventType) {         if (!eventType) throw new Error('')         if(globalEvent.hasOwnProperty(eventType)) {             delete globalEvent[eventType][this.id]         } else {             throw new Error('')         }     } }) init(camera) function init(camera, container) {     let intersectPoint, obj, mouseX, mouseY, clicked;     const targetObj = globalEvent.click     const rayCaster = new Raycaster();     function down(e) {         obj = null;         e.preventDefault();         mouseX = event.clientX;         mouseY = event.clientY;         if (!globalEvent.click) return;         rayCaster.setFromCamera(             new Vector2(                 (mouseX / window.innerWidth) * 2 - 1,                 -(mouseY / window.innerHeight) * 2 + 1             ),             camera         );          let intersects = rayCaster.intersectsObjects(getVisibleList(targetObj));         if (intersects.length > 0) {             if (clicked) {                 obj = null;                 return;             }             clicked = true;             obj = intersects[0].object;             intersectPoint = intersects[0].point;                                 } else {             clicked = false;         }     }     function move(e) {         event.preventDefault();         // 這里針對移動端做一些優化      }     function up(e) {         event.preventDefault();         if (clicked && !!obj && obj.callback) {             obj.callback(obj.object3d, intersectPoint);         }         clicked = false     }    const eventOption = {      passive: false    };    container.addEventListener('mousedown', down, {passive: false});    container.addEventListener('mousemove', move, {passive: false});    container.addEventListener('mouseup', up, {passive: false});    container.addEventListener('touchstart', down, {passive: false});    container.addEventListener('touchmove', move, {passive: false});    container.addEventListener('touchend', up, {passive: false});}  function getVisibleList(targetObj) {     const list = []     for (const key in targetObj) {         const target = targetObj[key].object3d;         if (target.visible) list.push(target);     }     return list }  /*     使用方式:直接在mesh上注冊事件     target: 命中物體     point: 命中點的三維坐標 */ mesh.$on('click', (target, point)) {  }

以上是“Webgl&Three.js中物體拾取的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女