three.js raysystem (v3)

Revision 3 of this benchmark created by gero3 on


Preparation HTML

<script src="https://raw.github.com/mrdoob/three.js/dev/build/three.js"></script>
<script>
var orig_ray = THREE.Ray;
</script>
<script src="https://raw.github.com/gist/3376707/a9b7a47e4b8d8a4d3170880ec23276fa70249c8b/ray.js"></script>
<script>
var local_ray = THREE.Ray;
</script>
<script>
(function(b){b.Ray=function(a,z,c,d){this.origin=a||new b.Vector3;this.direction=z||new b.Vector3;this.near=c||0;this.far=d||Infinity};var o=new b.Vector3,p=new b.Vector3,q=new b.Vector3,v=new b.Vector3,r=new b.Vector3,n=new b.Vector3,s=new b.Matrix4,w=function(a,b){return a.distance-b.distance},g=new b.Vector3,h=new b.Vector3,j=new b.Vector3,x=function(a,b,c){var d;g.sub(c,a);d=g.dot(b);a=h.add(a,j.copy(b).multiplyScalar(d));return c.distanceTo(a)},t=function(a,b,c,d){var e,k;g.sub(d,b);h.sub(c,
b);j.sub(a,b);a=g.dot(g);b=g.dot(h);c=g.dot(j);e=h.dot(h);d=h.dot(j);k=1/(a*e-b*b);e=(e*c-b*d)*k;a=(a*d-b*c)*k;return 0<=e&&0<=a&&1>e+a};b.Ray.prototype.precision=1E-4;b.Ray.prototype.setPrecision=function(a){this.precision=a};b.Ray.prototype.intersectObject=function(a,g){var c,d=[];if(!0===g)for(var e=0,k=a.children.length;e<k;e++)Array.prototype.push.apply(d,this.intersectObject(a.children[e],g));if(a instanceof b.Particle){distance=x(this.origin,this.direction,a.matrixWorld.getPosition());if(distance>
a.scale.x)return[];c={distance:distance,point:a.position,face:null,object:a};d.push(c)}else if(a instanceof b.Mesh){e=a.geometry.boundingSphere.radius*a.matrixWorld.getMaxScaleOnAxis();distance=x(this.origin,this.direction,a.matrixWorld.getPosition());if(distance>e)return d;var f,i,h=a.geometry,l=h.vertices,j,y,m;j=a.geometry.materials;y=a.material instanceof b.MeshFaceMaterial;var u,A=this.precision;a.matrixRotationWorld.extractRotation(a.matrixWorld);o.copy(this.origin);s.getInverse(a.matrixWorld);
p.copy(o);s.multiplyVector3(p);q.copy(this.direction);s.rotateAxis(q).normalize();e=0;for(k=h.faces.length;e<k;e++)if(c=h.faces[e],f=!0===y?j[c.materialIndex]:a.material,void 0!==f&&(m=f.side,v.sub(c.centroid,p),r=c.normal,f=q.dot(r),!(Math.abs(f)<A)&&(i=r.dot(v)/f,!(0>i)&&(m===b.DoubleSide||(m===b.FrontSide?0>f:0<f)))))if(n.add(p,q.multiplyScalar(i)),c instanceof b.Face3)f=l[c.a],i=l[c.b],m=l[c.c],t(n,f,i,m)&&(f=a.matrixWorld.multiplyVector3(n.clone()),distance=o.distanceTo(f),!(distance<this.near)&&
!(distance>this.far)&&(c={distance:distance,point:f,face:c,faceIndex:e,object:a},d.push(c)));else if(c instanceof b.Face4&&(f=l[c.a],i=l[c.b],m=l[c.c],u=l[c.d],t(n,f,i,u)||t(n,i,m,u)))f=a.matrixWorld.multiplyVector3(n.clone()),distance=o.distanceTo(f),!(distance<this.near)&&!(distance>this.far)&&(c={distance:distance,point:f,face:c,faceIndex:e,object:a},d.push(c))}d.sort(w);return d};b.Ray.prototype.intersectObjects=function(a,b){for(var c=[],d=0,e=a.length;d<e;d++)Array.prototype.push.apply(c,this.intersectObject(a[d],
b));c.sort(w);return c}})(THREE);
</script>
<script>
var optimized_local_ray = THREE.Ray;
</script>

Setup

camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
                                camera.position.set( 0, 300, 50 );
                                scene = new THREE.Scene();
            var cubes = new THREE.Object3D();
            scene.add(cubes);
    
                                var geometry = new THREE.CubeGeometry( 20, 20, 20 );
    
                                for ( var i = 0; i < 50; i ++ ) {
    
                                        var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: (i/50) * 0xffffff } ) );
    
                                        object.position.x = (i/50) * 800 - 400;
                                        object.position.y = (i/50) * 800 - 400;
                                        object.position.z = (i/50) * 800 - 400;
    
                                        object.rotation.x = ( (i/50) * 360 ) * Math.PI / 180;
                                        object.rotation.y = ( (i/50) * 360 ) * Math.PI / 180;
                                        object.rotation.z = ( (i/50) * 360 ) * Math.PI / 180;
    
                                        object.scale.x = (i/50) * 2 + 1;
                                        object.scale.y = (i/50) * 2 + 1;
                                        object.scale.z = (i/50) * 2 + 1;
    
                                        cubes.add( object );
              object.updateMatrixWorld();
                                }
    
            var planes = new THREE.Object3D();
            scene.add(planes);
    
                                var geometry = new THREE.PlaneGeometry( 20, 20);
    
                                for ( var i = 0; i < 50; i ++ ) {
    
                                        var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: (i/50) * 0xffffff } ) );
    
                                        object.position.x = (i/50) * 800 - 400;
                                        object.position.y = (i/50) * 800 - 400;
                                        object.position.z = (i/50) * 800 - 400;
    
                                        object.rotation.x = ( (i/50) * 360 ) * Math.PI / 180;
                                        object.rotation.y = ( (i/50) * 360 ) * Math.PI / 180;
                                        object.rotation.z = ( (i/50) * 360 ) * Math.PI / 180;
    
                                        object.scale.x = (i/50) * 2 + 1;
                                        object.scale.y = (i/50) * 2 + 1;
                                        object.scale.z = (i/50) * 2 + 1;
    
                                        planes.add( object );
              object.updateMatrixWorld();
                                }
    
            var combined = new THREE.Object3D();
            scene.add(combined);
    
                                var geometry = new THREE.CubeGeometry( 20, 20,20);
            var combGeometry = new THREE.CubeGeometry( 20, 20,20);
                                for ( var i = 0; i < 50; i ++ ) {
    
                                        var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: (i/50) * 0xffffff } ) );
    
                                        object.position.x = (i/50) * 800 - 400;
                                        object.position.y = (i/50) * 800 - 400;
                                        object.position.z = (i/50) * 800 - 400;
    
                                        object.rotation.x = ( (i/50) * 360 ) * Math.PI / 180;
                                        object.rotation.y = ( (i/50) * 360 ) * Math.PI / 180;
                                        object.rotation.z = ( (i/50) * 360 ) * Math.PI / 180;
    
                                        object.scale.x = (i/50) * 2 + 1;
                                        object.scale.y = (i/50) * 2 + 1;
                                        object.scale.z = (i/50) * 2 + 1;
    
              //object.updateMatrixWorld();
                                        THREE.GeometryUtils.merge(combGeometry, object );
    
                                }
            var object = new THREE.Mesh( combGeometry, new THREE.MeshLambertMaterial( { color: 0.5 * 0xffffff } ) );
            combined.add(object);
    
            object.updateMatrixWorld();
    
    
                                var vector = new THREE.Vector3( 0, 0, 1 );
                                projector = new THREE.Projector();
                                projector.unprojectVector( vector, camera );
    
                                var ray1= new orig_ray( camera.position, vector.subSelf( camera.position ).normalize() );
                                var ray2= new local_ray( camera.position, vector.subSelf( camera.position ).normalize() );
                                var ray3= new optimized_local_ray( camera.position, vector.subSelf( camera.position ).normalize() );

Test runner

Ready to run.

Testing in
TestOps/sec
original cubes
var intersects = ray1.intersectObjects( cubes.children )
ready
original planes
var intersects = ray1.intersectObjects( planes.children )
ready
original combined
var intersects = ray1.intersectObjects( combined.children )
ready
optimized cubes
var intersects = ray3.intersectObjects( cubes.children )
ready
optimized planes
var intersects = ray3.intersectObjects( planes.children )
ready
optimized combined
var intersects = ray3.intersectObjects( combined.children )
ready
original creation
new orig_ray()
ready
optimized creation
new optimized_local_ray()
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.