1 | var BLOCK = 128;
|
2 | var startX, startY;
|
3 |
|
4 | var scene, camera, renderer, loader, sceneId;
|
5 |
|
6 | importScripts( '../../../build/three.js' );
|
7 |
|
8 |
|
9 | self.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 |
|
28 |
|
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 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 | THREE.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 |
|
156 |
|
157 |
|
158 | if ( intersections.length === 0 ) return;
|
159 |
|
160 |
|
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 |
|
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 |
|
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 |
|
215 |
|
216 | if ( intersections.length > 0 ) continue;
|
217 |
|
218 |
|
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 |
|
240 |
|
241 | if ( intersections.length > 0 ) continue;
|
242 |
|
243 |
|
244 |
|
245 | if ( normalComputed === false ) {
|
246 |
|
247 |
|
248 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
475 | self.postMessage( {
|
476 | data: data.buffer,
|
477 | blockX: blockX,
|
478 | blockY: blockY,
|
479 | blockSize: blockSize,
|
480 | sceneId: sceneId,
|
481 | time: Date.now(),
|
482 | }, [ data.buffer ] );
|
483 |
|
484 | data = new Uint8ClampedArray( blockSize * blockSize * 4 );
|
485 |
|
486 | };
|
487 |
|
488 | }() );
|
489 |
|
490 | this.render = function ( scene, camera ) {
|
491 |
|
492 |
|
493 |
|
494 | if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
|
495 |
|
496 |
|
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 |
|
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 |
|
544 | Object.assign( THREE.RaytracingRendererWorker.prototype, THREE.EventDispatcher.prototype );
|