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 相关文章推荐
读jQuery之十三 添加事件和删除事件的核心方法
Aug 23 Javascript
Jquery取得iframe下内容的方法
Nov 18 Javascript
纯jquery实现模仿淘宝购物车结算
Aug 20 Javascript
javascript鼠标右键菜单自定义效果
Dec 08 Javascript
Node.js的项目构建工具Grunt的安装与配置教程
May 12 Javascript
js中的关联数组与普通数组详解
Jul 27 Javascript
JS实现六位字符密码输入器功能
Aug 19 Javascript
微信小程序 富文本转文本实例详解
Oct 24 Javascript
微信小程序进行微信支付的步骤昂述
Dec 01 Javascript
了解javascript中let和var及const关键字的区别
May 24 Javascript
VSCode 添加自定义注释的方法(附带红色警戒经典注释风格)
Aug 27 Javascript
uniapp开发打包多端应用完整方法指南
Dec 24 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
在线增减.htpasswd内的用户
2006/10/09 PHP
php pcntl_fork和pcntl_fork 的用法
2009/04/13 PHP
基于PHP的cURL快速入门教程 (小偷采集程序)
2011/06/02 PHP
php _autoload自动加载类与机制分析
2012/02/10 PHP
php中计算程序运行时间的类代码
2012/11/03 PHP
PHP使用DES进行加密与解密的方法详解
2013/06/06 PHP
php+ajax 实现输入读取数据库显示匹配信息
2015/10/08 PHP
WordPress中用于获取文章作者与分类信息的方法整理
2015/12/17 PHP
Yii2中DropDownList简单用法示例
2016/07/18 PHP
window.onload 加载完毕的问题及解决方案(上)
2009/07/09 Javascript
js setTimeout()函数介绍及应用以倒计时为例
2013/12/12 Javascript
javascript生成随机大小写字母的方法
2014/02/20 Javascript
控制文字内容的显示与隐藏示例
2014/06/11 Javascript
详解JavaScript逻辑And运算符
2015/12/04 Javascript
jQuery获取父元素及父节点的方法小结
2016/04/14 Javascript
getElementById().innerHTML与getElementById().value的区别
2016/10/27 Javascript
微信小程序  TLS 版本必须大于等于1.2问题解决
2017/02/22 Javascript
微信小程序 setData使用方法及常用错误解决办法
2017/05/11 Javascript
用npm-run实现自动化任务的方法示例
2019/01/14 Javascript
基于vue实现圆形菜单栏组件
2019/07/05 Javascript
浅谈layer的Icon样式以及一些常用的layer窗口使用方法
2019/09/11 Javascript
HTML+JS实现“代码雨”效果源码(黑客帝国文字下落效果)
2020/03/17 Javascript
[57:36]DOTA2-DPC中国联赛 正赛 SAG vs CDEC BO3 第三场 2月1日
2021/03/11 DOTA
Python字符编码判断方法分析
2016/07/01 Python
利用numpy实现一、二维数组的拼接简单代码示例
2017/12/15 Python
Python利用pandas计算多个CSV文件数据值的实例
2018/04/19 Python
Pytorch释放显存占用方式
2020/01/13 Python
python使用正则表达式去除中文文本多余空格,保留英文之间空格方法详解
2020/02/11 Python
一款纯css3实现简单的checkbox复选框和radio单选框
2014/11/05 HTML / CSS
南非最大的花卉和送礼服务:NetFlorist
2017/09/13 全球购物
西班牙家用电器和电子产品购物网站:Mi Electro
2019/02/25 全球购物
2014年党务公开方案
2014/05/08 职场文书
廉政文化进校园广播稿
2014/10/20 职场文书
小学四年级学生评语
2014/12/26 职场文书
Python面向对象之内置函数相关知识总结
2021/06/24 Python
MySQL中优化SQL语句的方法(show status、explain分析服务器状态信息)
2022/04/09 MySQL