Three.js-准确的射线投射以进行碰撞检测 [英] Three.js - Accurate ray casting for collision detection

查看:166
本文介绍了Three.js-准确的射线投射以进行碰撞检测的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Three.js版本 68 。我正在使用与这个人在这里使用的相同的碰撞检测方法,这在大多数情况下都是很好的(对作者非常感谢!): http://stemkoski.github.io/Three.js/Collision-Detection.html

I'm working with Three.js, version 68. I'm using the same method for collision detection as this guy is using here, which is great most of the time (A big "thank you" goes out to the author!): http://stemkoski.github.io/Three.js/Collision-Detection.html

如果您想从github下载它,请点击这里。只需查找 Collision-Detection.html https:// github。 com / stemkoski / stemkoski.github.com

Here is a link to the source if you want to download it from github. Just look for Collision-Detection.html: https://github.com/stemkoski/stemkoski.github.com

以下是对碰撞检测很重要的代码:

Here is the code that is important to the collision detection:

var MovingCube;
var collidableMeshList = [];

var wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(100, 50, -100);
scene.add(wall);
collidableMeshList.push(wall);
var wall = new THREE.Mesh(wallGeometry, wireMaterial);
wall.position.set(100, 50, -100);
scene.add(wall);

var wall2 = new THREE.Mesh(wallGeometry, wallMaterial);
wall2.position.set(-150, 50, 0);
wall2.rotation.y = 3.14159 / 2;
scene.add(wall2);
collidableMeshList.push(wall2);
var wall2 = new THREE.Mesh(wallGeometry, wireMaterial);
wall2.position.set(-150, 50, 0);
wall2.rotation.y = 3.14159 / 2;
scene.add(wall2);

var cubeGeometry = new THREE.CubeGeometry(50,50,50,1,1,1);
var wireMaterial = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe:true } );
MovingCube = new THREE.Mesh( cubeGeometry, wireMaterial );
MovingCube.position.set(0, 25.1, 0);


// collision detection:
//   determines if any of the rays from the cube's origin to each vertex
//      intersects any face of a mesh in the array of target meshes
//   for increased collision accuracy, add more vertices to the cube;
//      for example, new THREE.CubeGeometry( 64, 64, 64, 8, 8, 8, wireMaterial )
//   HOWEVER: when the origin of the ray is within the target mesh, collisions do not occur
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( collidableMeshList );
    if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() ) 
        appendText(" Hit ");
}

在大多数情况下,这种方法很有效,但是有时候我可以移动立方体部分插入墙中,不会发生碰撞。例如,查看以下图像:

This works great most of the time, but there are times when I can move the cube partially into the wall, and it won't register a collision. For example, look at this image:

在左上角只有一堆点的地方应该说命中,而实际上不是。
注意::我也尝试了他的建议并进行了以下操作,但似乎并没有太大帮助:

It should say "Hit" in the top-left corner where there are just a bunch of dots, and it's not. NOTE: I also tried his suggestion and did the following, but it didn't seem to help much:

THREE.BoxGeometry( 64, 64, 64, 8, 8, 8, wireMaterial ) // BoxGeometry is used in version 68 instead of CubeGeometry

有人知道这种方法会更准确吗? 另一个问题:是否有人知道以下 if 语句的含义,即为什么对象的距离必须小于方向矢量的长度?

Does anyone know how this method could be more accurate? Another question: Does anyone know what the following if statement is for, i.e. why does the object's distance have to be less than the length of the direction vector?:

if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() )


推荐答案

首先要回答最后一个问题:该行检测碰撞是否发生在您的MovingCube中。您的光线投射代码将光线从MovingCube的位置投射到其每个顶点。射线相交的所有内容以及与发现相交对象的MovingCube位置的距离( collisionResults [0] .distance )都将返回。将该距离与从MovingCube的位置到相关顶点的距离进行比较。如果到碰撞的距离小于到顶点的距离,则碰撞发生在立方体内部。

To answer your last question first: that line detects whether the collision happened inside your MovingCube. Your raycasting code casts a ray from the MovingCube's position towards each of its vertices. Anything that the ray intersects with is returned, along with the distance from the MovingCube's position at which the intersected object was found (collisionResults[0].distance). That distance is compared with the distance from the MovingCube's position to the relevant vertex. If the distance to the collision is less than the distance to the vertex, the collision happened inside the cube.

射线投射是一种较差的碰撞检测方法,因为它仅检测碰撞沿准确的方向投射光线。它还具有一些其他优势。例如,如果射线是从另一个对象内部投射的,则该另一个对象可能不被视为碰撞。再举一个例子,Three.js中的光线投射使用边界球(或边界框,如果不可用)来计算光线的相交,因此光线可以与对象相交,即使它们不会在视觉上撞到它们。

Raycasting is a poor method of collision detection because it only detects collisions in the exact directions rays are cast. It also has some additional edge cases. For example, if the ray is cast from inside another object, the other object might not be considered to be colliding. As another example, raycasting in Three.js uses bounding spheres (or, if unavailable, bounding boxes) to calculate ray intersection, so rays can "intersect" with objects even if they wouldn't hit them visually.

如果您仅处理球体或直立的长方体,那么检查碰撞的数学运算就很简单。 (这就是Three.js使用边界球和边界框的原因-大多数需要进行碰撞检查的应用程序都使用次要的仅发生碰撞的几何体,而不是渲染的几何体。)如果球体中心之间的距离小于半径之和。如果边缘重叠,则框会发生碰撞(例如,如果框1的左边缘在框2的右边缘的左侧,并且框在垂直距离之内是其半高的总和,而水平距离则是它们的半高的总和)

If you're only dealing with spheres or upright cuboids, it's straightforward math to check collision. (That's why Three.js uses bounding spheres and bounding boxes - and most applications that need to do collision checking use secondary collision-only geometries that are less complicated than the rendered ones.) Spheres are colliding if the distance between their centers is less than the sum of their radii. Boxes are colliding if the edges overlap (e.g. if the left edge of box 1 is to the left of the right edge of box 2, and the boxes are within a vertical distance the sum of their half-heights and a horizontal distance the sum of their half-lengths).

对于某些应用程序,您还可以使用体素,例如将世界划分为立方单位,进行盒式数学运算,并说如果两个对象与相同的立方单位重叠,则会发生碰撞。

For certain applications you can also use voxels, e.g. divide the world into cubical units, do box math, and say that two objects are colliding if they overlap with the same cube-unit.

对于更复杂的应用程序, ll可能想使用Ammo.js,Cannon.js或Physi.js之类的库。

For more complex applications, you'll probably want to use a library like Ammo.js, Cannon.js, or Physi.js.

射线投射之所以吸引人,是因为它可以用于更复杂的几何体而无需使用图书馆。但是,您发现它并不完美。 :-)

The reason raycasting is appealing is because it's workable with more complex geometries without using a library. As you've discovered, however, it's less than perfect. :-)

我写了一本书《 Three.js的游戏开发》,深入探讨了该主题。 (我不会在这里链接到它,因为我不是来宣传它的,但是如果您有兴趣,可以在Google上找到它。)该书附带了示例代码,显示了如何进行基本的冲突检测,包括用于3D夺旗游戏。

I wrote a book called Game Development with Three.js which goes into this topic in some depth. (I won't link to it here because I'm not here to promote it, but you can Google it if you're interested.) The book comes with sample code that shows how to do basic collision detection, including full code for a 3D capture-the-flag game.

这篇关于Three.js-准确的射线投射以进行碰撞检测的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆