Three.js - 用于碰撞检测的准确光线投射 [英] Three.js - Accurate ray casting for collision detection

查看:21
本文介绍了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:

它应该在左上角显示Hit",那里只有一堆点,但事实并非如此.注意:我也尝试了他的建议并执行了以下操作,但似乎没有太大帮助:

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() )

推荐答案

首先回答您的最后一个问题:该行检测碰撞是否发生在您的移动立方体内部.您的光线投射代码从移动立方体的位置向其每个顶点投射光线.返回与光线相交的任何东西,以及与找到相交对象的移动立方体位置的距离(collisionResults[0].distance).该距离与从移动立方体的位置到相关顶点的距离进行比较.如果到碰撞的距离小于到顶点的距离,则碰撞发生在立方体内部.

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.

对于更复杂的应用程序,您可能需要使用 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. :-)

我写了一本名为 Game Development with Three.js 的书,其中深入探讨了这个主题.(我不会在这里链接它,因为我不是来宣传它的,但如果你有兴趣,你可以谷歌一下.)这本书附带了示例代码,展示了如何进行基本的碰撞检测,包括完整的代码一款 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天全站免登陆