浏览代码

Merge remote-tracking branch 'm-schuetz/dev_raycasting_pointclouds' into dev

Mr.doob 11 年之前
父节点
当前提交
205f4a8bfa
共有 3 个文件被更改,包括 493 次插入13 次删除
  1. 1 0
      examples/index.html
  2. 344 0
      examples/webgl_interactive_raycasting_pointcloud.html
  3. 148 13
      src/core/Raycaster.js

+ 1 - 0
examples/index.html

@@ -158,6 +158,7 @@
 				"webgl_interactive_cubes_gpu",
 				"webgl_interactive_draggablecubes",
 				"webgl_interactive_particles",
+				"webgl_interactive_raycasting_pointcloud",
 				"webgl_interactive_voxelpainter",
 				"webgl_kinect",
 				"webgl_lensflares",

+ 344 - 0
examples/webgl_interactive_raycasting_pointcloud.html

@@ -0,0 +1,344 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - interactive - raycasting - pointcloud</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				color: #ffffff;
+				background-color: #000000;
+				margin: 0px;
+				overflow: hidden;
+			}
+			#info {
+				position: absolute;
+				top: 0px;
+				width: 100%;
+				padding: 5px;
+				font-family: Monospace;
+				font-size: 13px;
+				text-align: center;
+				font-weight: bold;
+			}
+			a {
+				color: #fff;
+			}
+		</style>
+	</head>
+
+	<body>
+		<div id="container"></div>
+		<div id="info"><a href="http://threejs.org" target="_blank">three.js</a> webgl - interactive - raycasting - pointcloud </div>
+
+		<script src="../build/three.min.js"></script>
+
+		<script src="js/Detector.js"></script>
+		<script src="js/libs/stats.min.js"></script>
+
+		<script>
+
+			if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
+
+			var renderer, scene, camera, stats;
+			var pointclouds;
+			var projector, raycaster, intersects;
+			var mouse = { x: 1, y: 1 };
+			var vector = new THREE.Vector3();
+			var intersection = null;
+			var spheres = [];
+			var spheresIndex = 0;
+			var clock;
+			
+			var threshold = 0.1;
+			var pointSize = 0.01;
+			var width = 150;
+			var length = 150;
+			var rotateY = new THREE.Matrix4().makeRotationY( 0.005 );
+
+			init();
+			animate();
+			
+			function generatePointCloudGeometry( color, width, length ){
+			
+				var geometry = new THREE.BufferGeometry();
+				var numPoints = width*length;
+				
+				var positions = new Float32Array( numPoints*3 );
+				var colors = new Float32Array( numPoints*3 );
+				
+				var k = 0;
+				for( var i = 0; i < width; i++ ) { 
+					for( var j = 0; j < length; j++ ) {
+						var u = i / width;
+						var v = j / length;
+						var x = u - 0.5;
+						var y = ( Math.cos( u * Math.PI * 8 ) + Math.sin( v * Math.PI * 8 ) ) / 20;
+						var z = v - 0.5;
+						
+						positions[ 3 * k ] = x;
+						positions[ 3 * k + 1 ] = y;
+						positions[ 3 * k + 2 ] = z;
+						
+						var intensity = ( y + 0.1 ) * 5;
+						colors[ 3 * k ] = color.r * intensity;
+						colors[ 3 * k + 1 ] = color.g * intensity;
+						colors[ 3 * k + 2 ] = color.b * intensity;
+						k++;
+					}
+				}
+				
+				geometry.addAttribute( 'position', new THREE.Float32Attribute( positions, 3 ) );
+				geometry.addAttribute( 'color', new THREE.Float32Attribute( colors, 3 ) );
+				geometry.computeBoundingBox();
+				
+				return geometry;
+				
+			}
+			
+			function generatePointcloud( color, width, length ) {
+			
+				var geometry = generatePointCloudGeometry( color, width, length );
+				
+				var material = new THREE.PointCloudMaterial( { size: pointSize, vertexColors: true } );
+				var pointcloud = new THREE.PointCloud( geometry, material );
+				
+				return pointcloud;
+				
+			}
+			
+			function generateIndexedPointcloud( color, width, length ) {
+			
+				var geometry = generatePointCloudGeometry( color, width, length );
+				var numPoints = width * length;
+				var indices = new Uint16Array( numPoints );
+				
+				var k = 0;
+				for( var i = 0; i < width; i++ ) {
+					for( var j = 0; j < length; j++ ) {
+						indices[ k ] = k;
+						k++;
+					}
+				}
+				
+				geometry.addAttribute( 'index', new THREE.Uint16Attribute( indices, 1 ) );
+				
+				var material = new THREE.PointCloudMaterial( { size: pointSize, vertexColors: true } );
+				var pointcloud = new THREE.PointCloud( geometry, material );
+				
+				return pointcloud;
+				
+			}
+			
+			function generateIndexedWithOffsetPointcloud( color, width, length ){
+			
+				var geometry = generatePointCloudGeometry( color, width, length );
+				var numPoints = width * length;
+				var indices = new Uint16Array( numPoints );
+				
+				var k = 0;
+				for( var i = 0; i < width; i++ ){
+					for( var j = 0; j < length; j++ ) {
+						indices[ k ] = k;
+						k++;
+					}
+				}
+				
+				geometry.addAttribute( 'index', new THREE.Uint16Attribute( indices, 1 ) );
+				
+				var offset = { start: 0, count: indices.length, index: 0 };
+				geometry.offsets.push( offset );
+				
+				var material = new THREE.PointCloudMaterial( { size: pointSize, vertexColors: true } );
+				var pointcloud = new THREE.PointCloud( geometry, material );
+				
+				return pointcloud;
+				
+			}
+			
+			function generateRegularPointcloud( color, width, length ) {
+			
+				var geometry = new THREE.Geometry();
+				var numPoints = width * length;
+				
+				var colors = [];
+				
+				var k = 0;
+				for( var i = 0; i < width; i++ ) {
+					for( var j = 0; j < length; j++ ) {
+						var u = i / width;
+						var v = j / length;
+						var x = u - 0.5;
+						var y = ( Math.cos( u * Math.PI * 8 ) + Math.sin( v * Math.PI * 8) ) / 20;
+						var z = v - 0.5;	
+						var v = new THREE.Vector3( x,y,z );
+						
+						var intensity = ( y + 0.1 ) * 7;
+						colors[ 3 * k ] = color.r * intensity;
+						colors[ 3 * k + 1 ] = color.g * intensity;
+						colors[ 3 * k + 2 ] = color.b * intensity;
+						
+						geometry.vertices.push( v );
+						colors[ k ] = ( color.clone().multiplyScalar( intensity ) );
+						
+						k++;
+					}
+				}
+				
+				geometry.colors = colors;
+				geometry.computeBoundingBox();
+				
+				var material = new THREE.PointCloudMaterial( { size: pointSize, vertexColors: true } );
+				var pointcloud = new THREE.PointCloud( geometry, material );
+				
+				return pointcloud;
+				
+			}
+
+			function init() {
+
+				container = document.getElementById( 'container' );
+
+				scene = new THREE.Scene();
+				
+				clock = new THREE.Clock();
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 10000 );
+				camera.applyMatrix( new THREE.Matrix4().makeTranslation( 0,0,20 ) );
+				camera.applyMatrix( new THREE.Matrix4().makeRotationX( -0.5 ) );
+
+				//
+
+				pcBuffer = generatePointcloud( new THREE.Color( 1,0,0 ), width, length );
+				pcBuffer.scale.set( 10,10,10 );
+				pcBuffer.position.set( -5,0,5 );
+				scene.add( pcBuffer );
+					
+				var pcIndexed = generateIndexedPointcloud( new THREE.Color( 0,1,0 ), width, length );
+				pcIndexed.scale.set( 10,10,10 );
+				pcIndexed.position.set( 5,0,5 );
+				scene.add( pcIndexed );
+			
+				var pcIndexedOffset = generateIndexedWithOffsetPointcloud( new THREE.Color( 0,1,1 ), width, length );
+				pcIndexedOffset.scale.set( 10,10,10 );
+				pcIndexedOffset.position.set( 5,0,-5 );
+				scene.add( pcIndexedOffset );
+				
+				var pcRegular = generateRegularPointcloud( new THREE.Color( 1,0,1 ), width, length );
+				pcRegular.scale.set( 10,10,10 );
+				pcRegular.position.set( -5,0,-5 );
+				scene.add( pcRegular );
+                
+				pointclouds = [ pcBuffer, pcIndexed, pcIndexedOffset, pcRegular ];
+				
+				//
+				
+				var sphereGeometry = new THREE.SphereGeometry( 0.1, 32, 32 );
+				var sphereMaterial = new THREE.MeshBasicMaterial( { color: 0xff0000, shading: THREE.FlatShading } );
+				
+				for ( var i = 0; i < 40; i++ ) { 
+				
+					var sphere = new THREE.Mesh( sphereGeometry, sphereMaterial );
+					scene.add( sphere );
+					spheres.push( sphere );
+				
+				}
+
+				//
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+				container.appendChild( renderer.domElement );
+
+				//
+
+				projector = new THREE.Projector();
+				raycaster = new THREE.Raycaster();
+				raycaster.params.PointCloud.threshold = threshold;
+
+				//
+
+				stats = new Stats();
+				stats.domElement.style.position = 'absolute';
+				stats.domElement.style.top = '0px';
+				container.appendChild( stats.domElement );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize, false );
+				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
+				
+			}
+
+			function onDocumentMouseMove( event ) {
+
+				event.preventDefault();
+
+				mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
+				mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+			
+			var toggle = 0;
+			
+			function render() { 
+			
+				camera.applyMatrix( rotateY );
+				camera.updateMatrixWorld( true );
+				
+				vector.set( mouse.x, mouse.y, 0.1 );
+
+				projector.unprojectVector( vector, camera );
+
+				raycaster.ray.set( camera.position, vector.sub( camera.position ).normalize() );
+			    
+				var intersections = raycaster.intersectObjects( pointclouds );
+				intersection = ( intersections.length ) > 0 ? intersections[ 0 ] : null;
+				
+				if ( toggle > 0.02 && intersection !== null) {
+					
+					spheres[ spheresIndex ].position.copy( intersection.point );
+					spheres[ spheresIndex ].scale.set( 1, 1, 1 );
+					spheresIndex = ( spheresIndex + 1 ) % spheres.length;
+					
+					toggle = 0;
+				
+				}
+				
+				for ( var i = 0; i < spheres.length; i++ ) {
+					
+					var sphere = spheres[ i ];
+					sphere.scale.multiplyScalar( 0.98 );
+					sphere.scale.clampScalar( 0.01, 1 );
+				
+				}
+				
+				toggle += clock.getDelta();
+
+				renderer.render( scene, camera );
+			}
+
+		</script>
+
+	</body>
+
+</html>

+ 148 - 13
src/core/Raycaster.js

@@ -14,6 +14,14 @@
 		this.near = near || 0;
 		this.far = far || Infinity;
 
+		this.params = {
+			Sprite: {},
+			Mesh: {},
+			PointCloud: { threshold: 1 },
+			LOD: {},
+			Line: {}
+		};
+		
 	};
 
 	var sphere = new THREE.Sphere();
@@ -59,30 +67,157 @@
 
 
 		} else if ( object instanceof THREE.PointCloud ) {
+		
+			var geometry = object.geometry;
+			var threshold = raycaster.params.PointCloud.threshold;
 
-			var vertices = object.geometry.vertices;
-
-			for ( var i = 0; i < vertices.length; i ++ ) {
+			inverseMatrix.getInverse( object.matrixWorld );  
+			localRay.copy( raycaster.ray ).applyMatrix4( inverseMatrix );
+			
+			if ( geometry.boundingBox !== null ) {
+			
+				if ( localRay.isIntersectionBox( geometry.boundingBox ) === false )  {
+				
+					return intersects;
+					
+				}
+				
+			}
+			
+			var localThreshold = threshold / ( ( object.scale.x + object.scale.y + object.scale.z ) / 3 );
+			var pos = new THREE.Vector3();
 
-				var v = vertices[ i ];
+			if ( geometry instanceof THREE.BufferGeometry ) {
+			
+				var attributes = geometry.attributes;
+				var positions = attributes.position.array;
+				
+				if ( attributes.index !== undefined ) {
+				
+					var indices = attributes.index.array;
+					var offsets = geometry.offsets;
+					
+					if ( offsets.length === 0 ) {
 
-				matrixPosition.copy( v ).applyMatrix4( object.matrixWorld );
+						var offset = { 
+							start: 0, 
+							count: indices.length, 
+							index: 0
+						};
+						
+						offsets = [ offset ];
 
-				var distance = raycaster.ray.distanceToPoint( matrixPosition );
+					}
+					
+					for ( var oi = 0, ol = offsets.length; oi < ol; ++oi ) {
 
-				if ( distance < 1 ) { // needs a better test; particle size?
+						var start = offsets[ oi ].start;
+						var count = offsets[ oi ].count;
+						var index = offsets[ oi ].index;
 
-					intersects.push( {
+						for ( var i = start, il = start + count; i < il; i++ ) {
+						
+							var a = index + indices[ i ];
+						
+							pos.set(
+								positions[ a * 3 ],
+								positions[ a * 3 + 1 ],
+								positions[ a * 3 + 2 ]
+							);
+							
+							var rayPointDistance = localRay.distanceToPoint( pos );
 
-						distance: distance,
-						index: i,
-						face: null,
-						object: object
+							if ( rayPointDistance < localThreshold ) {
+								
+								var intersectPoint = localRay.closestPointToPoint( pos );
+								intersectPoint.applyMatrix4( object.matrixWorld );
+								var distance = raycaster.ray.origin.distanceTo( intersectPoint );
+							
+								intersects.push( {
+							
+									distance: distance,
+									distanceToRay: rayPointDistance,
+									point: intersectPoint.clone(),
+									index: a,
+									face: null,
+									object: object
+									
+								} );
+							
+							}
+						
+						}
+						
+					}
+				
+				}else{
+				
+					var pointCount = positions.length / 3;
+
+					for (var i = 0; i < pointCount; i++ ) {
+					
+						pos.set(
+							positions[ 3 * i ], 
+							positions[ 3 * i + 1 ], 
+							positions[ 3 * i + 2 ]
+						);
 
-					} );
+						var rayPointDistance = localRay.distanceToPoint( pos );
 
+						if ( rayPointDistance < localThreshold ) {
+							
+							var intersectPoint = localRay.closestPointToPoint( pos );
+							intersectPoint.applyMatrix4( object.matrixWorld );
+							var distance = raycaster.ray.origin.distanceTo( intersectPoint );
+							
+							intersects.push( {
+							
+								distance: distance,
+								distanceToRay: rayPointDistance,
+								point: intersectPoint,
+								index: i,
+								face: null,
+								object: object
+								
+							} );
+							
+						}
+						
+					}
+					
 				}
+				
+			} else {
+			
+				var vertices = object.geometry.vertices;
+
+				for ( var i = 0; i < vertices.length; i ++ ) {
 
+					pos = vertices[ i ];
+					
+					var rayPointDistance = localRay.distanceToPoint( pos );
+
+					if ( rayPointDistance < localThreshold ) {
+						
+						var intersectPoint = localRay.closestPointToPoint( pos );
+						intersectPoint.applyMatrix4( object.matrixWorld );
+						var distance = raycaster.ray.origin.distanceTo( intersectPoint );
+						
+						intersects.push( {
+						
+							distance: distance,
+							distanceToRay: rayPointDistance,
+							point: intersectPoint.clone(),
+							index: i,
+							face: null,
+							object: object
+							
+						} );
+						
+					}
+
+				}
+				
 			}
 
 		} else if ( object instanceof THREE.LOD ) {