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 相关文章推荐
window.name代替cookie的实现代码
Nov 28 Javascript
js中function()使用方法
Dec 24 Javascript
JQuery查找DOM节点的方法
Jun 11 Javascript
JSON遍历方式实例总结
Dec 07 Javascript
jQuery Validate让普通按钮触发表单验证的方法
Dec 15 Javascript
详解JavaScript对象的深浅复制
Mar 30 Javascript
详解angular用$sce服务来过滤HTML标签
Apr 11 Javascript
two.js之实现动画效果示例
Nov 06 Javascript
微信小程序实现商品属性联动选择
Feb 15 Javascript
详解在网页上通过JS实现文本的语音朗读
Mar 28 Javascript
文章或博客自动生成章节目录索引(支持三级)的实现代码
May 10 Javascript
JS如何使用剪贴板操作Clipboard API
May 17 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
用PHP实现弹出消息提示框的两种方法
2013/12/17 PHP
C/S和B/S两种架构区别与优缺点分析
2014/10/23 PHP
PHP调用.NET的WebService 简单实例
2015/03/27 PHP
PHP代码实现爬虫记录――超管用
2015/07/31 PHP
关于PHP文件的自动运行方法分析
2016/05/13 PHP
PHP自定义函数判断是否为Get、Post及Ajax提交的方法
2017/07/27 PHP
yii2.0整合阿里云oss上传单个文件的示例
2017/09/19 PHP
PHP实现数据库统计时间戳按天分组输出数据的方法
2017/10/10 PHP
thinkPHP框架中执行原生SQL语句的方法
2017/10/25 PHP
Lumen timezone 时区设置方法(慢了8个小时)
2018/01/20 PHP
使用js获取地址栏中传递的值
2013/07/02 Javascript
JS定时刷新页面及跳转页面的方法
2013/07/04 Javascript
关于jQuery中.attr()和.prop()的问题探讨
2013/09/06 Javascript
jQuery事件绑定on()、bind()与delegate() 方法详解
2015/06/03 Javascript
Node.js实用代码段之正确拼接Buffer
2016/03/17 Javascript
Angularjs中$http以post请求通过消息体传递参数的实现方法
2016/08/05 Javascript
Angular.js中$apply()和$digest()的深入理解
2016/10/13 Javascript
微信小程序 页面跳转传参详解
2016/10/28 Javascript
js原生之焦点图转换加定时器实例
2016/12/12 Javascript
javascript阻止事件冒泡和浏览器的默认行为
2017/01/21 Javascript
[11:27]《一刀刀一天》之DOTA全时刻20:TI4总奖金突破920W TS赛事分析
2014/06/18 DOTA
[00:52]黑暗之门更新 新英雄孽主驾临DOTA2
2016/08/24 DOTA
全面了解Nginx, WSGI, Flask之间的关系
2018/01/09 Python
详解python的sorted函数对字典按key排序和按value排序
2018/08/10 Python
Python3的介绍、安装和命令行的认识(推荐)
2018/10/20 Python
python实现统计文本中单词出现的频率详解
2019/05/20 Python
Django文件存储 默认存储系统解析
2019/08/02 Python
python实现本地批量ping多个IP的方法示例
2019/08/07 Python
python实现滑雪游戏
2020/02/22 Python
应用电子专业学生的自我评价
2013/10/16 职场文书
中式餐厅创业计划书范文
2014/01/23 职场文书
纪律教育月活动总结
2014/08/26 职场文书
解放思想演讲稿
2014/09/11 职场文书
党的群众路线教育实践活动对照检查材料(教师)
2014/09/24 职场文书
父母教会我观后感
2015/06/17 职场文书
Python requests用法和django后台处理详解
2022/03/19 Python