| |
| |
| |
| |
| |
| |
| |
|
|
| THREE.RaytracingRenderer = function ( parameters ) { |
|
|
| console.log( 'THREE.RaytracingRenderer', 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 canvasWidth, canvasHeight; |
|
|
| var clearColor = new THREE.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 ) { |
|
|
| var imagedata = new ImageData( new Uint8ClampedArray( data.data ), data.blockSize, data.blockSize ); |
| context.putImageData( imagedata, data.blockX, data.blockY ); |
|
|
| |
|
|
| 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 ); |
|
|
| 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 ) { |
|
|
| 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, |
| |
| 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; |
|
|
| |
|
|
| 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; |
|
|
| |
|
|
| if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); |
|
|
| |
|
|
| 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 ); |
|
|
| } |
|
|
|
|
| |
|
|
| 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( THREE.RaytracingRenderer.prototype, THREE.EventDispatcher.prototype ); |
|
|