Three.js - 精确的光线投射用于碰撞检测

4
我正在使用版本为68的Three.js。我的碰撞检测方法与这位作者在此处使用的相同,大多数情况下非常好用(向作者致以最大的感谢!):http://stemkoski.github.io/Three.js/Collision-Detection.html 如果您想从github下载源代码,请点击以下链接,查找Collision-Detection.htmlhttps://github.com/stemkoski/stemkoski.github.com 以下是与碰撞检测相关的代码:
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 ");
}

这通常很有效,但有时我会将立方体部分地移入墙壁中,却不会注册碰撞。例如,看看这张图片:Collision Detection 在左上角应该显示“命中”,但没有。 注意:我也尝试了他的建议并进行了以下操作,但似乎帮助不大。
THREE.BoxGeometry( 64, 64, 64, 8, 8, 8, wireMaterial ) // BoxGeometry is used in version 68 instead of CubeGeometry

有人知道如何使这个方法更精确吗?另一个问题:有人知道以下if语句是用于什么的,即为什么对象的距离必须小于方向向量的长度吗?

if ( collisionResults.length > 0 && collisionResults[0].distance < directionVector.length() )
1个回答

6
首先回答你的最后一个问题:该行代码检测是否发生碰撞在你的MovingCube内。你的射线投射代码从MovingCube的位置向其每个顶点投射一条射线。射线与之相交的任何东西都将被返回,以及从MovingCube的位置找到相交对象的距离(collisionResults [0] .distance)。将该距离与到相关顶点的距离进行比较。如果到碰撞的距离小于到顶点的距离,则碰撞发生在立方体内。
射线投射是一种不好的碰撞检测方法,因为它仅能检测沿着射线精确方向的碰撞。它还有一些额外的特殊情况。例如,如果从另一个对象内部投射射线,则可能不认为该对象正在碰撞。另一个例子是,在Three.js中进行射线投射使用包围球(如果不可用,则使用包围框)来计算射线相交,因此即使射线与对象不会产生视觉上的命中,也可以“相交”。
如果您只处理球体或竖直长方体,则可以轻松地通过数学运算来检查碰撞。 (这就是为什么Three.js使用包围球和包围盒的原因 - 大多数需要进行碰撞检测的应用程序使用次要的仅碰撞几何体,这些几何体比渲染的几何体简单。)如果两个球体的中心点距离小于其半径之和,则它们发生碰撞。如果两个长方体边重叠(例如,如果盒子1的左边缘在盒子2的右边缘左侧,并且盒子在垂直距离等于它们半高度之和以及水平距离等于它们半长度之和时),则会发生碰撞。
对于某些应用程序,还可以使用体素(如将世界分成立方体单元,进行盒形运算,并说两个对象与相同的立方体单元重叠即发生碰撞)。
对于更复杂的应用程序,您可能需要使用像Ammo.js、Cannon.js或Physi.js之类的库。
射线投射有吸引力的原因是,即使不使用库也可以使用更复杂的几何形状。但正如你所发现的那样,它并不完美。 :-)
我写了一本名为《Game Development with Three.js》的书,深入探讨了这个主题。(我不会在这里链接它,因为我不是来推销它的,但是如果您感兴趣,可以Google它。)该书附带有演示基本碰撞检测的示例代码,包括一个3D占旗帜游戏的完整代码。

感谢您的出色回答!我正在尝试使用three.js使边界框正常工作。基本上,您可以执行以下操作:object.geometry.computeBoundingBox(); //object is THREE.Mesh var objectBoundingBox = object.geometry.boundingBox.clone();然后,您可以在另一个网格上执行相同的操作,并执行以下操作以查看它们是否相交: if (object.isIntersectionBox(otherObjectBoundingBox))但是,对于我来说,这总是正确的!Box3是边界框,源代码在此处:链接您有什么想法我可能做错了什么? - Programmer_D
在我之前的评论中,if语句有误。应该是这样的:if (objectBoundingBox.isIntersectionBox(otherObjectBoundingBox)) - Programmer_D
我会使用console.log()打印出你要比较的盒子,手动尝试isIntersectionBox源代码中的条件,看看得到什么结果,然后再从那里开始。 - IceCreamYou
这是一个边界框示例的jsfiddle链接:jsfiddle。正如您所看到的,这些立方体彼此之间并不接近,但是isIntersectionBox返回true。似乎边界框计算正确。我不确定该怎么做才能使它变成false。 - Programmer_D
好的,看起来这篇文章很有用!:Three.js: wrong BoundingBox after scaling a geometry。感谢使用边界框的想法 =) - Programmer_D

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接