three.js利用射线Raycaster进行碰撞检测


Posted in Javascript onMarch 12, 2020

本文实例为大家分享了利用射线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,0,1000);
 camera.lookAt(0,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, camera, 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('碰撞!');
  }
 }
 
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Node.js中创建和管理外部进程详解
Aug 16 Javascript
jquery带动画效果幻灯片特效代码
Aug 27 Javascript
深入理解JS DOM事件机制
Aug 06 Javascript
js实现添加可信站点、修改activex安全设置,禁用弹出窗口阻止程序
Aug 17 Javascript
微信小程序 使用picker封装省市区三级联动实例代码
Oct 28 Javascript
js实现可输入可选择的select下拉框
Dec 21 Javascript
如何正确理解javascript的模块化
Mar 02 Javascript
原生js轮播特效
May 18 Javascript
详解webpack解惑:require的五种用法
Jun 09 Javascript
js实现数字滚动特效
Dec 16 Javascript
Node.js API详解之 module模块用法实例分析
May 13 Javascript
详解Typescript里的This的使用方法
Jan 08 Javascript
JS实现碰撞检测效果
Mar 12 #Javascript
使用JS实现动态时钟
Mar 12 #Javascript
Vue使用vue-draggable 插件在不同列表之间拖拽功能
Mar 12 #Javascript
js实现动态时钟
Mar 12 #Javascript
package.json各个属性说明详解
Mar 11 #Javascript
package.json中homepage属性的作用详解
Mar 11 #Javascript
vue项目中使用vue-layer弹框插件的方法
Mar 11 #Javascript
You might like
一个域名查询的程序
2006/10/09 PHP
ThinkPHP多语言支持与多模板支持概述
2014/08/22 PHP
PHP中数据类型转换的三种方式
2015/04/02 PHP
php将字符串转换为数组实例讲解
2020/05/05 PHP
用JavaScript脚本实现Web页面信息交互
2006/10/11 Javascript
JavaScript设置body高度为浏览器高度的方法
2015/02/09 Javascript
JavaScript中数据结构与算法(一):栈
2015/06/19 Javascript
js+div实现文字滚动和图片切换效果代码
2015/08/27 Javascript
配置Grunt的Task时通配符支持和动态生成文件名问题
2015/09/06 Javascript
JQUERY表单暂存功能插件分享
2016/02/23 Javascript
基于bootstrap风格的弹框插件
2016/12/28 Javascript
JavaScript创建对象_动力节点Java学院整理
2017/06/27 Javascript
详解小程序原生使用ES7 async/await语法
2018/08/06 Javascript
jQuery序列化form表单数据为JSON对象的实现方法
2018/09/20 jQuery
Vue.js 中的实用工具方法【推荐】
2019/07/04 Javascript
javascript canvas API内容整理
2020/02/16 Javascript
python使用插值法画出平滑曲线
2018/12/15 Python
Python操作redis和mongoDB的方法
2019/12/19 Python
selenium自动化测试入门实战
2020/12/21 Python
HTML5离线应用与客户端存储的实现
2018/05/03 HTML / CSS
详解HTML5将footer置于页面最底部的方法(CSS+JS)
2018/10/11 HTML / CSS
CK美国官网:Calvin Klein
2016/08/26 全球购物
个性化皮包、小袋、生活配件:Mon Purse
2019/03/26 全球购物
俄罗斯大型在线书店:Читай-город
2019/10/10 全球购物
俄罗斯外国汽车和国产汽车配件网上商店:Движком
2020/04/19 全球购物
GWT的应用有哪两种部署模式
2012/12/21 面试题
经济管理自荐书
2014/06/09 职场文书
2014三年级班主任工作总结
2014/12/05 职场文书
研究生就业推荐表导师评语
2014/12/31 职场文书
滴水洞导游词
2015/02/10 职场文书
廉洁自律个人总结
2015/02/14 职场文书
小学校本教研总结
2015/08/13 职场文书
年中了,该如何写好个人述职报告?
2019/07/02 职场文书
有关保护环境的宣传标语100条
2019/08/07 职场文书
为什么在foreach循环中JAVA集合不能添加或删除元素
2021/06/11 Java/Android
Spring mvc是如何实现与数据库的前后端的连接操作的?
2021/06/30 Java/Android