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

所属分类: 网络编程 / JavaScript 阅读数: 1770
收藏 0 赞 0 分享

本文实例为大家分享了利用射线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判断查询日期是否为有效日期

很多网站都涉及到输入日期选项,如果客户日期输入错误,可能导入查询不到甚至查询到错误的信息,为了更好的满足用户需求,需要对日期进行校验,下面给大家介绍使用纯javascript如何判断查询日期是否为有效日期,需要的朋友可以参考下
收藏 0 赞 0 分享

jquery实现的蓝色二级导航条效果代码

这篇文章主要介绍了jquery实现的蓝色二级导航条效果代码,涉及jquery鼠标事件及页面样式的动态切换效果实现技巧,非常简单实用,需要的朋友可以参考下
收藏 0 赞 0 分享

ajax如何实现页面局部跳转与结果返回

AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术,通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新,本篇文章给大家介绍ajax如何实现页面局部跳转与结果返
收藏 0 赞 0 分享

jQuery实现的类似淘宝网站搜索框样式代码分享

这篇文章主要介绍了类似淘宝网站搜索框样式实现代码,推荐给大家,有需要的小伙伴可以参考下。
收藏 0 赞 0 分享

js实现的黑背景灰色二级导航菜单效果代码

这篇文章主要介绍了js实现的黑背景灰色二级导航菜单效果代码,涉及javascript操作页面元素动态切换的实现技巧,非常具有实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

jQuery仿360导航页图标拖动排序效果代码分享

这篇文章主要为大家详细介绍了360导航页图标拖动排序效果代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

javascript中SetInterval与setTimeout的定时器用法

Javascript的setTimeOut和setInterval函数应用非常广泛,它们都用来处理延时和定时任务,比如打开网页一段时间后弹出一个登录框,页面每隔一段时间发送异步请求获取最新数据等,本文文章通过代码示例给大家介绍javascript中SetInterval与setT
收藏 0 赞 0 分享

jquery带下拉菜单和焦点图代码分享

这篇文章主要介绍了jquery带下拉菜单和焦点图代码,推荐给大家,有需要的小伙伴可以参考下。
收藏 0 赞 0 分享

jQuery实现的背景动态变化导航菜单效果

这篇文章主要介绍了jQuery实现的背景动态变化导航菜单效果,涉及jquery页面元素背景动态变换的实现技巧,非常具有实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

jquery+CSS实现的水平布局多级网页菜单效果

这篇文章主要介绍了jquery+CSS实现的水平布局多级网页菜单效果,涉及jquery页面元素属性动态变换效果实现技巧,非常具有实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多