three.js利用射線Raycaster進(jìn)行碰撞檢測
本文實(shí)例為大家分享了利用射線Raycaster進(jìn)行碰撞檢測的具體代碼,供大家參考,具體內(nèi)容如下
學(xué)習(xí)碰撞檢測之前,我們先了解一下Raycaster類
Raycaster 應(yīng)該翻譯為“光線投射”,顧名思義,就是投射出去的一束光線。
Raycaster的構(gòu)造函數(shù)如下
Raycaster( origin, direction, near, far ) { origin — 射線的起點(diǎn)向量。 direction — 射線的方向向量,應(yīng)該歸一化。 near — 所有返回的結(jié)果應(yīng)該比 near 遠(yuǎn)。Near不能為負(fù),默認(rèn)值為0。 far — 所有返回的結(jié)果應(yīng)該比 far 近。Far 不能小于 near,默認(rèn)值為無窮大。
使用Raycaster進(jìn)行碰撞檢測
用Raycaster來檢測碰撞的原理很簡單,我們需要以物體的中心為起點(diǎn),向各個(gè)頂點(diǎn)(vertices)發(fā)出射線,然后檢查射線是否與其它的物體相交。如果出現(xiàn)了相交的情況,檢查最近的一個(gè)交點(diǎn)與射線起點(diǎn)間的距離,如果這個(gè)距離比射線起點(diǎn)至物體頂點(diǎn)間的距離要小,則說明發(fā)生了碰撞。
這個(gè)方法有一個(gè) 缺點(diǎn) ,當(dāng)物體的中心在另一個(gè)物體內(nèi)部時(shí),是不能夠檢測到碰撞的。而且當(dāng)兩個(gè)物體能夠互相穿過,且有較大部分重合時(shí),檢測效果也不理想。
還有需要 注意 的一點(diǎn)是:在Three.js中創(chuàng)建物體時(shí),它的頂點(diǎn)(veritces)數(shù)目是與它的分段數(shù)目相關(guān)的,分段越多,頂點(diǎn)數(shù)目越多。為了檢測過程中的準(zhǔn)確度考慮,需要適當(dāng)增加物體的分段。
檢測光線是否與物體相交使用的是 intersectObject 或 intersectObjects 方法:
.intersectObject ( object, recursive ) //object — 檢測該物體是否與射線相交。 //recursive — 如果設(shè)置,則會(huì)檢測物體所有的子代。
相交的結(jié)果會(huì)以一個(gè)數(shù)組的形式返回,其中的元素依照距離排序,越近的排在越前.
這樣通過對(duì)數(shù)組中的元素進(jìn)行處理,就能得出想要的結(jié)果。
intersectObjects 與 intersectObject 類似,除了傳入的參數(shù)是一個(gè)數(shù)組之外,并無大的差別。
/** * 功能:檢測 movingCube 是否與數(shù)組 collideMeshList 中的元素發(fā)生了碰撞 * */ var originPoint = movingCube.position.clone(); for (var vertexIndex = 0; vertexIndex < movingCube.geometry.vertices.length; vertexIndex++) { // 頂點(diǎn)原始坐標(biāo) var localVertex = movingCube.geometry.vertices[vertexIndex].clone(); // 頂點(diǎn)經(jīng)過變換后的坐標(biāo) var globalVertex = localVertex.applyMatrix4(movingCube.matrix); // 獲得由中心指向頂點(diǎn)的向量 var directionVector = globalVertex.sub(movingCube.position); // 將方向向量初始化 var ray = new THREE.Raycaster(originPoint, directionVector.clone().normalize()); // 檢測射線與多個(gè)物體的相交情況 var collisionResults = ray.intersectObjects(collideMeshList); // 如果返回結(jié)果不為空,且交點(diǎn)與射線起點(diǎn)的距離小于物體中心至頂點(diǎn)的距離,則發(fā)生了碰撞 if (collisionResults.length > 0 && collisionResults[0].distance < directionVector.length()) { crash = true; // crash 是一個(gè)標(biāo)記變量 } }
在Three.js中是使用矩陣來記錄3D轉(zhuǎn)換的,每一個(gè)Object3D的實(shí)例都有一個(gè)矩陣,存儲(chǔ)了位置position,旋轉(zhuǎn)rotation和伸縮scale。
var globalVertex = localVertex.applyMatrix4(movingCube.matrix);
這一句代碼將物體的本地坐標(biāo)乘以變換矩陣,得到了這個(gè)物體在世界坐標(biāo)系中的值,處理之后的值才是我們所需要的。
下面是一個(gè)測試的完整實(shí)例:
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <script src="../js/three.js"></script> <script src="../js/controls/DragControls.js"></script> <script src="../js/controls/TrackballControls.js"></script> <script src="../js/stats.min.js"></script> <script src="Main.js"></script> <title>Document</title> </head> <body οnlοad="initThree();"> <div id="canvas-frame"></div> </body> </html>
Main.js
var scene,camera,controls,renderer,cube,originPoint; var WIDTH,HEIGHT; var objects = []; //創(chuàng)建渲染器 function initRenderer(){ WIDTH = window.innerWidth; HEIGHT = window.innerHeight; renderer = new THREE.WebGLRenderer({ antialias:true, }); renderer.setSize(WIDTH,HEIGHT); renderer.setPixelRatio(WIDTH/HEIGHT); document.getElementById('canvas-frame').appendChild(renderer.domElement); } //創(chuàng)建場景 function initScene(){ scene = new THREE.Scene(); scene.background = new THREE.Color( 0xf0f0f0 ); } //創(chuàng)建相機(jī) function initCamera(){ camera = new THREE.PerspectiveCamera(50,WIDTH/HEIGHT,1,10000); camera.position.set(0,0,1000); camera.lookAt(0,0,0); } //創(chuàng)建光源 function initLight(){ // 方向光 var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 ); scene.add( directionalLight ); // 環(huán)境光 scene.add( new THREE.AmbientLight( 0x505050 ) ); } //創(chuàng)建對(duì)象 function initObject(){ var geometry = new THREE.BoxBufferGeometry( 40, 40, 40 ); for ( var i = 0; i < 2; i ++ ) { var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) ); //隨機(jī)位置 object.position.x = Math.random() * 1000 - 500; object.position.y = Math.random() * 600 - 300; object.position.z = Math.random() * 800 - 400; //隨機(jī)角度 object.rotation.x = Math.random() * 2 * Math.PI; object.rotation.y = Math.random() * 2 * Math.PI; object.rotation.z = Math.random() * 2 * Math.PI; //隨機(jī)大小 object.scale.x = Math.random() * 2 + 1; object.scale.y = Math.random() * 2 + 1; object.scale.z = Math.random() * 2 + 1; //開啟陰影 object.castShadow = true; object.receiveShadow = true; scene.add( object ); // 放入數(shù)組 objects.push( object ); } // var geometry = new THREE.BoxGeometry( 80, 80, 80 ); var material = new THREE.MeshLambertMaterial( {color: 0xfff000} ); cube = new THREE.Mesh( geometry, material ); scene.add( cube ); /** * .clone () : Vector3 * 返回一個(gè)新的Vector3,其具有和當(dāng)前這個(gè)向量相同的x、y和z。 */ originPoint = cube.position.clone(); } //創(chuàng)建控制器 function initControls(){ // TrackballControls 軌跡球控件,最常用的控件,可以使用鼠標(biāo)輕松的移動(dòng)、平移,縮放場景。 controls = new THREE.TrackballControls( camera ); controls.rotateSpeed = 1.0;// 旋轉(zhuǎn)速度 controls.zoomSpeed = 1.2;// 縮放速度 controls.panSpeed = 0.8;// 平controls controls.noZoom = false; controls.noPan = false; controls.staticMoving = true;// 靜止移動(dòng),為 true 則沒有慣性 controls.dynamicDampingFactor = 0.3;// 阻尼系數(shù) 越小 則滑動(dòng)越大 // DragControls 初始化拖拽控件 var dragControls = new THREE.DragControls( objects, camera, renderer.domElement ); // 開始拖拽 dragControls.addEventListener( 'dragstart', function () { controls.enabled = false; } ); // 拖拽結(jié)束 dragControls.addEventListener( 'dragend', function () { controls.enabled = true; } ); } function initThree(){ initRenderer(); initScene(); initCamera(); initLight(); initObject(); initControls(); animation(); } //循環(huán) function animation(){ requestAnimationFrame(animation); renderer.render(scene,camera); // 更新控制器 controls.update(); // 循環(huán)碰撞檢測 for (var i = 0; i < cube.geometry.vertices.length; i++) { // 頂點(diǎn)原始坐標(biāo) var localVertex = cube.geometry.vertices[i].clone(); // 頂點(diǎn)經(jīng)過變換后的坐標(biāo) // matrix 局部變換矩陣。 applyMatrix4 并返回新Matrix4(4x4矩陣)對(duì)象. var globalVertex = localVertex.applyMatrix4(cube.matrix); // 獲得由中心指向頂點(diǎn)的向量 var directionVector = globalVertex.sub(cube.position); // 將方向向量初始化 var ray = new THREE.Raycaster(originPoint, directionVector.clone().normalize()); // 檢測射線與多個(gè)物體的相交情況 var collisionResults = ray.intersectObjects(objects); // 如果返回結(jié)果不為空,且交點(diǎn)與射線起點(diǎn)的距離小于物體中心至頂點(diǎn)的距離,則發(fā)生了碰撞 if (collisionResults.length > 0 && collisionResults[0].distance < directionVector.length()) { console.log('碰撞!'); } } }
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
js數(shù)字轉(zhuǎn)中文兩種實(shí)現(xiàn)方法
在前端開發(fā)中有時(shí)候會(huì)需要到將阿拉伯?dāng)?shù)字轉(zhuǎn)化為中文,當(dāng)前做個(gè)記錄,提供自己之后翻閱,這篇文章主要給大家介紹了關(guān)于js數(shù)字轉(zhuǎn)中文兩種實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下2023-10-10JavaScript解析json格式數(shù)據(jù)簡單示例
這篇文章主要介紹了JavaScript解析json格式數(shù)據(jù)簡單示例,本文通過for循環(huán)來獲取json結(jié)點(diǎn)數(shù)據(jù),需要的朋友可以參考下2014-12-12js獲取html頁面節(jié)點(diǎn)方法(遞歸方式)
這篇文章主要介紹了js使用遞歸方式獲取html頁面節(jié)點(diǎn)的方法,大家可以參考使用吧2013-12-12微信小程序點(diǎn)擊頂部導(dǎo)航欄切換樣式代碼實(shí)例
這篇文章主要介紹了微信小程序點(diǎn)擊頂部導(dǎo)航欄切換樣式代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11JavaScript 語言基礎(chǔ)知識(shí)點(diǎn)總結(jié)(思維導(dǎo)圖)
這篇文章通過思維導(dǎo)圖格式總結(jié)了JavaScript 語言基礎(chǔ)知識(shí)點(diǎn),想要學(xué)習(xí)js的朋友可以參考下2013-11-11js通過元素class名字獲取元素集合的具體實(shí)現(xiàn)
獲取元素集合的方法有很多,接下來為大家介紹喜愛使用js通過元素class名字獲取元素集合的方法2014-01-01