three.js raysystem

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>
THREE.Ray=function(K,L,M,N){this.origin=K||new THREE.Vector3;this.direction=L||new THREE.Vector3;this.near=M||0;this.far=N||Infinity;var q,p,r,v,s=new THREE.Vector3,t=new THREE.Vector3,u=new THREE.Vector3,D=new THREE.Vector3,w=new THREE.Vector3,m=new THREE.Vector3,x=new THREE.Matrix4,F=function(a,c){return a.distance-c.distance},g=new THREE.Vector3,h=new THREE.Vector3,j=new THREE.Vector3,G,H,c,I=function(a,E,b){g.sub(b,a);G=g.dot(E);H=h.add(a,j.copy(E).multiplyScalar(G));return c=b.distanceTo(H)},
n,i,o,k,y,z,A,B,C=function(a,c,b,e){g.sub(e,c);h.sub(b,c);j.sub(a,c);n=g.dot(g);i=g.dot(h);o=g.dot(j);k=h.dot(h);y=h.dot(j);z=1/(n*k-i*i);A=(k*o-i*y)*z;B=(n*y-i*o)*z;return 0<=A&&0<=B&&1>A+B},J=1E-4;this.setPrecision=function(a){J=a};this.intersectObject=function(a,g){var b,e=[];if(!0===g)for(var f=0,h=a.children.length;f<h;f++)Array.prototype.push.apply(e,this.intersectObject(a.children[f],g));if(a instanceof THREE.Particle){c=I(this.origin,this.direction,a.matrixWorld.getPosition());if(c>a.scale.x)return[];
b={distance:c,point:a.position,face:null,object:a};e.push(b)}else if(a instanceof THREE.Mesh){f=a.geometry.boundingSphere.radius*a.matrixWorld.getMaxScaleOnAxis();c=I(this.origin,this.direction,a.matrixWorld.getPosition());if(c>f)return e;var d,i,j=a.geometry,l=j.vertices,n,o,k;n=a.geometry.materials;o=a.material instanceof THREE.MeshFaceMaterial;a.matrixRotationWorld.extractRotation(a.matrixWorld);s.copy(this.origin);x.getInverse(a.matrixWorld);t.copy(s);x.multiplyVector3(t);u.copy(this.direction);
x.rotateAxis(u).normalize();f=0;for(h=j.faces.length;f<h;f++)if(b=j.faces[f],d=!0===o?n[b.materialIndex]:a.material,void 0!==d&&(k=d.side,D.sub(b.centroid,t),w=b.normal,d=u.dot(w),!(Math.abs(d)<J)&&(i=w.dot(D)/d,!(0>i)&&(k===THREE.DoubleSide||(k===THREE.FrontSide?0>d:0<d)))))if(m.add(t,u.multiplyScalar(i)),b instanceof THREE.Face3)q=l[b.a],p=l[b.b],r=l[b.c],C(m,q,p,r)&&(d=a.matrixWorld.multiplyVector3(m.clone()),c=s.distanceTo(d),!(c<this.near)&&!(c>this.far)&&(b={distance:c,point:d,face:b,faceIndex:f,
object:a},e.push(b)));else if(b instanceof THREE.Face4&&(q=l[b.a],p=l[b.b],r=l[b.c],v=l[b.d],C(m,q,p,v)||C(m,p,r,v)))d=a.matrixWorld.multiplyVector3(m.clone()),c=s.distanceTo(d),!(c<this.near)&&!(c>this.far)&&(b={distance:c,point:d,face:b,faceIndex:f,object:a},e.push(b))}e.sort(F);return e};this.intersectObjects=function(a,c){for(var b=[],e=0,f=a.length;e<f;e++)Array.prototype.push.apply(b,this.intersectObject(a[e],c));b.sort(F);return b}};
</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
local cubes
var intersects = ray2.intersectObjects( cubes.children )
ready
original planes
var intersects = ray1.intersectObjects( planes.children )
ready
local planes
var intersects = ray2.intersectObjects( planes.children )
ready
original combined
var intersects = ray1.intersectObjects( combined.children )
ready
local combined
var intersects = ray2.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

Revisions

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