1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | THREE.RaytracingRenderer = function ( parameters ) {
|
10 |
|
11 | parameters = parameters || {};
|
12 |
|
13 | var scope = this;
|
14 | var pool = [];
|
15 | var renderering = false;
|
16 |
|
17 | var canvas = document.createElement( 'canvas' );
|
18 | var context = canvas.getContext( '2d', {
|
19 | alpha: parameters.alpha === true
|
20 | } );
|
21 |
|
22 | var canvasWidth, canvasHeight;
|
23 |
|
24 | var clearColor = new THREE.Color( 0x000000 );
|
25 |
|
26 | this.domElement = canvas;
|
27 |
|
28 | this.autoClear = true;
|
29 |
|
30 | var workers = parameters.workers;
|
31 | var blockSize = parameters.blockSize || 64;
|
32 | this.randomize = parameters.randomize;
|
33 |
|
34 | var toRender = [], workerId = 0, sceneId = 0;
|
35 |
|
36 | console.log( '%cSpinning off ' + workers + ' Workers ', 'font-size: 20px; background: black; color: white; font-family: monospace;' );
|
37 |
|
38 | this.setWorkers = function ( w ) {
|
39 |
|
40 | workers = w || navigator.hardwareConcurrency || 4;
|
41 |
|
42 | while ( pool.length < workers ) {
|
43 |
|
44 | var worker = new Worker( parameters.workerPath );
|
45 | worker.id = workerId ++;
|
46 |
|
47 | worker.onmessage = function ( e ) {
|
48 |
|
49 | var data = e.data;
|
50 |
|
51 | if ( ! data ) return;
|
52 |
|
53 | if ( data.blockSize && sceneId == data.sceneId ) {
|
54 |
|
55 | var imagedata = new ImageData( new Uint8ClampedArray( data.data ), data.blockSize, data.blockSize );
|
56 | context.putImageData( imagedata, data.blockX, data.blockY );
|
57 |
|
58 |
|
59 |
|
60 | console.log( 'Worker ' + this.id, data.time / 1000, ( Date.now() - reallyThen ) / 1000 + ' s' );
|
61 |
|
62 | if ( pool.length > workers ) {
|
63 |
|
64 | pool.splice( pool.indexOf( this ), 1 );
|
65 | return this.terminate();
|
66 |
|
67 | }
|
68 |
|
69 | renderNext( this );
|
70 |
|
71 | }
|
72 |
|
73 | };
|
74 |
|
75 | worker.color = new THREE.Color().setHSL( Math.random(), 0.8, 0.8 ).getHexString();
|
76 | pool.push( worker );
|
77 |
|
78 | updateSettings( worker );
|
79 |
|
80 | if ( renderering ) {
|
81 |
|
82 | worker.postMessage( {
|
83 | scene: sceneJSON,
|
84 | camera: cameraJSON,
|
85 | annex: materials,
|
86 | sceneId: sceneId
|
87 | } );
|
88 |
|
89 | renderNext( worker );
|
90 |
|
91 | }
|
92 |
|
93 | }
|
94 |
|
95 | if ( ! renderering ) {
|
96 |
|
97 | while ( pool.length > workers ) {
|
98 |
|
99 | pool.pop().terminate();
|
100 |
|
101 | }
|
102 |
|
103 | }
|
104 |
|
105 | };
|
106 |
|
107 | this.setWorkers( workers );
|
108 |
|
109 | this.setClearColor = function ( color /*, alpha */ ) {
|
110 |
|
111 | clearColor.set( color );
|
112 |
|
113 | };
|
114 |
|
115 | this.setPixelRatio = function () {};
|
116 |
|
117 | this.setSize = function ( width, height ) {
|
118 |
|
119 | canvas.width = width;
|
120 | canvas.height = height;
|
121 |
|
122 | canvasWidth = canvas.width;
|
123 | canvasHeight = canvas.height;
|
124 |
|
125 | pool.forEach( updateSettings );
|
126 |
|
127 | };
|
128 |
|
129 | this.setSize( canvas.width, canvas.height );
|
130 |
|
131 | this.clear = function () {
|
132 |
|
133 | };
|
134 |
|
135 |
|
136 |
|
137 | var totalBlocks, xblocks, yblocks;
|
138 |
|
139 | function updateSettings( worker ) {
|
140 |
|
141 | worker.postMessage( {
|
142 |
|
143 | init: [ canvasWidth, canvasHeight ],
|
144 | worker: worker.id,
|
145 |
|
146 | blockSize: blockSize
|
147 |
|
148 | } );
|
149 |
|
150 | }
|
151 |
|
152 | function renderNext( worker ) {
|
153 |
|
154 | if ( ! toRender.length ) {
|
155 |
|
156 | renderering = false;
|
157 | return scope.dispatchEvent( { type: "complete" } );
|
158 |
|
159 | }
|
160 |
|
161 | var current = toRender.pop();
|
162 |
|
163 | var blockX = ( current % xblocks ) * blockSize;
|
164 | var blockY = ( current / xblocks | 0 ) * blockSize;
|
165 |
|
166 | worker.postMessage( {
|
167 | render: true,
|
168 | x: blockX,
|
169 | y: blockY,
|
170 | sceneId: sceneId
|
171 | } );
|
172 |
|
173 | context.fillStyle = '#' + worker.color;
|
174 | context.fillRect( blockX, blockY, blockSize, blockSize );
|
175 |
|
176 | }
|
177 |
|
178 | var materials = {};
|
179 |
|
180 | var sceneJSON, cameraJSON, reallyThen;
|
181 |
|
182 |
|
183 |
|
184 | var _annex = {
|
185 |
|
186 | mirror: 1,
|
187 | reflectivity: 1,
|
188 | refractionRatio: 1,
|
189 | glass: 1
|
190 |
|
191 | };
|
192 |
|
193 | function serializeObject( o ) {
|
194 |
|
195 | var mat = o.material;
|
196 |
|
197 | if ( ! mat || mat.uuid in materials ) return;
|
198 |
|
199 | var props = {};
|
200 | for ( var m in _annex ) {
|
201 |
|
202 | if ( mat[ m ] !== undefined ) {
|
203 |
|
204 | props[ m ] = mat[ m ];
|
205 |
|
206 | }
|
207 |
|
208 | }
|
209 |
|
210 | materials[ mat.uuid ] = props;
|
211 |
|
212 | }
|
213 |
|
214 | this.render = function ( scene, camera ) {
|
215 |
|
216 | renderering = true;
|
217 |
|
218 |
|
219 |
|
220 | if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
|
221 |
|
222 |
|
223 |
|
224 | if ( camera.parent === null ) camera.updateMatrixWorld();
|
225 |
|
226 |
|
227 | sceneJSON = scene.toJSON();
|
228 | cameraJSON = camera.toJSON();
|
229 | ++ sceneId;
|
230 |
|
231 | scene.traverse( serializeObject );
|
232 |
|
233 | pool.forEach( function ( worker ) {
|
234 |
|
235 | worker.postMessage( {
|
236 | scene: sceneJSON,
|
237 | camera: cameraJSON,
|
238 | annex: materials,
|
239 | sceneId: sceneId
|
240 | } );
|
241 |
|
242 | } );
|
243 |
|
244 | context.fillStyle = clearColor.getStyle();
|
245 | context.fillRect( 0, 0, canvasWidth, canvasHeight );
|
246 |
|
247 | reallyThen = Date.now();
|
248 |
|
249 | xblocks = Math.ceil( canvasWidth / blockSize );
|
250 | yblocks = Math.ceil( canvasHeight / blockSize );
|
251 | totalBlocks = xblocks * yblocks;
|
252 |
|
253 | toRender = [];
|
254 |
|
255 | for ( var i = 0; i < totalBlocks; i ++ ) {
|
256 |
|
257 | toRender.push( i );
|
258 |
|
259 | }
|
260 |
|
261 |
|
262 |
|
263 |
|
264 | if ( scope.randomize ) {
|
265 |
|
266 | for ( var i = 0; i < totalBlocks; i ++ ) {
|
267 |
|
268 | var swap = Math.random() * totalBlocks | 0;
|
269 | var tmp = toRender[ swap ];
|
270 | toRender[ swap ] = toRender[ i ];
|
271 | toRender[ i ] = tmp;
|
272 |
|
273 | }
|
274 |
|
275 | }
|
276 |
|
277 |
|
278 | pool.forEach( renderNext );
|
279 |
|
280 | };
|
281 |
|
282 | };
|
283 |
|
284 | Object.assign( THREE.RaytracingRenderer.prototype, THREE.EventDispatcher.prototype );
|