如何在three.js中的位置半径范围内获取其他3D对象

Bri*_*ian 5 radial three.js raycasting

我在three.js中有一个3D场景,其中我需要获取一个在源对象的X范围内的对象数组.目前,我正在使用的示例是在for循环内部使用光线投射,该循环迭代场景中存在的"可碰撞对象"数组.我觉得必须有一个更好的方法来处理这个问题,因为如果数组中的每个对象都必须从数组中的每个其他对象进行光线投射,这种方法会呈指数级复杂化.随着可碰撞对象阵列的增长,这会产生巨大的性能影响.

//hold collidable objects
var collidableObjects = [];

var scene = new THREE.Scene();

var cubeGeo = new THREE.CubeGeometry( 10 , 10 , 10 );

var materialA = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
var materialB = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );

var cubeA = new THREE.Mesh( cubeGeo , materialA );

collidableObjects.push( cubeA );

scene.add( cubeA );

//Change this variable to a larger number to see the processing time explode
var range = 100;

for( var x = 0 ; x < range ; x += 20 ) {
    for( var z = 0; z < range ; z += 20 ) {

        if( x === 0 && z === 0 ) continue;

        var cube = new THREE.Mesh( cubeGeo , materialB );
        scene.add( cube );
        cube.position.x = x;
        cube.position.z = z;
        collidableObjects.push( cube );

        var cube = cube.clone();
        scene.add( cube );
        cube.position.x = x * -1;
        cube.position.z = z;
        collidableObjects.push( cube );

        var cube = cube.clone();
        scene.add( cube );
        cube.position.x = x;
        cube.position.z = z * -1;
        collidableObjects.push( cube );

        var cube = cube.clone();
        scene.add( cube );
        cube.position.x = x * -1;
        cube.position.z = z * -1;
        collidableObjects.push( cube );

    }
}



var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

camera.position.y = 200;
camera.lookAt( scene.position );

function render() {
    //requestAnimationFrame(render);
    renderer.render(scene, camera);
    console.log( getObjectsWithinRange( cubeA , 30 ) );
}

function getObjectsWithinRange( source , range ) {
    var startTime = new Date().getTime();
    var inRange = [];
    for( var i = 0; i < collidableObjects.length ; ++i ) {
        var ray = new THREE.Raycaster( source.position , collidableObjects[i].position , 0 , range );
        if( ( obj = ray.intersectObject( collidableObjects[i] ) ) && obj.length ) {
            inRange.push( obj[0] );
        }
    }

    var endTime = new Date().getTime();

    console.log( 'Processing Time: ' , endTime - startTime );

    return inRange;
}

render();
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到JSfiddle.

如果您将指示的变量更改为更大的数字,例如200,那么您将看到处理时间开始失控.我觉得必须有降低下来这样的阵列简单的方法使我看着为three.js所的Raycaster的文档,我注意到,无论是nearfar属性说"该值指示哪些对象可以根据丢弃在远处." 所以我假设有一些内部函数用于在投射所有光线之前根据距离细化结果.

我对此进行了一些挖掘,并提出了一个单一的功能Ray.js.

distanceToPoint: function () {

var v1 = new THREE.Vector3();

return function ( point ) {

var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );

// point behind the ray

if ( directionDistance < 0 ) {

return this.origin.distanceTo( point );

}

v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );

return v1.distanceTo( point );

};

}(),
Run Code Online (Sandbox Code Playgroud)

我想我正在寻找的是一种更好的方法来获取场景中所有在源对象的X半径范围内的对象.我甚至不需要使用Raycasting,因为我对网格碰撞不感兴趣,而只是源对象X半径内的对象列表.由于场景的设置方式,我甚至不需要递归到这些物体的子项中.所以我觉得必须有一些内部函数或者只是使用THREE.Vector3对象和数学来按距离细化它们的东西.在这种情况下,运行数学必须比Raycasting便宜得多.如果已经有一个函数在three.js中的某个地方处理它,我不想从头开始重新创建一个.我也意识到这可能是一个非常冗长的问题,因为它很可能是一个单行答案,但我想确保我已经掌握了所有细节和以下内容,以防其他人想要这样做以后再搜索它.

Ice*_*You 10

碰撞检查是一个更普遍的问题,如果您在Three.js之外的上下文中考虑它,我认为您将获得更多成功.有许多方法可以管理需要检查彼此冲突的大量对象.以下是一些可能与您相关的优化:

第一个优化是每个对象都有一个布尔属性,指示自上次物理更新后它是否移动.如果您要比较的两个对象都没有移动,则无需重新计算碰撞.如果您有大量处于稳定状态的物体(例如可以推动的板条箱),这通常是相关的.除此之外,您还可以构建许多其他优化; 例如,通常如果两个物体没有移动,它们就不会碰撞,因为如果它们发生碰撞,它们会反弹(移开).

第二个优化是你通常只需要在一定距离内检查碰撞.例如,如果您知道所有对象都小于100个单位,那么您可以检查是否(x1-x2)^2 + (y1-y2)^2 + (z1-z2)^2 > 100^2.如果检查为真(表示两个对象之间的距离很大),则无需计算详细的碰撞.事实上,这是或多或少near/ far优化是Raycaster为你,但你却不打算把你的代码中使用它,因为你总是调用intersectObject方法.

第三个优化是您Raycaster在每个物理更新中分配一堆新的和相关的对象.相反,您可以保留一个Raycasters池(甚至是一个Raycaster)并只更新它们的属性.这样可以避免大量的垃圾收集.

最后,处理大量可碰撞对象的最常见的通用方法称为空间分区.这个想法基本上是你将你的世界划分为给定数量的空间并跟踪哪些空间物体所在.然后,当你需要计算碰撞时,你只需要检查同一空间中的其他物体.这种方法最常用的方法是使用八叉树(8-ary树).正如WestLangley所提到的,Three.js有一个从r59开始的Octree实现,以及一个例子(源代码).以下是使用2D示例对空间分区概念的合理介绍.

除了这些优化之外,如果您需要做任何特别复杂的事情,您可能需要考虑使用外部物理库,它将为您管理这些优化.最流行的在此刻与three.js所使用的Physijs,Cannon.jsAmmo.js.