2
0
Эх сурвалжийг харах

Raytracing: RaytracingWorkerRenderer.js -> RaytracingRenderer.js

- also raytracing_sandbox_workers.html -> raytracing_sandbox.html
Joshua Koo 9 жил өмнө
parent
commit
1fdcba0e57

+ 1 - 2
examples/index.html

@@ -475,8 +475,7 @@
 				"canvas_sandbox"
 			],
 			"raytracing": [
-				"raytracing_sandbox",
-				"raytracing_sandbox_workers"
+				"raytracing_sandbox"
 			],
 			"software": [
 				"software_geometry_earth",

+ 143 - 402
examples/js/renderers/RaytracingRenderer.js

@@ -1,6 +1,9 @@
 /**
- * @author mrdoob / http://mrdoob.com/
- * @author alteredq / http://alteredqualia.com/
+ * RaytracingRenderer renders by raytracing it's scene. However, it does not
+ * compute the pixels itself but it hands off and coordinates the taks for workers.
+ * The workers compute the pixel values and this renderer simply paints it to the Canvas.
+ *
+ * @author zz85 / http://github.com/zz85
  */
 
 THREE.RaytracingRenderer = function ( parameters ) {
@@ -10,6 +13,8 @@ THREE.RaytracingRenderer = function ( parameters ) {
 	parameters = parameters || {};
 
 	var scope = this;
+	var pool = [];
+	var renderering = false;
 
 	var canvas = document.createElement( 'canvas' );
 	var context = canvas.getContext( '2d', {
@@ -23,469 +28,198 @@ THREE.RaytracingRenderer = function ( parameters ) {
 
 	var clearColor = new THREE.Color( 0x000000 );
 
-	var origin = new THREE.Vector3();
-	var direction = new THREE.Vector3();
-
-	var cameraPosition = new THREE.Vector3();
-
-	var raycaster = new THREE.Raycaster( origin, direction );
-	var raycasterLight = new THREE.Raycaster();
-
-	var perspective;
-	var modelViewMatrix = new THREE.Matrix4();
-	var cameraNormalMatrix = new THREE.Matrix3();
-
-	var objects;
-	var lights = [];
-	var cache = {};
-
-	var animationFrameId = null;
-
 	this.domElement = canvas;
 
 	this.autoClear = true;
 
-	this.setClearColor = function ( color, alpha ) {
-
-		clearColor.set( color );
-
-	};
-
-	this.setPixelRatio = function () {};
-
-	this.setSize = function ( width, height ) {
-
-		canvas.width = width;
-		canvas.height = height;
-
-		canvasWidth = canvas.width;
-		canvasHeight = canvas.height;
-
-		canvasWidthHalf = Math.floor( canvasWidth / 2 );
-		canvasHeightHalf = Math.floor( canvasHeight / 2 );
-
-		context.fillStyle = 'white';
-
-	};
-
-	this.setSize( canvas.width, canvas.height );
-
-	this.clear = function () {
-
-	};
-
-	//
-
-	var spawnRay = ( function () {
-
-		var diffuseColor = new THREE.Color();
-		var specularColor = new THREE.Color();
-		var lightColor = new THREE.Color();
-		var schlick = new THREE.Color();
-
-		var lightContribution = new THREE.Color();
-
-		var eyeVector = new THREE.Vector3();
-		var lightVector = new THREE.Vector3();
-		var normalVector = new THREE.Vector3();
-		var halfVector = new THREE.Vector3();
-
-		var localPoint = new THREE.Vector3();
-		var reflectionVector = new THREE.Vector3();
-
-		var tmpVec = new THREE.Vector3();
-
-		var tmpColor = [];
-
-		for ( var i = 0; i < maxRecursionDepth; i ++ ) {
-
-			tmpColor[ i ] = new THREE.Color();
-
-		}
-
-		return function spawnRay( rayOrigin, rayDirection, outputColor, recursionDepth ) {
-
-			var ray = raycaster.ray;
-
-			ray.origin = rayOrigin;
-			ray.direction = rayDirection;
-
-			//
-
-			var rayLight = raycasterLight.ray;
-
-			//
-
-			outputColor.setRGB( 0, 0, 0 );
-
-			//
-
-			var intersections = raycaster.intersectObjects( objects, true );
-
-			// ray didn't find anything
-			// (here should come setting of background color?)
-
-			if ( intersections.length === 0 ) {
-
-				return;
-
-			}
-
-			// ray hit
-
-			var intersection = intersections[ 0 ];
-
-			var point = intersection.point;
-			var object = intersection.object;
-			var material = object.material;
-			var face = intersection.face;
-
-			var vertices = object.geometry.vertices;
-
-			//
-
-			var _object = cache[ object.id ];
-
-			localPoint.copy( point ).applyMatrix4( _object.inverseMatrix );
-			eyeVector.subVectors( raycaster.ray.origin, point ).normalize();
-
-			// resolve pixel diffuse color
-
-			if ( material instanceof THREE.MeshLambertMaterial ||
-				 material instanceof THREE.MeshPhongMaterial ||
-				 material instanceof THREE.MeshBasicMaterial ) {
-
-				diffuseColor.copyGammaToLinear( material.color );
-
-			} else {
-
-				diffuseColor.setRGB( 1, 1, 1 );
-
-			}
-
-			if ( material.vertexColors === THREE.FaceColors ) {
-
-				diffuseColor.multiply( face.color );
-
-			}
-
-			// compute light shading
-
-			rayLight.origin.copy( point );
-
-			if ( material instanceof THREE.MeshBasicMaterial ) {
-
-				for ( var i = 0, l = lights.length; i < l; i ++ ) {
-
-					var light = lights[ i ];
-
-					lightVector.setFromMatrixPosition( light.matrixWorld );
-					lightVector.sub( point );
-
-					rayLight.direction.copy( lightVector ).normalize();
-
-					var intersections = raycasterLight.intersectObjects( objects, true );
-
-					// point in shadow
-
-					if ( intersections.length > 0 ) continue;
-
-					// point visible
-
-					outputColor.add( diffuseColor );
-
-				}
-
-			} else if ( material instanceof THREE.MeshLambertMaterial ||
-						material instanceof THREE.MeshPhongMaterial ) {
-
-				var normalComputed = false;
-
-				for ( var i = 0, l = lights.length; i < l; i ++ ) {
-
-					var light = lights[ i ];
-
-					lightColor.copyGammaToLinear( light.color );
+	var workers = parameters.workers;
+	var blockSize = parameters.blockSize || 64;
+	var toRender = [], workerId = 0, sceneId = 0;
 
-					lightVector.setFromMatrixPosition( light.matrixWorld );
-					lightVector.sub( point );
+	console.log( '%cSpinning off ' + workers + ' Workers ', 'font-size: 20px; background: black; color: white; font-family: monospace;' );
 
-					rayLight.direction.copy( lightVector ).normalize();
+	this.setWorkers = function( w ) {
 
-					var intersections = raycasterLight.intersectObjects( objects, true );
+		workers = w || navigator.hardwareConcurrency || 4;
 
-					// point in shadow
+		while ( pool.length < workers ) {
+			var worker = new Worker( parameters.workerPath );
+			worker.id = workerId++;
 
-					if ( intersections.length > 0 ) continue;
+			worker.onmessage = function( e ) {
 
-					// point lit
+				var data = e.data;
 
-					if ( normalComputed === false ) {
+				if ( ! data ) return;
 
-						// the same normal can be reused for all lights
-						// (should be possible to cache even more)
+				if ( data.blockSize && sceneId == data.sceneId ) { // we match sceneId here to be sure
 
-						computePixelNormal( normalVector, localPoint, material.shading, face, vertices );
-						normalVector.applyMatrix3( _object.normalMatrix ).normalize();
+					var imagedata = new ImageData( new Uint8ClampedArray( data.data ), data.blockSize, data.blockSize );
+					context.putImageData( imagedata, data.blockX, data.blockY );
 
-						normalComputed = true;
+					// completed
 
-					}
-
-					// compute attenuation
-
-					var attenuation = 1.0;
+					console.log( 'Worker ' + this.id, data.time / 1000, ( Date.now() - reallyThen ) / 1000 + ' s' );
 
-					if ( light.physicalAttenuation === true ) {
+					if ( pool.length > workers ) {
 
-						attenuation = lightVector.length();
-						attenuation = 1.0 / ( attenuation * attenuation );
+						pool.splice( pool.indexOf( this ), 1 );
+						return this.terminate();
 
 					}
 
-					lightVector.normalize();
-
-					// compute diffuse
-
-					var dot = Math.max( normalVector.dot( lightVector ), 0 );
-					var diffuseIntensity = dot * light.intensity;
-
-					lightContribution.copy( diffuseColor );
-					lightContribution.multiply( lightColor );
-					lightContribution.multiplyScalar( diffuseIntensity * attenuation );
-
-					outputColor.add( lightContribution );
-
-					// compute specular
-
-					if ( material instanceof THREE.MeshPhongMaterial ) {
-
-						halfVector.addVectors( lightVector, eyeVector ).normalize();
-
-						var dotNormalHalf = Math.max( normalVector.dot( halfVector ), 0.0 );
-						var specularIntensity = Math.max( Math.pow( dotNormalHalf, material.shininess ), 0.0 ) * diffuseIntensity;
-
-						var specularNormalization = ( material.shininess + 2.0 ) / 8.0;
-
-						specularColor.copyGammaToLinear( material.specular );
-
-						var alpha = Math.pow( Math.max( 1.0 - lightVector.dot( halfVector ), 0.0 ), 5.0 );
-
-						schlick.r = specularColor.r + ( 1.0 - specularColor.r ) * alpha;
-						schlick.g = specularColor.g + ( 1.0 - specularColor.g ) * alpha;
-						schlick.b = specularColor.b + ( 1.0 - specularColor.b ) * alpha;
-
-						lightContribution.copy( schlick );
-
-						lightContribution.multiply( lightColor );
-						lightContribution.multiplyScalar( specularNormalization * specularIntensity * attenuation );
-						outputColor.add( lightContribution );
-
-					}
+					renderNext( this );
 
 				}
 
 			}
 
-			// reflection / refraction
-
-			var reflectivity = material.reflectivity;
-
-			if ( ( material.mirror || material.glass ) && reflectivity > 0 && recursionDepth < maxRecursionDepth ) {
-
-				if ( material.mirror ) {
-
-					reflectionVector.copy( rayDirection );
-					reflectionVector.reflect( normalVector );
-
-				} else if ( material.glass ) {
-
-					var eta = material.refractionRatio;
-
-					var dotNI = rayDirection.dot( normalVector );
-					var k = 1.0 - eta * eta * ( 1.0 - dotNI * dotNI );
-
-					if ( k < 0.0 ) {
-
-						reflectionVector.set( 0, 0, 0 );
-
-					} else {
+			worker.color = new THREE.Color().setHSL( Math.random() , 0.8, 0.8 ).getHexString();
+			pool.push( worker );
 
-						reflectionVector.copy( rayDirection );
-						reflectionVector.multiplyScalar( eta );
+			if ( renderering ) {
 
-						var alpha = eta * dotNI + Math.sqrt( k );
-						tmpVec.copy( normalVector );
-						tmpVec.multiplyScalar( alpha );
-						reflectionVector.sub( tmpVec );
+				updateSettings( worker );
 
-					}
-
-				}
-
-				var theta = Math.max( eyeVector.dot( normalVector ), 0.0 );
-				var rf0 = reflectivity;
-				var fresnel = rf0 + ( 1.0 - rf0 ) * Math.pow( ( 1.0 - theta ), 5.0 );
-
-				var weight = fresnel;
-
-				var zColor = tmpColor[ recursionDepth ];
-
-				spawnRay( point, reflectionVector, zColor, recursionDepth + 1 );
-
-				if ( material.specular !== undefined ) {
-
-					zColor.multiply( material.specular );
-
-				}
+				worker.postMessage( {
+					scene: sceneJSON,
+					camera: cameraJSON,
+					annex: materials,
+					sceneId: sceneId
+				} );
 
-				zColor.multiplyScalar( weight );
-				outputColor.multiplyScalar( 1 - weight );
-				outputColor.add( zColor );
+				renderNext( worker );
 
 			}
 
-		};
-
-	}() );
+		}
 
-	var computePixelNormal = ( function () {
+		if ( ! renderering ) {
 
-		var tmpVec1 = new THREE.Vector3();
-		var tmpVec2 = new THREE.Vector3();
-		var tmpVec3 = new THREE.Vector3();
+			while ( pool.length > workers ) {
 
-		return function computePixelNormal( outputVector, point, shading, face, vertices ) {
+				pool.pop().terminate();
 
-			var faceNormal = face.normal;
-			var vertexNormals = face.vertexNormals;
+			}
 
-			if ( shading === THREE.FlatShading ) {
+		}
 
-				outputVector.copy( faceNormal );
+	};
 
-			} else if ( shading === THREE.SmoothShading ) {
+	this.setWorkers( workers );
 
-				// compute barycentric coordinates
+	this.setClearColor = function ( color, alpha ) {
 
-				var vA = vertices[ face.a ];
-				var vB = vertices[ face.b ];
-				var vC = vertices[ face.c ];
+		clearColor.set( color );
 
-				tmpVec3.crossVectors( tmpVec1.subVectors( vB, vA ), tmpVec2.subVectors( vC, vA ) );
-				var areaABC = faceNormal.dot( tmpVec3 );
+	};
 
-				tmpVec3.crossVectors( tmpVec1.subVectors( vB, point ), tmpVec2.subVectors( vC, point ) );
-				var areaPBC = faceNormal.dot( tmpVec3 );
-				var a = areaPBC / areaABC;
+	this.setPixelRatio = function () {};
 
-				tmpVec3.crossVectors( tmpVec1.subVectors( vC, point ), tmpVec2.subVectors( vA, point ) );
-				var areaPCA = faceNormal.dot( tmpVec3 );
-				var b = areaPCA / areaABC;
+	this.setSize = function ( width, height ) {
 
-				var c = 1.0 - a - b;
+		canvas.width = width;
+		canvas.height = height;
 
-				// compute interpolated vertex normal
+		canvasWidth = canvas.width;
+		canvasHeight = canvas.height;
 
-				tmpVec1.copy( vertexNormals[ 0 ] );
-				tmpVec1.multiplyScalar( a );
+		canvasWidthHalf = Math.floor( canvasWidth / 2 );
+		canvasHeightHalf = Math.floor( canvasHeight / 2 );
 
-				tmpVec2.copy( vertexNormals[ 1 ] );
-				tmpVec2.multiplyScalar( b );
+		context.fillStyle = 'white';
 
-				tmpVec3.copy( vertexNormals[ 2 ] );
-				tmpVec3.multiplyScalar( c );
+		pool.forEach( updateSettings );
 
-				outputVector.addVectors( tmpVec1, tmpVec2 );
-				outputVector.add( tmpVec3 );
+	};
 
-			}
+	this.setSize( canvas.width, canvas.height );
 
-		};
+	this.clear = function () {
 
-	}() );
+	};
 
-	var renderBlock = ( function () {
+	//
 
-		var blockSize = 64;
+	var totalBlocks, xblocks, yblocks;
 
-		var canvasBlock = document.createElement( 'canvas' );
-		canvasBlock.width = blockSize;
-		canvasBlock.height = blockSize;
+	function updateSettings( worker ) {
 
-		var contextBlock = canvasBlock.getContext( '2d', {
+		worker.postMessage( {
 
-			alpha: parameters.alpha === true
+			init: [ canvasWidth, canvasHeight ],
+			worker: worker.id,
+			// workers: pool.length,
+			blockSize: blockSize
 
 		} );
 
-		var imagedata = contextBlock.getImageData( 0, 0, blockSize, blockSize );
-		var data = imagedata.data;
+	}
 
-		var pixelColor = new THREE.Color();
+	function renderNext( worker ) {
+		if ( ! toRender.length ) {
 
-		return function renderBlock( blockX, blockY ) {
+			renderering = false;
+			return scope.dispatchEvent( { type: "complete" } );
 
-			var index = 0;
-
-			for ( var y = 0; y < blockSize; y ++ ) {
-
-				for ( var x = 0; x < blockSize; x ++, index += 4 ) {
-
-					// spawn primary ray at pixel position
+		}
 
-					origin.copy( cameraPosition );
+		var current = toRender.pop();
 
-					direction.set( x + blockX - canvasWidthHalf, - ( y + blockY - canvasHeightHalf ), - perspective );
-					direction.applyMatrix3( cameraNormalMatrix ).normalize();
+		var blockX = ( current % xblocks ) * blockSize;
+		var blockY = ( current / xblocks | 0 ) * blockSize;
 
-					spawnRay( origin, direction, pixelColor, 0 );
+		worker.postMessage( {
+			render: true,
+			x: blockX,
+			y: blockY,
+			sceneId: sceneId
+		} );
 
-					// convert from linear to gamma
+		context.fillStyle = '#' + worker.color;
 
-					data[ index ]     = Math.sqrt( pixelColor.r ) * 255;
-					data[ index + 1 ] = Math.sqrt( pixelColor.g ) * 255;
-					data[ index + 2 ] = Math.sqrt( pixelColor.b ) * 255;
+		context.fillRect( blockX, blockY, blockSize, blockSize );
 
-				}
+	}
 
-			}
+	var materials = {};
 
-			context.putImageData( imagedata, blockX, blockY );
+	var sceneJSON, cameraJSON, reallyThen;
 
-			blockX += blockSize;
+	// additional properties that were not serialize automatically
 
-			if ( blockX >= canvasWidth ) {
+	var _annex = {
 
-				blockX = 0;
-				blockY += blockSize;
+		mirror: 1,
+		reflectivity: 1,
+		refractionRatio: 1,
+		glass: 1,
 
-				if ( blockY >= canvasHeight ) {
+	};
 
-					scope.dispatchEvent( { type: "complete" } );
-					return;
+	function serializeObject( o ) {
 
-				}
+		var mat = o.material;
 
-			}
+		if ( ! mat || mat.uuid in materials ) return;
 
-			context.fillRect( blockX, blockY, blockSize, blockSize );
+		var props = {};
+		for ( var m in _annex ) {
 
-			animationFrameId = requestAnimationFrame( function () {
+			if ( mat[ m ] !== undefined ) {
 
-				renderBlock( blockX, blockY );
+				props[ m ] = mat[ m ];
 
-			} );
+			}
 
-		};
+		}
 
-	}() );
+		materials[ mat.uuid ] = props;
+	}
 
 	this.render = function ( scene, camera ) {
 
-		if ( this.autoClear === true ) this.clear();
-
-		cancelAnimationFrame( animationFrameId );
+		renderering = true;
 
 		// update scene graph
 
@@ -495,49 +229,56 @@ THREE.RaytracingRenderer = function ( parameters ) {
 
 		if ( camera.parent === null ) camera.updateMatrixWorld();
 
-		camera.matrixWorldInverse.getInverse( camera.matrixWorld );
-		cameraPosition.setFromMatrixPosition( camera.matrixWorld );
 
-		//
+		sceneJSON = scene.toJSON();
+		cameraJSON = camera.toJSON();
+		++ sceneId;
 
-		cameraNormalMatrix.getNormalMatrix( camera.matrixWorld );
-		origin.copy( cameraPosition );
+		scene.traverse( serializeObject );
 
-		perspective = 0.5 / Math.tan( THREE.Math.degToRad( camera.fov * 0.5 ) ) * canvasHeight;
+		pool.forEach( function( worker ) {
 
-		objects = scene.children;
+			worker.postMessage( {
+				scene: sceneJSON,
+				camera: cameraJSON,
+				annex: materials,
+				sceneId: sceneId
+			} );
+		} );
 
-		// collect lights and set up object matrices
+		context.clearRect( 0, 0, canvasWidth, canvasHeight );
+		reallyThen = Date.now();
 
-		lights.length = 0;
+		xblocks = Math.ceil( canvasWidth / blockSize );
+		yblocks = Math.ceil( canvasHeight / blockSize );
+		totalBlocks = xblocks * yblocks;
 
-		scene.traverse( function ( object ) {
+		toRender = [];
 
-			if ( object instanceof THREE.Light ) {
+		for ( var i = 0; i < totalBlocks; i ++ ) {
 
-				lights.push( object );
+			toRender.push( i );
 
-			}
+		}
 
-			if ( cache[ object.id ] === undefined ) {
 
-				cache[ object.id ] = {
-					normalMatrix: new THREE.Matrix3(),
-					inverseMatrix: new THREE.Matrix4()
-				};
+		// Randomize painting :)
 
-			}
+		if ( scope.randomize ) {
 
-			modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+			for ( var i = 0; i < totalBlocks; i ++ ) {
 
-			var _object = cache[ object.id ];
+				var swap = Math.random()  * totalBlocks | 0;
+				var tmp = toRender[ swap ];
+				toRender[ swap ] = toRender[ i ];
+				toRender[ i ] = tmp;
 
-			_object.normalMatrix.getNormalMatrix( modelViewMatrix );
-			_object.inverseMatrix.getInverse( object.matrixWorld );
+			}
+
+		}
 
-		} );
 
-		renderBlock( 0, 0 );
+		pool.forEach( renderNext );
 
 	};
 

+ 2 - 3
examples/js/renderers/RaytracingWorker.js

@@ -1,4 +1,4 @@
-var workers, worker;
+var worker;
 var BLOCK = 128;
 var startX, startY, division, completed = 0;
 
@@ -19,7 +19,6 @@ self.onmessage = function( e ) {
 			height = data.init[ 1 ];
 
 		worker = data.worker;
-		workers = data.workers;
 		BLOCK = data.blockSize;
 
 		if ( ! renderer ) renderer = new THREE.RaytracingRendererWorker();
@@ -487,7 +486,7 @@ THREE.RaytracingRendererWorker = function ( parameters ) {
 				}
 
 			}
-			
+
 			// Use transferable objects! :)
 			self.postMessage( {
 				data: data.buffer,

+ 0 - 292
examples/js/renderers/RaytracingWorkerRenderer.js

@@ -1,292 +0,0 @@
-/**
- * The way to use RaytracingWorkerRenderer is similar to RaytracingRenderer
- * except that it is simply a coordinator for workers. The workers compute
- * the pixel values and this renderer simply paints it to the Canvas. As such,
- * it is simply a renderer.
- *
- * TODO
- * - serialize scene and hand it to workers
- * - pass worker path as option
- *
- * @author zz85 / http://github.com/zz85
- */
-
-THREE.RaytracingWorkerRenderer = function ( parameters ) {
-
-	console.log( 'THREE.RaytracingWorkerRenderer', THREE.REVISION );
-
-	parameters = parameters || {};
-
-	var scope = this;
-	var pool = [];
-	var renderering = false;
-
-	var canvas = document.createElement( 'canvas' );
-	var context = canvas.getContext( '2d', {
-		alpha: parameters.alpha === true
-	} );
-
-	var maxRecursionDepth = 3;
-
-	var canvasWidth, canvasHeight;
-	var canvasWidthHalf, canvasHeightHalf;
-
-	var clearColor = new THREE.Color( 0x000000 );
-
-	this.domElement = canvas;
-
-	this.autoClear = true;
-
-	var workers = parameters.workers;
-	var blockSize = parameters.blockSize || 64;
-	var toRender = [], workerId = 0, sceneId = 0;
-
-	console.log( '%cSpinning off ' + workers + ' Workers ', 'font-size: 20px; background: black; color: white; font-family: monospace;' );
-
-	this.setWorkers = function( w ) {
-
-		workers = w || navigator.hardwareConcurrency || 4;
-
-		while ( pool.length < workers ) {
-			var worker = new Worker( 'js/renderers/RaytracingWorker.js' );
-			worker.id = workerId++;
-
-			worker.onmessage = function( e ) {
-
-				var data = e.data;
-
-				if ( ! data ) return;
-
-				if ( data.blockSize && sceneId == data.sceneId ) { // we match sceneId here to be sure
-
-					var imagedata = new ImageData( new Uint8ClampedArray( data.data ), data.blockSize, data.blockSize );
-					context.putImageData( imagedata, data.blockX, data.blockY );
-
-					// completed
-
-					console.log( 'Worker ' + this.id, data.time / 1000, ( Date.now() - reallyThen ) / 1000 + ' s' );
-
-					if ( pool.length > workers ) {
-
-						pool.splice( pool.indexOf( this ), 1 );
-						return this.terminate();
-
-					}
-
-					renderNext( this );
-
-				}
-
-			}
-
-			worker.color = new THREE.Color().setHSL( Math.random() , 0.8, 0.8 ).getHexString();
-			pool.push( worker );
-
-			if ( renderering ) {
-
-				updateSettings( worker );
-
-				worker.postMessage( {
-					scene: sceneJSON,
-					camera: cameraJSON,
-					annex: materials,
-					sceneId: sceneId
-				} );
-
-				renderNext( worker );
-
-			}
-
-		}
-
-		if ( ! renderering ) {
-
-			while ( pool.length > workers ) {
-
-				pool.pop().terminate();
-
-			}
-
-		}
-
-	};
-
-	this.setWorkers( workers );
-
-	this.setClearColor = function ( color, alpha ) {
-
-		clearColor.set( color );
-
-	};
-
-	this.setPixelRatio = function () {};
-
-	this.setSize = function ( width, height ) {
-
-		canvas.width = width;
-		canvas.height = height;
-
-		canvasWidth = canvas.width;
-		canvasHeight = canvas.height;
-
-		canvasWidthHalf = Math.floor( canvasWidth / 2 );
-		canvasHeightHalf = Math.floor( canvasHeight / 2 );
-
-		context.fillStyle = 'white';
-
-		pool.forEach( updateSettings );
-
-	};
-
-	this.setSize( canvas.width, canvas.height );
-
-	this.clear = function () {
-
-	};
-
-	//
-
-	var totalBlocks, xblocks, yblocks;
-
-	function updateSettings( worker ) {
-
-		worker.postMessage( {
-
-			init: [ canvasWidth, canvasHeight ],
-			worker: worker.id,
-			// workers: pool.length,
-			blockSize: blockSize
-
-		} );
-
-	}
-
-	function renderNext( worker ) {
-		if ( ! toRender.length ) {
-
-			renderering = false;
-			return scope.dispatchEvent( { type: "complete" } );
-
-		}
-
-		var current = toRender.pop();
-
-		var blockX = ( current % xblocks ) * blockSize;
-		var blockY = ( current / xblocks | 0 ) * blockSize;
-
-		worker.postMessage( {
-			render: true,
-			x: blockX,
-			y: blockY,
-			sceneId: sceneId
-		} );
-
-		context.fillStyle = '#' + worker.color;
-
-		context.fillRect( blockX, blockY, blockSize, blockSize );
-
-	}
-
-	var materials = {};
-
-	var sceneJSON, cameraJSON, reallyThen;
-
-	// additional properties that were not serialize automatically
-
-	var _annex = {
-
-		mirror: 1,
-		reflectivity: 1,
-		refractionRatio: 1,
-		glass: 1,
-
-	};
-
-	function serializeObject( o ) {
-
-		var mat = o.material;
-
-		if ( ! mat || mat.uuid in materials ) return;
-
-		var props = {};
-		for ( var m in _annex ) {
-
-			if ( mat[ m ] !== undefined ) {
-
-				props[ m ] = mat[ m ];
-
-			}
-
-		}
-
-		materials[ mat.uuid ] = props;
-	}
-
-	this.render = function ( scene, camera ) {
-
-		renderering = true;
-
-		// update scene graph
-
-		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
-
-		// update camera matrices
-
-		if ( camera.parent === null ) camera.updateMatrixWorld();
-
-
-		sceneJSON = scene.toJSON();
-		cameraJSON = camera.toJSON();
-		++ sceneId;
-
-		scene.traverse( serializeObject );
-
-		pool.forEach( function( worker ) {
-
-			worker.postMessage( {
-				scene: sceneJSON,
-				camera: cameraJSON,
-				annex: materials,
-				sceneId: sceneId
-			} );
-		} );
-
-		context.clearRect( 0, 0, canvasWidth, canvasHeight );
-		reallyThen = Date.now();
-
-		xblocks = Math.ceil( canvasWidth / blockSize );
-		yblocks = Math.ceil( canvasHeight / blockSize );
-		totalBlocks = xblocks * yblocks;
-
-		toRender = [];
-
-		for ( var i = 0; i < totalBlocks; i ++ ) {
-
-			toRender.push( i );
-
-		}
-
-
-		// Randomize painting :)
-
-		if ( scope.randomize ) {
-
-			for ( var i = 0; i < totalBlocks; i ++ ) {
-
-				var swap = Math.random()  * totalBlocks | 0;
-				var tmp = toRender[ swap ];
-				toRender[ swap ] = toRender[ i ];
-				toRender[ i ] = tmp;
-
-			}
-
-		}
-
-
-		pool.forEach( renderNext );
-
-	};
-
-};
-
-THREE.EventDispatcher.prototype.apply( THREE.RaytracingWorkerRenderer.prototype );

+ 84 - 30
examples/raytracing_sandbox.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html lang="en">
 	<head>
-		<title>three.js - raytracing renderer</title>
+		<title>three.js - raytracing renderer with web workers</title>
 		<meta charset="utf-8">
 		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
 		<style>
@@ -14,36 +14,27 @@
 		</style>
 	</head>
 	<body>
-
 		<script src="../build/three.min.js"></script>
 		<script src="js/renderers/RaytracingRenderer.js"></script>
 
 		<script>
 
-			var container;
+			var hash = location.hash ? location.hash.substring( 1 ) : '3';
+
+			var WORKERS = + hash || navigator.hardwareConcurrency || 3;
+
+			var container, info;
 
 			var camera, controls, scene, renderer;
 
-			var torus, cube;
+			var torus, cube, objects = [];
 
 			init();
 			render();
 
-			function init() {
-
-				container = document.createElement( 'div' );
-				document.body.appendChild( container );
-
-				var info = document.createElement( 'div' );
-				info.style.position = 'absolute';
-				info.style.top = '10px';
-				info.style.width = '100%';
-				info.style.zIndex = '100';
-				info.style.textAlign = 'center';
-				info.innerHTML = '<a href="http://threejs.org" target="_blank">three.js<a/> - raytracing renderer';
-				container.appendChild( info );
+			function initScene( width, height ) {
 
-				camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera = new THREE.PerspectiveCamera( 60, width / height, 1, 1000 );
 				camera.position.z = 600;
 
 				scene = new THREE.Scene();
@@ -157,51 +148,54 @@
 
 				sphere = new THREE.Mesh( sphereGeometry, phongMaterial );
 				sphere.scale.multiplyScalar( 0.5 );
-				sphere.position.set( -50, -250+5, -50 );
+				sphere.position.set( - 50, - 250 + 5, - 50 );
 				scene.add( sphere );
+				objects.push ( sphere );
 
 				sphere2 = new THREE.Mesh( sphereGeometry, mirrorMaterialSmooth );
 				sphere2.scale.multiplyScalar( 0.5 );
-				sphere2.position.set( 175, -250+5, -150 );
+				sphere2.position.set( 175, - 250 + 5, - 150 );
 				scene.add( sphere2 );
+				objects.push ( sphere2 );
 
 				// Box
 
 				box = new THREE.Mesh( boxGeometry, mirrorMaterialFlat );
-				box.position.set( -175, -250+2.5, -150 );
+				box.position.set( - 175, - 250 + 2.5, - 150 );
 				box.rotation.y = 0.5;
 				scene.add( box );
+				objects.push ( box );
 
 				// Glass
 
 				glass = new THREE.Mesh( sphereGeometry, glassMaterialSmooth );
 				glass.scale.multiplyScalar( 0.5 );
-				glass.position.set( 75, -250+5, -75 );
+				glass.position.set( 75, - 250 + 5, - 75 );
 				glass.rotation.y = 0.5;
 				scene.add( glass );
 
 				// bottom
 
 				plane = new THREE.Mesh( planeGeometry, phongMaterialBoxBottom );
-				plane.position.set( 0, -300+2.5, -300 );
+				plane.position.set( 0, - 300 + 2.5, - 300 );
 				scene.add( plane );
 
 				// top
 
 				plane = new THREE.Mesh( planeGeometry, phongMaterialBox );
-				plane.position.set( 0, 300-2.5, -300 );
+				plane.position.set( 0, 300 - 2.5, - 300 );
 				scene.add( plane );
 
 				// back
 
 				plane = new THREE.Mesh( planeGeometry, phongMaterialBox );
 				plane.rotation.x = 1.57;
-				plane.position.set( 0, 0, -300 );
+				plane.position.set( 0, 0, - 300 );
 				scene.add( plane );
 
 				plane = new THREE.Mesh( planeGeometry, mirrorMaterialFlatDark );
 				plane.rotation.x = 1.57;
-				plane.position.set( 0, 0, -300+10 );
+				plane.position.set( 0, 0, - 300 + 10 );
 				plane.scale.multiplyScalar( 0.85 );
 				scene.add( plane );
 
@@ -209,14 +203,14 @@
 
 				plane = new THREE.Mesh( planeGeometry, phongMaterialBoxLeft );
 				plane.rotation.z = 1.57;
-				plane.position.set( -300, 0, -300 );
+				plane.position.set( - 300, 0, - 300 );
 				scene.add( plane );
 
 				// right
 
 				plane = new THREE.Mesh( planeGeometry, phongMaterialBoxRight );
 				plane.rotation.z = 1.57;
-				plane.position.set( 300, 0, -300 );
+				plane.position.set( 300, 0, - 300 );
 				scene.add( plane );
 
 				// light
@@ -224,7 +218,7 @@
 				var intensity = 70000;
 
 				var light = new THREE.PointLight( 0xffaa55, intensity );
-				light.position.set( -200, 100, 100 );
+				light.position.set( - 200, 100, 100 );
 				light.physicalAttenuation = true;
 				scene.add( light );
 
@@ -238,9 +232,33 @@
 				light.physicalAttenuation = true;
 				scene.add( light );
 
+			}
+
+			function init() {
+
+				container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				info = document.createElement( 'div' );
+				info.style.position = 'absolute';
+				info.style.top = '10px';
+				info.style.width = '100%';
+				info.style.zIndex = '100';
+				info.style.textAlign = 'center';
+				container.appendChild( info );
+
+				updateWorkers()
+
+				//
+
+				initScene( window.innerWidth, window.innerHeight );
+
 				//
 
-				renderer = new THREE.RaytracingRenderer();
+				renderer = new THREE.RaytracingRenderer( {
+					workers: WORKERS,
+					workerPath: 'js/renderers/RaytracingWorker.js'
+				} );
 				renderer.setClearColor( 0xf0f0f0 );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 
@@ -250,14 +268,50 @@
 
 				container.appendChild( renderer.domElement );
 
+
+				window.addEventListener( 'resize', function( e ) {
+
+					renderer.setSize( innerWidth, innerHeight );
+
+				} );
+
 			}
 
+			function updateWorkers( x ) {
+
+				if ( x ) {
+
+					WORKERS = Math.max( 1, WORKERS + x );
+					renderer.setWorkers( WORKERS );
+
+				}
+
+				info.innerHTML = '<a href="http://threejs.org" target="_blank">three.js<a/> - raytracing renderer (using '  + WORKERS + ' <button onclick="updateWorkers(-1)">-</button><button onclick="updateWorkers(1)">+</button> web workers)' +
+				'<br/><button onclick="rearrange()">Rearrange</button><button onclick="render()">Render</button>';
+
+
+			}
+
+			function rearrange() {
+
+				objects.forEach( function( o ) {
+
+					o.position.y += ( Math.random() - 0.5 ) * 100;
+					o.position.x += ( Math.random() - 0.5 ) * 400;
+					o.position.z += ( Math.random() - 0.5 ) * 400;
+
+				} );
+
+			}
+
+
 			function render() {
 
 				renderer.render( scene, camera );
 
 			}
 
+
 		</script>
 
 	</body>

+ 0 - 317
examples/raytracing_sandbox_workers.html

@@ -1,317 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-	<head>
-		<title>three.js - raytracing renderer with web workers</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 {
-				font-family: Monospace;
-				color: #ffffff;
-				margin: 0px;
-				padding: 0px;
-			}
-		</style>
-	</head>
-	<body>
-		<script src="../build/three.min.js"></script>
-		<script src="js/renderers/RaytracingWorkerRenderer.js"></script>
-
-		<script>
-
-			var hash = location.hash ? location.hash.substring( 1 ) : '3';
-
-			var WORKERS = + hash || navigator.hardwareConcurrency || 3;
-
-			var container, info;
-
-			var camera, controls, scene, renderer;
-
-			var torus, cube, objects = [];
-
-			init();
-			render();
-
-			function initScene( width, height ) {
-
-				camera = new THREE.PerspectiveCamera( 60, width / height, 1, 1000 );
-				camera.position.z = 600;
-
-				scene = new THREE.Scene();
-
-				// materials
-
-				var phongMaterial = new THREE.MeshPhongMaterial( {
-					color: 0xffffff,
-					specular: 0x222222,
-					shininess: 150,
-					vertexColors: THREE.NoColors,
-					shading: THREE.SmoothShading
-				} );
-
-				var phongMaterialBox = new THREE.MeshPhongMaterial( {
-					color: 0xffffff,
-					specular: 0x111111,
-					shininess: 100,
-					vertexColors: THREE.NoColors,
-					shading: THREE.FlatShading
-				} );
-
-				var phongMaterialBoxBottom = new THREE.MeshPhongMaterial( {
-					color: 0x666666,
-					specular: 0x111111,
-					shininess: 100,
-					vertexColors: THREE.NoColors,
-					shading: THREE.FlatShading
-				} );
-
-				var phongMaterialBoxLeft = new THREE.MeshPhongMaterial( {
-					color: 0x990000,
-					specular: 0x111111,
-					shininess: 100,
-					vertexColors: THREE.NoColors,
-					shading: THREE.FlatShading
-				} );
-
-				var phongMaterialBoxRight = new THREE.MeshPhongMaterial( {
-					color: 0x0066ff,
-					specular: 0x111111,
-					shininess: 100,
-					vertexColors: THREE.NoColors,
-					shading: THREE.FlatShading
-				} );
-
-				var mirrorMaterialFlat = new THREE.MeshPhongMaterial( {
-					color: 0x000000,
-					specular: 0xff8888,
-					shininess: 10000,
-					vertexColors: THREE.NoColors,
-					shading: THREE.FlatShading
-				} );
-				mirrorMaterialFlat.mirror = true;
-				mirrorMaterialFlat.reflectivity = 1;
-
-				var mirrorMaterialFlatDark = new THREE.MeshPhongMaterial( {
-					color: 0x000000,
-					specular: 0xaaaaaa,
-					shininess: 10000,
-					vertexColors: THREE.NoColors,
-					shading: THREE.FlatShading
-				} );
-				mirrorMaterialFlatDark.mirror = true;
-				mirrorMaterialFlatDark.reflectivity = 1;
-
-				var mirrorMaterialSmooth = new THREE.MeshPhongMaterial( {
-					color: 0xffaa00,
-					specular: 0x222222,
-					shininess: 10000,
-					vertexColors: THREE.NoColors,
-					shading: THREE.SmoothShading
-				} );
-				mirrorMaterialSmooth.mirror = true;
-				mirrorMaterialSmooth.reflectivity = 0.3;
-
-				var glassMaterialFlat = new THREE.MeshPhongMaterial( {
-					color: 0x000000,
-					specular: 0x00ff00,
-					shininess: 10000,
-					vertexColors: THREE.NoColors,
-					shading: THREE.FlatShading
-				} );
-				glassMaterialFlat.glass = true;
-				glassMaterialFlat.reflectivity = 0.5;
-
-				var glassMaterialSmooth = new THREE.MeshPhongMaterial( {
-					color: 0x000000,
-					specular: 0xffaa55,
-					shininess: 10000,
-					vertexColors: THREE.NoColors,
-					shading: THREE.SmoothShading
-				} );
-				glassMaterialSmooth.glass = true;
-				glassMaterialSmooth.reflectivity = 0.25;
-				glassMaterialSmooth.refractionRatio = 0.6;
-
-				// geometries
-
-				var torusGeometry = new THREE.TorusKnotGeometry( 150 );
-				var sphereGeometry = new THREE.SphereGeometry( 100, 16, 8 );
-				var planeGeometry = new THREE.BoxGeometry( 600, 5, 600 );
-				var boxGeometry = new THREE.BoxGeometry( 100, 100, 100 );
-
-				// TorusKnot
-
-				//torus = new THREE.Mesh( torusGeometry, phongMaterial );
-				//scene.add( torus );
-
-				// Sphere
-
-				sphere = new THREE.Mesh( sphereGeometry, phongMaterial );
-				sphere.scale.multiplyScalar( 0.5 );
-				sphere.position.set( - 50, - 250 + 5, - 50 );
-				scene.add( sphere );
-				objects.push ( sphere );
-
-				sphere2 = new THREE.Mesh( sphereGeometry, mirrorMaterialSmooth );
-				sphere2.scale.multiplyScalar( 0.5 );
-				sphere2.position.set( 175, - 250 + 5, - 150 );
-				scene.add( sphere2 );
-				objects.push ( sphere2 );
-
-				// Box
-
-				box = new THREE.Mesh( boxGeometry, mirrorMaterialFlat );
-				box.position.set( - 175, - 250 + 2.5, - 150 );
-				box.rotation.y = 0.5;
-				scene.add( box );
-				objects.push ( box );
-
-				// Glass
-
-				glass = new THREE.Mesh( sphereGeometry, glassMaterialSmooth );
-				glass.scale.multiplyScalar( 0.5 );
-				glass.position.set( 75, - 250 + 5, - 75 );
-				glass.rotation.y = 0.5;
-				scene.add( glass );
-
-				// bottom
-
-				plane = new THREE.Mesh( planeGeometry, phongMaterialBoxBottom );
-				plane.position.set( 0, - 300 + 2.5, - 300 );
-				scene.add( plane );
-
-				// top
-
-				plane = new THREE.Mesh( planeGeometry, phongMaterialBox );
-				plane.position.set( 0, 300 - 2.5, - 300 );
-				scene.add( plane );
-
-				// back
-
-				plane = new THREE.Mesh( planeGeometry, phongMaterialBox );
-				plane.rotation.x = 1.57;
-				plane.position.set( 0, 0, - 300 );
-				scene.add( plane );
-
-				plane = new THREE.Mesh( planeGeometry, mirrorMaterialFlatDark );
-				plane.rotation.x = 1.57;
-				plane.position.set( 0, 0, - 300 + 10 );
-				plane.scale.multiplyScalar( 0.85 );
-				scene.add( plane );
-
-				// left
-
-				plane = new THREE.Mesh( planeGeometry, phongMaterialBoxLeft );
-				plane.rotation.z = 1.57;
-				plane.position.set( - 300, 0, - 300 );
-				scene.add( plane );
-
-				// right
-
-				plane = new THREE.Mesh( planeGeometry, phongMaterialBoxRight );
-				plane.rotation.z = 1.57;
-				plane.position.set( 300, 0, - 300 );
-				scene.add( plane );
-
-				// light
-
-				var intensity = 70000;
-
-				var light = new THREE.PointLight( 0xffaa55, intensity );
-				light.position.set( - 200, 100, 100 );
-				light.physicalAttenuation = true;
-				scene.add( light );
-
-				var light = new THREE.PointLight( 0x55aaff, intensity );
-				light.position.set( 200, 100, 100 );
-				light.physicalAttenuation = true;
-				scene.add( light );
-
-				var light = new THREE.PointLight( 0xffffff, intensity * 1.5 );
-				light.position.set( 0, 0, 300 );
-				light.physicalAttenuation = true;
-				scene.add( light );
-
-			}
-
-			function init() {
-
-				container = document.createElement( 'div' );
-				document.body.appendChild( container );
-
-				info = document.createElement( 'div' );
-				info.style.position = 'absolute';
-				info.style.top = '10px';
-				info.style.width = '100%';
-				info.style.zIndex = '100';
-				info.style.textAlign = 'center';
-				container.appendChild( info );
-
-				updateWorkers()
-
-				//
-
-				initScene( window.innerWidth, window.innerHeight );
-
-				//
-
-				renderer = new THREE.RaytracingWorkerRenderer( {
-					workers: WORKERS
-				} );
-				renderer.setClearColor( 0xf0f0f0 );
-				renderer.setSize( window.innerWidth, window.innerHeight );
-
-				renderer.domElement.style.position = "absolute";
-				renderer.domElement.style.top = "0px";
-				renderer.domElement.style.left = "0px";
-
-				container.appendChild( renderer.domElement );
-
-
-				window.addEventListener( 'resize', function( e ) {
-
-					renderer.setSize( innerWidth, innerHeight );
-
-				} );
-
-			}
-
-			function updateWorkers( x ) {
-
-				if ( x ) {
-
-					WORKERS = Math.max( 1, WORKERS + x );
-					renderer.setWorkers( WORKERS );
-
-				}
-
-				info.innerHTML = '<a href="http://threejs.org" target="_blank">three.js<a/> - raytracing renderer (using '  + WORKERS + ' <button onclick="updateWorkers(-1)">-</button><button onclick="updateWorkers(1)">+</button> web workers)' +
-				'<br/><button onclick="rearrange()">Rearrange</button><button onclick="render()">Render</button>';
-
-
-			}
-
-			function rearrange() {
-
-				objects.forEach( function( o ) {
-
-					o.position.y += ( Math.random() - 0.5 ) * 100;
-					o.position.x += ( Math.random() - 0.5 ) * 400;
-					o.position.z += ( Math.random() - 0.5 ) * 400;
-
-				} );
-
-			}
-
-
-			function render() {
-
-				renderer.render( scene, camera );
-
-			}
-
-
-		</script>
-
-	</body>
-</html>