three.js利用射線Raycaster進(jìn)行碰撞檢測(cè)
本文實(shí)例為大家分享了利用射線Raycaster進(jìn)行碰撞檢測(cè)的具體代碼,供大家參考,具體內(nèi)容如下
學(xué)習(xí)碰撞檢測(cè)之前,我們先了解一下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)值為無(wú)窮大。
使用Raycaster進(jìn)行碰撞檢測(cè)
用Raycaster來(lái)檢測(cè)碰撞的原理很簡(jiǎn)單,我們需要以物體的中心為起點(diǎn),向各個(gè)頂點(diǎn)(vertices)發(fā)出射線,然后檢查射線是否與其它的物體相交。如果出現(xiàn)了相交的情況,檢查最近的一個(gè)交點(diǎn)與射線起點(diǎn)間的距離,如果這個(gè)距離比射線起點(diǎn)至物體頂點(diǎn)間的距離要小,則說(shuō)明發(fā)生了碰撞。
這個(gè)方法有一個(gè) 缺點(diǎn) ,當(dāng)物體的中心在另一個(gè)物體內(nèi)部時(shí),是不能夠檢測(cè)到碰撞的。而且當(dāng)兩個(gè)物體能夠互相穿過(guò),且有較大部分重合時(shí),檢測(cè)效果也不理想。
還有需要 注意 的一點(diǎn)是:在Three.js中創(chuàng)建物體時(shí),它的頂點(diǎn)(veritces)數(shù)目是與它的分段數(shù)目相關(guān)的,分段越多,頂點(diǎn)數(shù)目越多。為了檢測(cè)過(guò)程中的準(zhǔn)確度考慮,需要適當(dāng)增加物體的分段。
檢測(cè)光線是否與物體相交使用的是 intersectObject 或 intersectObjects 方法:
.intersectObject ( object, recursive ) //object — 檢測(cè)該物體是否與射線相交。 //recursive — 如果設(shè)置,則會(huì)檢測(cè)物體所有的子代。
相交的結(jié)果會(huì)以一個(gè)數(shù)組的形式返回,其中的元素依照距離排序,越近的排在越前.
這樣通過(guò)對(duì)數(shù)組中的元素進(jìn)行處理,就能得出想要的結(jié)果。
intersectObjects 與 intersectObject 類似,除了傳入的參數(shù)是一個(gè)數(shù)組之外,并無(wú)大的差別。
/**
* 功能:檢測(cè) 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)過(guò)變換后的坐標(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());
// 檢測(cè)射線與多個(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中是使用矩陣來(lái)記錄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è)測(cè)試的完整實(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)建場(chǎ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)、平移,縮放場(chǎ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)碰撞檢測(cè)
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)過(guò)變換后的坐標(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());
// 檢測(cè)射線與多個(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('碰撞!');
}
}
}
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- JS實(shí)現(xiàn)碰撞檢測(cè)的方法分析
- javascript制作游戲開發(fā)碰撞檢測(cè)的封裝代碼
- js實(shí)現(xiàn)碰撞檢測(cè)特效代碼分享
- 原生JS實(shí)現(xiàn)的碰撞檢測(cè)功能示例
- 原生js實(shí)現(xiàn)碰撞檢測(cè)
- javascript實(shí)現(xiàn)多邊形碰撞檢測(cè)
- 基于javascript實(shí)現(xiàn)碰撞檢測(cè)
- js實(shí)現(xiàn)拖拽與碰撞檢測(cè)
- 原生js實(shí)現(xiàn)移動(dòng)小球(碰撞檢測(cè))
- js 實(shí)現(xiàn)碰撞檢測(cè)的示例
- js實(shí)現(xiàn)碰撞檢測(cè)
相關(guān)文章
微信小程序—setTimeOut定時(shí)器的問(wèn)題及解決
這篇文章主要介紹了微信小程序—setTimeOut定時(shí)器的問(wèn)題及解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07
JavaScript對(duì)象與數(shù)組的幾種常見復(fù)制方法
在 JavaScript 開發(fā)中,對(duì)象和數(shù)組的復(fù)制是一個(gè)非常常見的操作,無(wú)論是函數(shù)參數(shù)傳遞、狀態(tài)管理,還是避免不必要的副作用,了解和掌握各種復(fù)制方式至關(guān)重要,我們將系統(tǒng)梳理 JavaScript 中的幾種常見復(fù)制方法,并對(duì)其應(yīng)用場(chǎng)景進(jìn)行詳細(xì)說(shuō)明,需要的朋友可以參考下2024-10-10
Microsfot .NET Framework4.0框架 安裝失敗的解決方法
今天在安裝.NET Framework 4.0,安裝了半天結(jié)果提示未安裝成功,提示原因是服務(wù)未啟動(dòng)了,error code: (0x80070643), "安裝時(shí)發(fā)生嚴(yán)重錯(cuò)誤 "2013-08-08
javascript中獲取class的簡(jiǎn)單實(shí)現(xiàn)
下面小編就為大家?guī)?lái)一篇javascript中獲取class的簡(jiǎn)單實(shí)現(xiàn)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-07-07
JS利用cookies設(shè)置每隔24小時(shí)彈出框
這篇文章主要介紹了利用cookies設(shè)置每隔24小時(shí)彈出框的實(shí)例代碼,需要的朋友可以參考下2017-04-04
一文詳解JS?類型轉(zhuǎn)換方法以及如何避免隱式轉(zhuǎn)換
這篇文章主要為大家介紹了JS?類型轉(zhuǎn)換方法以及如何避免隱式轉(zhuǎn)換示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
js 定義對(duì)象數(shù)組(結(jié)合)多維數(shù)組方法
下面小編就為大家?guī)?lái)一篇js 定義對(duì)象數(shù)組(結(jié)合)多維數(shù)組方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-07-07
基于javascript實(shí)現(xiàn)放大鏡特效
這篇文章主要為大家詳細(xì)介紹了基于javascript實(shí)現(xiàn)放大鏡特效,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12
javascript電商網(wǎng)站搶購(gòu)倒計(jì)時(shí)效果實(shí)現(xiàn)
這篇文章主要介紹了javascript電商網(wǎng)站搶購(gòu)倒計(jì)時(shí)效果實(shí)現(xiàn)代碼,掌握日期對(duì)象Date,獲取時(shí)間的方法,感興趣的小伙伴們可以參考一下2015-11-11
ExtJs使用自定義插件動(dòng)態(tài)保存表頭配置(隱藏或顯示)
這篇文章主要介紹了ExtJs使用自定義插件動(dòng)態(tài)保存表頭配置(隱藏或顯示) ,需要的朋友可以參考下2018-09-09

