UNPKG

12.4 kBJavaScriptView Raw
1var BLOCK = 128;
2var startX, startY;
3
4var scene, camera, renderer, loader, sceneId;
5
6importScripts( '../../../build/three.js' );
7
8
9self.onmessage = function ( e ) {
10
11 var data = e.data;
12 if ( ! data ) return;
13
14 if ( data.init ) {
15
16 var
17 width = data.init[ 0 ],
18 height = data.init[ 1 ];
19
20 BLOCK = data.blockSize;
21
22 if ( ! renderer ) renderer = new THREE.RaytracingRendererWorker();
23 if ( ! loader ) loader = new THREE.ObjectLoader();
24
25 renderer.setSize( width, height );
26
27 // TODO fix passing maxRecursionDepth as parameter.
28 // if (data.maxRecursionDepth) maxRecursionDepth = data.maxRecursionDepth;
29
30 }
31
32 if ( data.scene ) {
33
34 scene = loader.parse( data.scene );
35 camera = loader.parse( data.camera );
36
37 var meta = data.annex;
38 scene.traverse( function ( o ) {
39
40 if ( o.isPointLight ) {
41
42 o.physicalAttenuation = true;
43
44 }
45
46 var mat = o.material;
47
48 if ( ! mat ) return;
49
50 var material = meta[ mat.uuid ];
51
52 for ( var m in material ) {
53
54 mat[ m ] = material[ m ];
55
56 }
57
58 } );
59
60 sceneId = data.sceneId;
61
62 }
63
64 if ( data.render && scene && camera ) {
65
66 startX = data.x;
67 startY = data.y;
68 renderer.render( scene, camera );
69
70 }
71
72};
73
74/**
75 * DOM-less version of Raytracing Renderer
76 * @author mrdoob / http://mrdoob.com/
77 * @author alteredq / http://alteredqualia.com/
78 * @author zz95 / http://github.com/zz85
79 */
80
81THREE.RaytracingRendererWorker = function () {
82
83 var maxRecursionDepth = 3;
84
85 var canvasWidth, canvasHeight;
86 var canvasWidthHalf, canvasHeightHalf;
87 var origin = new THREE.Vector3();
88 var direction = new THREE.Vector3();
89
90 var cameraPosition = new THREE.Vector3();
91
92 var raycaster = new THREE.Raycaster( origin, direction );
93 var ray = raycaster.ray;
94
95 var raycasterLight = new THREE.Raycaster();
96 var rayLight = raycasterLight.ray;
97
98 var perspective;
99 var cameraNormalMatrix = new THREE.Matrix3();
100
101 var objects;
102 var lights = [];
103 var cache = {};
104
105 this.setSize = function ( width, height ) {
106
107 canvasWidth = width;
108 canvasHeight = height;
109
110 canvasWidthHalf = Math.floor( canvasWidth / 2 );
111 canvasHeightHalf = Math.floor( canvasHeight / 2 );
112
113 };
114
115 //
116
117 var spawnRay = ( function () {
118
119 var diffuseColor = new THREE.Color();
120 var specularColor = new THREE.Color();
121 var lightColor = new THREE.Color();
122 var schlick = new THREE.Color();
123
124 var lightContribution = new THREE.Color();
125
126 var eyeVector = new THREE.Vector3();
127 var lightVector = new THREE.Vector3();
128 var normalVector = new THREE.Vector3();
129 var halfVector = new THREE.Vector3();
130
131 var localPoint = new THREE.Vector3();
132 var reflectionVector = new THREE.Vector3();
133
134 var tmpVec = new THREE.Vector3();
135
136 var tmpColor = [];
137
138 for ( var i = 0; i < maxRecursionDepth; i ++ ) {
139
140 tmpColor[ i ] = new THREE.Color();
141
142 }
143
144 return function spawnRay( rayOrigin, rayDirection, outputColor, recursionDepth ) {
145
146 outputColor.setRGB( 0, 0, 0 );
147
148 //
149
150 ray.origin = rayOrigin;
151 ray.direction = rayDirection;
152
153 var intersections = raycaster.intersectObjects( objects, true );
154
155 // ray didn't find anything
156 // (here should come setting of background color?)
157
158 if ( intersections.length === 0 ) return;
159
160 // ray hit
161
162 var intersection = intersections[ 0 ];
163
164 var point = intersection.point;
165 var object = intersection.object;
166 var material = object.material;
167 var face = intersection.face;
168
169 var geometry = object.geometry;
170
171 //
172
173 var _object = cache[ object.id ];
174
175 eyeVector.subVectors( ray.origin, point ).normalize();
176
177 // resolve pixel diffuse color
178
179 if ( material.isMeshLambertMaterial ||
180 material.isMeshPhongMaterial ||
181 material.isMeshBasicMaterial ) {
182
183 diffuseColor.copyGammaToLinear( material.color );
184
185 } else {
186
187 diffuseColor.setRGB( 1, 1, 1 );
188
189 }
190
191 if ( material.vertexColors === THREE.FaceColors ) {
192
193 diffuseColor.multiply( face.color );
194
195 }
196
197 // compute light shading
198
199 rayLight.origin.copy( point );
200
201 if ( material.isMeshBasicMaterial ) {
202
203 for ( var i = 0, l = lights.length; i < l; i ++ ) {
204
205 var light = lights[ i ];
206
207 lightVector.setFromMatrixPosition( light.matrixWorld );
208 lightVector.sub( point );
209
210 rayLight.direction.copy( lightVector ).normalize();
211
212 var intersections = raycasterLight.intersectObjects( objects, true );
213
214 // point in shadow
215
216 if ( intersections.length > 0 ) continue;
217
218 // point visible
219
220 outputColor.add( diffuseColor );
221
222 }
223
224 } else if ( material.isMeshLambertMaterial || material.isMeshPhongMaterial ) {
225
226 var normalComputed = false;
227
228 for ( var i = 0, l = lights.length; i < l; i ++ ) {
229
230 var light = lights[ i ];
231
232 lightVector.setFromMatrixPosition( light.matrixWorld );
233 lightVector.sub( point );
234
235 rayLight.direction.copy( lightVector ).normalize();
236
237 var intersections = raycasterLight.intersectObjects( objects, true );
238
239 // point in shadow
240
241 if ( intersections.length > 0 ) continue;
242
243 // point lit
244
245 if ( normalComputed === false ) {
246
247 // the same normal can be reused for all lights
248 // (should be possible to cache even more)
249
250 localPoint.copy( point ).applyMatrix4( _object.inverseMatrix );
251 computePixelNormal( normalVector, localPoint, material.flatShading, face, geometry );
252 normalVector.applyMatrix3( _object.normalMatrix ).normalize();
253
254 normalComputed = true;
255
256 }
257
258 lightColor.copyGammaToLinear( light.color );
259
260 // compute attenuation
261
262 var attenuation = 1.0;
263
264 if ( light.physicalAttenuation === true ) {
265
266 attenuation = lightVector.length();
267 attenuation = 1.0 / ( attenuation * attenuation );
268
269 }
270
271 lightVector.normalize();
272
273 // compute diffuse
274
275 var dot = Math.max( normalVector.dot( lightVector ), 0 );
276 var diffuseIntensity = dot * light.intensity;
277
278 lightContribution.copy( diffuseColor );
279 lightContribution.multiply( lightColor );
280 lightContribution.multiplyScalar( diffuseIntensity * attenuation );
281
282 outputColor.add( lightContribution );
283
284 // compute specular
285
286 if ( material.isMeshPhongMaterial ) {
287
288 halfVector.addVectors( lightVector, eyeVector ).normalize();
289
290 var dotNormalHalf = Math.max( normalVector.dot( halfVector ), 0.0 );
291 var specularIntensity = Math.max( Math.pow( dotNormalHalf, material.shininess ), 0.0 ) * diffuseIntensity;
292
293 var specularNormalization = ( material.shininess + 2.0 ) / 8.0;
294
295 specularColor.copyGammaToLinear( material.specular );
296
297 var alpha = Math.pow( Math.max( 1.0 - lightVector.dot( halfVector ), 0.0 ), 5.0 );
298
299 schlick.r = specularColor.r + ( 1.0 - specularColor.r ) * alpha;
300 schlick.g = specularColor.g + ( 1.0 - specularColor.g ) * alpha;
301 schlick.b = specularColor.b + ( 1.0 - specularColor.b ) * alpha;
302
303 lightContribution.copy( schlick );
304 lightContribution.multiply( lightColor );
305 lightContribution.multiplyScalar( specularNormalization * specularIntensity * attenuation );
306
307 outputColor.add( lightContribution );
308
309 }
310
311 }
312
313 }
314
315 // reflection / refraction
316
317 var reflectivity = material.reflectivity;
318
319 if ( ( material.mirror || material.glass ) && reflectivity > 0 && recursionDepth < maxRecursionDepth ) {
320
321 if ( material.mirror ) {
322
323 reflectionVector.copy( rayDirection );
324 reflectionVector.reflect( normalVector );
325
326 } else if ( material.glass ) {
327
328 var eta = material.refractionRatio;
329
330 var dotNI = rayDirection.dot( normalVector );
331 var k = 1.0 - eta * eta * ( 1.0 - dotNI * dotNI );
332
333 if ( k < 0.0 ) {
334
335 reflectionVector.set( 0, 0, 0 );
336
337 } else {
338
339 reflectionVector.copy( rayDirection );
340 reflectionVector.multiplyScalar( eta );
341
342 var alpha = eta * dotNI + Math.sqrt( k );
343 tmpVec.copy( normalVector );
344 tmpVec.multiplyScalar( alpha );
345 reflectionVector.sub( tmpVec );
346
347 }
348
349 }
350
351 var theta = Math.max( eyeVector.dot( normalVector ), 0.0 );
352 var rf0 = reflectivity;
353 var fresnel = rf0 + ( 1.0 - rf0 ) * Math.pow( ( 1.0 - theta ), 5.0 );
354
355 var weight = fresnel;
356
357 var zColor = tmpColor[ recursionDepth ];
358
359 spawnRay( point, reflectionVector, zColor, recursionDepth + 1 );
360
361 if ( material.specular !== undefined ) {
362
363 zColor.multiply( material.specular );
364
365 }
366
367 zColor.multiplyScalar( weight );
368 outputColor.multiplyScalar( 1 - weight );
369 outputColor.add( zColor );
370
371 }
372
373 };
374
375 }() );
376
377 var computePixelNormal = ( function () {
378
379 var vA = new THREE.Vector3();
380 var vB = new THREE.Vector3();
381 var vC = new THREE.Vector3();
382
383 var tmpVec1 = new THREE.Vector3();
384 var tmpVec2 = new THREE.Vector3();
385 var tmpVec3 = new THREE.Vector3();
386
387 return function computePixelNormal( outputVector, point, flatShading, face, geometry ) {
388
389 var faceNormal = face.normal;
390
391 if ( flatShading === true ) {
392
393 outputVector.copy( faceNormal );
394
395 } else {
396
397 var positions = geometry.attributes.position;
398 var normals = geometry.attributes.normal;
399
400 vA.fromBufferAttribute( positions, face.a );
401 vB.fromBufferAttribute( positions, face.b );
402 vC.fromBufferAttribute( positions, face.c );
403
404 // compute barycentric coordinates
405
406 tmpVec3.crossVectors( tmpVec1.subVectors( vB, vA ), tmpVec2.subVectors( vC, vA ) );
407 var areaABC = faceNormal.dot( tmpVec3 );
408
409 tmpVec3.crossVectors( tmpVec1.subVectors( vB, point ), tmpVec2.subVectors( vC, point ) );
410 var areaPBC = faceNormal.dot( tmpVec3 );
411 var a = areaPBC / areaABC;
412
413 tmpVec3.crossVectors( tmpVec1.subVectors( vC, point ), tmpVec2.subVectors( vA, point ) );
414 var areaPCA = faceNormal.dot( tmpVec3 );
415 var b = areaPCA / areaABC;
416
417 var c = 1.0 - a - b;
418
419 // compute interpolated vertex normal
420
421 tmpVec1.fromBufferAttribute( normals, face.a );
422 tmpVec2.fromBufferAttribute( normals, face.b );
423 tmpVec3.fromBufferAttribute( normals, face.c );
424
425 tmpVec1.multiplyScalar( a );
426 tmpVec2.multiplyScalar( b );
427 tmpVec3.multiplyScalar( c );
428
429 outputVector.addVectors( tmpVec1, tmpVec2 );
430 outputVector.add( tmpVec3 );
431
432 }
433
434 };
435
436 }() );
437
438 var renderBlock = ( function () {
439
440 var blockSize = BLOCK;
441
442 var data = new Uint8ClampedArray( blockSize * blockSize * 4 );
443
444 var pixelColor = new THREE.Color();
445
446 return function renderBlock( blockX, blockY ) {
447
448 var index = 0;
449
450 for ( var y = 0; y < blockSize; y ++ ) {
451
452 for ( var x = 0; x < blockSize; x ++, index += 4 ) {
453
454 // spawn primary ray at pixel position
455
456 origin.copy( cameraPosition );
457
458 direction.set( x + blockX - canvasWidthHalf, - ( y + blockY - canvasHeightHalf ), - perspective );
459 direction.applyMatrix3( cameraNormalMatrix ).normalize();
460
461 spawnRay( origin, direction, pixelColor, 0 );
462
463 // convert from linear to gamma
464
465 data[ index + 0 ] = Math.sqrt( pixelColor.r ) * 255;
466 data[ index + 1 ] = Math.sqrt( pixelColor.g ) * 255;
467 data[ index + 2 ] = Math.sqrt( pixelColor.b ) * 255;
468 data[ index + 3 ] = 255;
469
470 }
471
472 }
473
474 // Use transferable objects! :)
475 self.postMessage( {
476 data: data.buffer,
477 blockX: blockX,
478 blockY: blockY,
479 blockSize: blockSize,
480 sceneId: sceneId,
481 time: Date.now(), // time for this renderer
482 }, [ data.buffer ] );
483
484 data = new Uint8ClampedArray( blockSize * blockSize * 4 );
485
486 };
487
488 }() );
489
490 this.render = function ( scene, camera ) {
491
492 // update scene graph
493
494 if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
495
496 // update camera matrices
497
498 if ( camera.parent === null ) camera.updateMatrixWorld();
499
500 cameraPosition.setFromMatrixPosition( camera.matrixWorld );
501
502 //
503
504 cameraNormalMatrix.getNormalMatrix( camera.matrixWorld );
505
506 perspective = 0.5 / Math.tan( THREE.Math.degToRad( camera.fov * 0.5 ) ) * canvasHeight;
507
508 objects = scene.children;
509
510 // collect lights and set up object matrices
511
512 lights.length = 0;
513
514 scene.traverse( function ( object ) {
515
516 if ( object.isPointLight ) {
517
518 lights.push( object );
519
520 }
521
522 if ( cache[ object.id ] === undefined ) {
523
524 cache[ object.id ] = {
525 normalMatrix: new THREE.Matrix3(),
526 inverseMatrix: new THREE.Matrix4()
527 };
528
529 }
530
531 var _object = cache[ object.id ];
532
533 _object.normalMatrix.getNormalMatrix( object.matrixWorld );
534 _object.inverseMatrix.getInverse( object.matrixWorld );
535
536 } );
537
538 renderBlock( startX, startY );
539
540 };
541
542};
543
544Object.assign( THREE.RaytracingRendererWorker.prototype, THREE.EventDispatcher.prototype );