1. 程式人生 > 程式設計 >three.js利用射線Raycaster進行碰撞檢測

three.js利用射線Raycaster進行碰撞檢測

本文例項為大家分享了利用射線Raycaster進行碰撞檢測的具體程式碼,供大家參考,具體內容如下

學習碰撞檢測之前,我們先了解一下Raycaster類

Raycaster 應該翻譯為“光線投射”,顧名思義,就是投射出去的一束光線。

Raycaster的建構函式如下

Raycaster( origin,direction,near,far ) {
origin — 射線的起點向量。
direction — 射線的方向向量,應該歸一化。
near — 所有返回的結果應該比 near 遠。Near不能為負,預設值為0。
far — 所有返回的結果應該比 far 近。Far 不能小於 near,預設值為無窮大。

使用Raycaster進行碰撞檢測

用Raycaster來檢測碰撞的原理很簡單,我們需要以物體的中心為起點,向各個頂點(vertices)發出射線,然後檢查射線是否與其它的物體相交。如果出現了相交的情況,檢查最近的一個交點與射線起點間的距離,如果這個距離比射線起點至物體頂點間的距離要小,則說明發生了碰撞。

這個方法有一個 缺點 ,當物體的中心在另一個物體內部時,是不能夠檢測到碰撞的。而且當兩個物體能夠互相穿過,且有較大部分重合時,檢測效果也不理想。

還有需要 注意 的一點是:在Three.js中建立物體時,它的頂點(veritces)數目是與它的分段數目相關的,分段越多,頂點數目越多。為了檢測過程中的準確度考慮,需要適當增加物體的分段。

檢測光線是否與物體相交使用的是 intersectObject 或 intersectObjects 方法:

.intersectObject ( object,recursive )
 
//object — 檢測該物體是否與射線相交。
//recursive — 如果設定,則會檢測物體所有的子代。

相交的結果會以一個數組的形式返回,其中的元素依照距離排序,越近的排在越前.

這樣通過對陣列中的元素進行處理,就能得出想要的結果。

intersectObjects 與 intersectObject 類似,除了傳入的引數是一個數組之外,並無大的差別。

/**
 * 功能:檢測 movingCube 是否與陣列 collideMeshList 中的元素髮生了碰撞
 * 
 */
var originPoint = movingCube.position.clone();
 
for (var vertexIndex = 0; vertexIndex < movingCube.geometry.vertices.length; vertexIndex++) {
 // 頂點原始座標
 var localVertex = movingCube.geometry.vertices[vertexIndex].clone();
 // 頂點經過變換後的座標
 var globalVertex = localVertex.applyMatrix4(movingCube.matrix);
 // 獲得由中心指向頂點的向量
 var directionVector = globalVertex.sub(movingCube.position);
 
 // 將方向向量初始化
 var ray = new THREE.Raycaster(originPoint,directionVector.clone().normalize());
 // 檢測射線與多個物體的相交情況
 var collisionResults = ray.intersectObjects(collideMeshList);
 // 如果返回結果不為空,且交點與射線起點的距離小於物體中心至頂點的距離,則發生了碰撞
 if (collisionResults.length > 0 && collisionResults[0].distance < directionVector.length()) {
  crash = true; // crash 是一個標記變數
 }
}

在Three.js中是使用矩陣來記錄3D轉換的,每一個Object3D的例項都有一個矩陣,儲存了位置position,旋轉rotation和伸縮scale。

var globalVertex = localVertex.applyMatrix4(movingCube.matrix);

這一句程式碼將物體的本地座標乘以變換矩陣,得到了這個物體在世界座標系中的值,處理之後的值才是我們所需要的。

下面是一個測試的完整例項:

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 = [];
//建立渲染器
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);
 
}
//建立場景
function initScene(){
 scene = new THREE.Scene();
 scene.background = new THREE.Color( 0xf0f0f0 );
}
//建立相機
function initCamera(){
 camera = new THREE.PerspectiveCamera(50,WIDTH/HEIGHT,1,10000);
 camera.position.set(0,1000);
 camera.lookAt(0,0);
}
//建立光源
function initLight(){
 // 方向光
 var directionalLight = new THREE.DirectionalLight( 0xffffff,0.5 );
 scene.add( directionalLight );
 // 環境光
 scene.add( new THREE.AmbientLight( 0x505050 ) );
}
//建立物件
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 } ) );
  //隨機位置
  object.position.x = Math.random() * 1000 - 500;
  object.position.y = Math.random() * 600 - 300;
  object.position.z = Math.random() * 800 - 400;
  //隨機角度
  object.rotation.x = Math.random() * 2 * Math.PI;
  object.rotation.y = Math.random() * 2 * Math.PI;
  object.rotation.z = Math.random() * 2 * Math.PI;
  //隨機大小
  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 );
  // 放入陣列
  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
  * 返回一個新的Vector3,其具有和當前這個向量相同的x、y和z。
  */
 originPoint = cube.position.clone();
 
}
//建立控制器
function initControls(){
 // TrackballControls 軌跡球控制元件,最常用的控制元件,可以使用滑鼠輕鬆的移動、平移,縮放場景。
 controls = new THREE.TrackballControls( camera );
 controls.rotateSpeed = 1.0;// 旋轉速度
 controls.zoomSpeed = 1.2;// 縮放速度
 controls.panSpeed = 0.8;// 平controls
 controls.noZoom = false;
 controls.noPan = false;
 controls.staticMoving = true;// 靜止移動,為 true 則沒有慣性
 controls.dynamicDampingFactor = 0.3;// 阻尼係數 越小 則滑動越大
 // DragControls 初始化拖拽控制元件
 var dragControls = new THREE.DragControls( objects,renderer.domElement );
 // 開始拖拽
 dragControls.addEventListener( 'dragstart',function () {
 
  controls.enabled = false;
 
 } );
 // 拖拽結束
 dragControls.addEventListener( 'dragend',function () {
 
  controls.enabled = true;
 
 } );
}
 
function initThree(){
 initRenderer();
 initScene();
 initCamera();
 initLight();
 initObject();
 initControls();
 animation();
}
//迴圈
function animation(){
 requestAnimationFrame(animation);
 renderer.render(scene,camera);
 // 更新控制器
 controls.update();
 // 迴圈碰撞檢測
 for (var i = 0; i < cube.geometry.vertices.length; i++) {
  // 頂點原始座標
  var localVertex = cube.geometry.vertices[i].clone();
  // 頂點經過變換後的座標
  // matrix 區域性變換矩陣。 applyMatrix4 並返回新Matrix4(4x4矩陣)物件.
  var globalVertex = localVertex.applyMatrix4(cube.matrix);
  // 獲得由中心指向頂點的向量
  var directionVector = globalVertex.sub(cube.position);
  // 將方向向量初始化
  var ray = new THREE.Raycaster(originPoint,directionVector.clone().normalize());
  // 檢測射線與多個物體的相交情況
  var collisionResults = ray.intersectObjects(objects);
  // 如果返回結果不為空,且交點與射線起點的距離小於物體中心至頂點的距離,則發生了碰撞
  if (collisionResults.length > 0 && collisionResults[0].distance < directionVector.length()) {
   console.log('碰撞!');
  }
 }
 
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。