Bläddra i källkod

Merge pull request #16508 from Mugen87/dev32

JSM: Added module and TS file for RaytracingRenderer.
Michael Herzog 6 år sedan
förälder
incheckning
eed8f50f50

+ 1 - 0
docs/manual/en/introduction/Import-via-modules.html

@@ -152,6 +152,7 @@
 						<li>Projector</li>
 						<li>SoftwareRenderer</li>
 						<li>SVGRenderer</li>
+						<li>RaytracingRenderer</li>
 					</ul>
 				</li>
 				<li>shaders

+ 28 - 0
examples/jsm/renderers/RaytracingRenderer.d.ts

@@ -0,0 +1,28 @@
+import {
+  EventDispatcher,
+  Color,
+  Scene,
+  Camera
+} from '../../../src/Three';
+
+export interface RaytracingRendererParameters {
+  alpha?: boolean;
+  blockSize?: number;
+  randomize: boolean;
+  workerPath: string;
+  workers: number;
+}
+
+export class RaytracingRenderer extends EventDispatcher {
+  constructor(parameters: RaytracingRendererParameters);
+  domElement: HTMLElement;
+  autoClear: boolean;
+  randomize: boolean;
+
+  setWorkers(w: number): void;
+  setClearColor(color: Color, alpha: number): void;
+  setPixelRatio(): void;
+  setSize(width: number, height: number): void;
+  clear(): void;
+  render(scene: Scene, camera: Camera): void;
+}

+ 295 - 0
examples/jsm/renderers/RaytracingRenderer.js

@@ -0,0 +1,295 @@
+/**
+ * RaytracingRenderer renders by raytracing it's scene. However, it does not
+ * compute the pixels itself but it hands off and coordinates the tasks for workers.
+ * The workers compute the pixel values and this renderer simply paints it to the Canvas.
+ *
+ * @author zz85 / http://github.com/zz85
+ */
+
+import {
+	Color,
+	EventDispatcher,
+	REVISION
+} from "../../../build/three.module.js";
+
+var RaytracingRenderer = function ( parameters ) {
+
+	console.log( 'THREE.RaytracingRenderer', 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 canvasWidth, canvasHeight;
+
+	var clearColor = new Color( 0x000000 );
+
+	this.domElement = canvas;
+
+	this.autoClear = true;
+
+	var workers = parameters.workers;
+	var blockSize = parameters.blockSize || 64;
+	this.randomize = parameters.randomize;
+
+	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( parameters.workerPath );
+			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 Color().setHSL( Math.random(), 0.8, 0.8 ).getHexString();
+			pool.push( worker );
+
+			updateSettings( worker );
+
+			if ( renderering ) {
+
+				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;
+
+		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 );
+
+	};
+
+};
+
+Object.assign( RaytracingRenderer.prototype, EventDispatcher.prototype );
+
+export { RaytracingRenderer };

+ 1 - 0
utils/modularize.js

@@ -67,6 +67,7 @@ var files = [
 	{ path: 'renderers/Projector.js', dependencies: [], ignoreList: [] },
 	{ path: 'renderers/SoftwareRenderer.js', dependencies: [ { name: 'Projector', path: 'renderers/Projector.js' }, { name: 'RenderableFace', path: 'renderers/Projector.js' }, { name: 'RenderableLine', path: 'renderers/Projector.js' }, { name: 'RenderableSprite', path: 'renderers/Projector.js' } ], ignoreList: [] },
 	{ path: 'renderers/SVGRenderer.js', dependencies: [ { name: 'Projector', path: 'renderers/Projector.js' }, { name: 'RenderableFace', path: 'renderers/Projector.js' }, { name: 'RenderableLine', path: 'renderers/Projector.js' }, { name: 'RenderableSprite', path: 'renderers/Projector.js' } ], ignoreList: [] },
+	{ path: 'renderers/RaytracingRenderer.js', dependencies: [], ignoreList: [] },
 
 	{ path: 'utils/BufferGeometryUtils.js', dependencies: [], ignoreList: [] },
 	{ path: 'utils/GeometryUtils.js', dependencies: [], ignoreList: [] },