three-gpu-pathtracer 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/build/index.module.js +638 -222
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +634 -219
- package/build/index.umd.cjs.map +1 -1
- package/package.json +2 -2
- package/src/core/PathTracingRenderer.js +5 -0
- package/src/materials/AmbientOcclusionMaterial.js +2 -0
- package/src/materials/PhysicalPathTracingMaterial.js +45 -36
- package/src/shader/shaderEnvMapSampling.js +4 -5
- package/src/shader/shaderGGXFunctions.js +6 -6
- package/src/shader/shaderLightSampling.js +48 -50
- package/src/shader/shaderMaterialSampling.js +9 -15
- package/src/shader/shaderRandFunctions.js +57 -0
- package/src/shader/shaderSobolSampling.js +256 -0
- package/src/shader/shaderStructs.js +6 -2
- package/src/shader/shaderUtils.js +50 -92
- package/src/uniforms/LightsInfoUniformStruct.js +47 -2
- package/src/uniforms/MaterialsTexture.js +12 -6
- package/src/uniforms/utils.js +11 -2
- package/src/utils/SobolNumberMapGenerator.js +80 -0
package/build/index.umd.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('three'), require('three/examples/jsm/postprocessing/Pass.js'), require('three-mesh-bvh'), require('three/examples/jsm/utils/BufferGeometryUtils.js')) :
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports', 'three', 'three/examples/jsm/postprocessing/Pass.js', 'three-mesh-bvh', 'three/examples/jsm/utils/BufferGeometryUtils.js'], factory) :
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ThreePathTracer = global.ThreePathTracer || {}, global.THREE, global.THREE, global.MeshBVHLib, global.THREE));
|
|
5
|
-
})(this, (function (exports, three, Pass_js, threeMeshBvh, BufferGeometryUtils_js) { 'use strict';
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('three'), require('three/examples/jsm/postprocessing/Pass.js'), require('three/examples/jsm/postprocessing/Pass'), require('three-mesh-bvh'), require('three/examples/jsm/utils/BufferGeometryUtils.js')) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports', 'three', 'three/examples/jsm/postprocessing/Pass.js', 'three/examples/jsm/postprocessing/Pass', 'three-mesh-bvh', 'three/examples/jsm/utils/BufferGeometryUtils.js'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ThreePathTracer = global.ThreePathTracer || {}, global.THREE, global.THREE, global.THREE, global.MeshBVHLib, global.THREE));
|
|
5
|
+
})(this, (function (exports, three, Pass_js, Pass, threeMeshBvh, BufferGeometryUtils_js) { 'use strict';
|
|
6
6
|
|
|
7
7
|
class MaterialBase extends three.ShaderMaterial {
|
|
8
8
|
|
|
@@ -124,6 +124,339 @@
|
|
|
124
124
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
// References
|
|
128
|
+
// - https://jcgt.org/published/0009/04/01/
|
|
129
|
+
// - Code from https://www.shadertoy.com/view/WtGyDm
|
|
130
|
+
|
|
131
|
+
// functions to generate multi-dimensions variables of the same functions
|
|
132
|
+
// to support 1, 2, 3, and 4 dimensional sobol sampling.
|
|
133
|
+
function generateSobolFunctionVariants( dim = 1 ) {
|
|
134
|
+
|
|
135
|
+
let type = 'uint';
|
|
136
|
+
if ( dim > 1 ) {
|
|
137
|
+
|
|
138
|
+
type = 'uvec' + dim;
|
|
139
|
+
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return /* glsl */`
|
|
143
|
+
${ type } sobolReverseBits( ${ type } x ) {
|
|
144
|
+
|
|
145
|
+
x = ( ( ( x & 0xaaaaaaaau ) >> 1 ) | ( ( x & 0x55555555u ) << 1 ) );
|
|
146
|
+
x = ( ( ( x & 0xccccccccu ) >> 2 ) | ( ( x & 0x33333333u ) << 2 ) );
|
|
147
|
+
x = ( ( ( x & 0xf0f0f0f0u ) >> 4 ) | ( ( x & 0x0f0f0f0fu ) << 4 ) );
|
|
148
|
+
x = ( ( ( x & 0xff00ff00u ) >> 8 ) | ( ( x & 0x00ff00ffu ) << 8 ) );
|
|
149
|
+
return ( ( x >> 16 ) | ( x << 16 ) );
|
|
150
|
+
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
${ type } sobolHashCombine( uint seed, ${ type } v ) {
|
|
154
|
+
|
|
155
|
+
return seed ^ ( v + ${ type }( ( seed << 6 ) + ( seed >> 2 ) ) );
|
|
156
|
+
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
${ type } sobolLaineKarrasPermutation( ${ type } x, ${ type } seed ) {
|
|
160
|
+
|
|
161
|
+
x += seed;
|
|
162
|
+
x ^= x * 0x6c50b47cu;
|
|
163
|
+
x ^= x * 0xb82f1e52u;
|
|
164
|
+
x ^= x * 0xc7afe638u;
|
|
165
|
+
x ^= x * 0x8d22f6e6u;
|
|
166
|
+
return x;
|
|
167
|
+
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
${ type } nestedUniformScrambleBase2( ${ type } x, ${ type } seed ) {
|
|
171
|
+
|
|
172
|
+
x = sobolLaineKarrasPermutation( x, seed );
|
|
173
|
+
x = sobolReverseBits( x );
|
|
174
|
+
return x;
|
|
175
|
+
|
|
176
|
+
}
|
|
177
|
+
`;
|
|
178
|
+
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function generateSobolSampleFunctions( dim = 1 ) {
|
|
182
|
+
|
|
183
|
+
let utype = 'uint';
|
|
184
|
+
let vtype = 'float';
|
|
185
|
+
let num = '';
|
|
186
|
+
let components = '.r';
|
|
187
|
+
let combineValues = '1u';
|
|
188
|
+
if ( dim > 1 ) {
|
|
189
|
+
|
|
190
|
+
utype = 'uvec' + dim;
|
|
191
|
+
vtype = 'vec' + dim;
|
|
192
|
+
num = dim + '';
|
|
193
|
+
if ( dim === 2 ) {
|
|
194
|
+
|
|
195
|
+
components = '.rg';
|
|
196
|
+
combineValues = 'uvec2( 1u, 2u )';
|
|
197
|
+
|
|
198
|
+
} else if ( dim === 3 ) {
|
|
199
|
+
|
|
200
|
+
components = '.rgb';
|
|
201
|
+
combineValues = 'uvec3( 1u, 2u, 3u )';
|
|
202
|
+
|
|
203
|
+
} else {
|
|
204
|
+
|
|
205
|
+
components = '';
|
|
206
|
+
combineValues = 'uvec4( 1u, 2u, 3u, 4u )';
|
|
207
|
+
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return /* glsl */`
|
|
213
|
+
|
|
214
|
+
${ vtype } sobol${ num }( int effect ) {
|
|
215
|
+
|
|
216
|
+
uint seed = sobolGetSeed( sobolBounceIndex, uint( effect ) );
|
|
217
|
+
uint index = sobolPathIndex;
|
|
218
|
+
|
|
219
|
+
uint shuffle_seed = sobolHashCombine( seed, 0u );
|
|
220
|
+
uint shuffled_index = nestedUniformScrambleBase2( sobolReverseBits( index ), shuffle_seed );
|
|
221
|
+
${ vtype } sobol_pt = sobolGetTexturePoint( shuffled_index )${ components };
|
|
222
|
+
${ utype } result = ${ utype }( sobol_pt * 16777216.0 );
|
|
223
|
+
|
|
224
|
+
${ utype } seed2 = sobolHashCombine( seed, ${ combineValues } );
|
|
225
|
+
result = nestedUniformScrambleBase2( result, seed2 );
|
|
226
|
+
|
|
227
|
+
return SOBOL_FACTOR * ${ vtype }( result >> 8 );
|
|
228
|
+
|
|
229
|
+
}
|
|
230
|
+
`;
|
|
231
|
+
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const shaderSobolCommon = /* glsl */`
|
|
235
|
+
|
|
236
|
+
// Utils
|
|
237
|
+
const float SOBOL_FACTOR = 1.0 / 16777216.0;
|
|
238
|
+
const uint SOBOL_MAX_POINTS = 256u * 256u;
|
|
239
|
+
|
|
240
|
+
${ generateSobolFunctionVariants( 1 ) }
|
|
241
|
+
${ generateSobolFunctionVariants( 2 ) }
|
|
242
|
+
${ generateSobolFunctionVariants( 3 ) }
|
|
243
|
+
${ generateSobolFunctionVariants( 4 ) }
|
|
244
|
+
|
|
245
|
+
uint sobolHash( uint x ) {
|
|
246
|
+
|
|
247
|
+
// finalizer from murmurhash3
|
|
248
|
+
x ^= x >> 16;
|
|
249
|
+
x *= 0x85ebca6bu;
|
|
250
|
+
x ^= x >> 13;
|
|
251
|
+
x *= 0xc2b2ae35u;
|
|
252
|
+
x ^= x >> 16;
|
|
253
|
+
return x;
|
|
254
|
+
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
`;
|
|
258
|
+
|
|
259
|
+
const shaderSobolGeneration = /* glsl */`
|
|
260
|
+
|
|
261
|
+
const uint SOBOL_DIRECTIONS_1[ 32 ] = uint[ 32 ](
|
|
262
|
+
0x80000000u, 0xc0000000u, 0xa0000000u, 0xf0000000u,
|
|
263
|
+
0x88000000u, 0xcc000000u, 0xaa000000u, 0xff000000u,
|
|
264
|
+
0x80800000u, 0xc0c00000u, 0xa0a00000u, 0xf0f00000u,
|
|
265
|
+
0x88880000u, 0xcccc0000u, 0xaaaa0000u, 0xffff0000u,
|
|
266
|
+
0x80008000u, 0xc000c000u, 0xa000a000u, 0xf000f000u,
|
|
267
|
+
0x88008800u, 0xcc00cc00u, 0xaa00aa00u, 0xff00ff00u,
|
|
268
|
+
0x80808080u, 0xc0c0c0c0u, 0xa0a0a0a0u, 0xf0f0f0f0u,
|
|
269
|
+
0x88888888u, 0xccccccccu, 0xaaaaaaaau, 0xffffffffu
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
const uint SOBOL_DIRECTIONS_2[ 32 ] = uint[ 32 ](
|
|
273
|
+
0x80000000u, 0xc0000000u, 0x60000000u, 0x90000000u,
|
|
274
|
+
0xe8000000u, 0x5c000000u, 0x8e000000u, 0xc5000000u,
|
|
275
|
+
0x68800000u, 0x9cc00000u, 0xee600000u, 0x55900000u,
|
|
276
|
+
0x80680000u, 0xc09c0000u, 0x60ee0000u, 0x90550000u,
|
|
277
|
+
0xe8808000u, 0x5cc0c000u, 0x8e606000u, 0xc5909000u,
|
|
278
|
+
0x6868e800u, 0x9c9c5c00u, 0xeeee8e00u, 0x5555c500u,
|
|
279
|
+
0x8000e880u, 0xc0005cc0u, 0x60008e60u, 0x9000c590u,
|
|
280
|
+
0xe8006868u, 0x5c009c9cu, 0x8e00eeeeu, 0xc5005555u
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
const uint SOBOL_DIRECTIONS_3[ 32 ] = uint[ 32 ](
|
|
284
|
+
0x80000000u, 0xc0000000u, 0x20000000u, 0x50000000u,
|
|
285
|
+
0xf8000000u, 0x74000000u, 0xa2000000u, 0x93000000u,
|
|
286
|
+
0xd8800000u, 0x25400000u, 0x59e00000u, 0xe6d00000u,
|
|
287
|
+
0x78080000u, 0xb40c0000u, 0x82020000u, 0xc3050000u,
|
|
288
|
+
0x208f8000u, 0x51474000u, 0xfbea2000u, 0x75d93000u,
|
|
289
|
+
0xa0858800u, 0x914e5400u, 0xdbe79e00u, 0x25db6d00u,
|
|
290
|
+
0x58800080u, 0xe54000c0u, 0x79e00020u, 0xb6d00050u,
|
|
291
|
+
0x800800f8u, 0xc00c0074u, 0x200200a2u, 0x50050093u
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
const uint SOBOL_DIRECTIONS_4[ 32 ] = uint[ 32 ](
|
|
295
|
+
0x80000000u, 0x40000000u, 0x20000000u, 0xb0000000u,
|
|
296
|
+
0xf8000000u, 0xdc000000u, 0x7a000000u, 0x9d000000u,
|
|
297
|
+
0x5a800000u, 0x2fc00000u, 0xa1600000u, 0xf0b00000u,
|
|
298
|
+
0xda880000u, 0x6fc40000u, 0x81620000u, 0x40bb0000u,
|
|
299
|
+
0x22878000u, 0xb3c9c000u, 0xfb65a000u, 0xddb2d000u,
|
|
300
|
+
0x78022800u, 0x9c0b3c00u, 0x5a0fb600u, 0x2d0ddb00u,
|
|
301
|
+
0xa2878080u, 0xf3c9c040u, 0xdb65a020u, 0x6db2d0b0u,
|
|
302
|
+
0x800228f8u, 0x400b3cdcu, 0x200fb67au, 0xb00ddb9du
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
uint getMaskedSobol( uint index, uint directions[ 32 ] ) {
|
|
306
|
+
|
|
307
|
+
uint X = 0u;
|
|
308
|
+
for ( int bit = 0; bit < 32; bit ++ ) {
|
|
309
|
+
|
|
310
|
+
uint mask = ( index >> bit ) & 1u;
|
|
311
|
+
X ^= mask * directions[ bit ];
|
|
312
|
+
|
|
313
|
+
}
|
|
314
|
+
return X;
|
|
315
|
+
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
vec4 generateSobolPoint( uint index ) {
|
|
319
|
+
|
|
320
|
+
if ( index >= SOBOL_MAX_POINTS ) {
|
|
321
|
+
|
|
322
|
+
return vec4( 0.0 );
|
|
323
|
+
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// NOTEL this sobol "direction" is also available but we can't write out 5 components
|
|
327
|
+
// uint x = index & 0x00ffffffu;
|
|
328
|
+
uint x = sobolReverseBits( getMaskedSobol( index, SOBOL_DIRECTIONS_1 ) ) & 0x00ffffffu;
|
|
329
|
+
uint y = sobolReverseBits( getMaskedSobol( index, SOBOL_DIRECTIONS_2 ) ) & 0x00ffffffu;
|
|
330
|
+
uint z = sobolReverseBits( getMaskedSobol( index, SOBOL_DIRECTIONS_3 ) ) & 0x00ffffffu;
|
|
331
|
+
uint w = sobolReverseBits( getMaskedSobol( index, SOBOL_DIRECTIONS_4 ) ) & 0x00ffffffu;
|
|
332
|
+
|
|
333
|
+
return vec4( x, y, z, w ) * SOBOL_FACTOR;
|
|
334
|
+
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
`;
|
|
338
|
+
|
|
339
|
+
const shaderSobolSampling = /* glsl */`
|
|
340
|
+
|
|
341
|
+
// Seeds
|
|
342
|
+
uniform sampler2D sobolTexture;
|
|
343
|
+
uint sobolPixelIndex;
|
|
344
|
+
uint sobolPathIndex;
|
|
345
|
+
uint sobolBounceIndex;
|
|
346
|
+
|
|
347
|
+
uint sobolGetSeed( uint bounce, uint effect ) {
|
|
348
|
+
|
|
349
|
+
return sobolHash(
|
|
350
|
+
sobolHashCombine(
|
|
351
|
+
sobolHashCombine(
|
|
352
|
+
sobolHash( bounce ),
|
|
353
|
+
sobolPixelIndex
|
|
354
|
+
),
|
|
355
|
+
effect
|
|
356
|
+
)
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
vec4 sobolGetTexturePoint( uint index ) {
|
|
362
|
+
|
|
363
|
+
if ( index >= SOBOL_MAX_POINTS ) {
|
|
364
|
+
|
|
365
|
+
index = index % SOBOL_MAX_POINTS;
|
|
366
|
+
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
uvec2 dim = uvec2( textureSize( sobolTexture, 0 ).xy );
|
|
370
|
+
uint y = index / dim.x;
|
|
371
|
+
uint x = index - y * dim.x;
|
|
372
|
+
vec2 uv = vec2( x, y ) / vec2( dim );
|
|
373
|
+
return texture( sobolTexture, uv );
|
|
374
|
+
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
${ generateSobolSampleFunctions( 1 ) }
|
|
378
|
+
${ generateSobolSampleFunctions( 2 ) }
|
|
379
|
+
${ generateSobolSampleFunctions( 3 ) }
|
|
380
|
+
${ generateSobolSampleFunctions( 4 ) }
|
|
381
|
+
|
|
382
|
+
`;
|
|
383
|
+
|
|
384
|
+
class SobolNumbersMaterial extends MaterialBase {
|
|
385
|
+
|
|
386
|
+
constructor() {
|
|
387
|
+
|
|
388
|
+
super( {
|
|
389
|
+
|
|
390
|
+
blending: three.NoBlending,
|
|
391
|
+
|
|
392
|
+
uniforms: {
|
|
393
|
+
|
|
394
|
+
resolution: { value: new three.Vector2() },
|
|
395
|
+
|
|
396
|
+
},
|
|
397
|
+
|
|
398
|
+
vertexShader: /* glsl */`
|
|
399
|
+
|
|
400
|
+
varying vec2 vUv;
|
|
401
|
+
void main() {
|
|
402
|
+
|
|
403
|
+
vUv = uv;
|
|
404
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
|
405
|
+
|
|
406
|
+
}
|
|
407
|
+
`,
|
|
408
|
+
|
|
409
|
+
fragmentShader: /* glsl */`
|
|
410
|
+
|
|
411
|
+
${ shaderSobolCommon }
|
|
412
|
+
${ shaderSobolGeneration }
|
|
413
|
+
|
|
414
|
+
varying vec2 vUv;
|
|
415
|
+
uniform vec2 resolution;
|
|
416
|
+
void main() {
|
|
417
|
+
|
|
418
|
+
uint index = uint( gl_FragCoord.y ) * uint( resolution.x ) + uint( gl_FragCoord.x );
|
|
419
|
+
gl_FragColor = generateSobolPoint( index );
|
|
420
|
+
|
|
421
|
+
}
|
|
422
|
+
`,
|
|
423
|
+
|
|
424
|
+
} );
|
|
425
|
+
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
class SobolNumberMapGenerator {
|
|
431
|
+
|
|
432
|
+
generate( renderer, dimensions = 256 ) {
|
|
433
|
+
|
|
434
|
+
const target = new three.WebGLRenderTarget( dimensions, dimensions, {
|
|
435
|
+
|
|
436
|
+
type: three.FloatType,
|
|
437
|
+
format: three.RGBAFormat,
|
|
438
|
+
minFilter: three.NearestFilter,
|
|
439
|
+
magFilter: three.NearestFilter,
|
|
440
|
+
generateMipmaps: false,
|
|
441
|
+
|
|
442
|
+
} );
|
|
443
|
+
|
|
444
|
+
const ogTarget = renderer.getRenderTarget();
|
|
445
|
+
renderer.setRenderTarget( target );
|
|
446
|
+
|
|
447
|
+
const quad = new Pass.FullScreenQuad( new SobolNumbersMaterial() );
|
|
448
|
+
quad.material.resolution.set( dimensions, dimensions );
|
|
449
|
+
quad.render( renderer );
|
|
450
|
+
|
|
451
|
+
renderer.setRenderTarget( ogTarget );
|
|
452
|
+
quad.dispose();
|
|
453
|
+
|
|
454
|
+
return target;
|
|
455
|
+
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
}
|
|
459
|
+
|
|
127
460
|
function* renderTask() {
|
|
128
461
|
|
|
129
462
|
const {
|
|
@@ -132,6 +465,7 @@
|
|
|
132
465
|
_blendQuad,
|
|
133
466
|
_primaryTarget,
|
|
134
467
|
_blendTargets,
|
|
468
|
+
_sobolTarget,
|
|
135
469
|
alpha,
|
|
136
470
|
camera,
|
|
137
471
|
material,
|
|
@@ -158,6 +492,7 @@
|
|
|
158
492
|
const w = _primaryTarget.width;
|
|
159
493
|
const h = _primaryTarget.height;
|
|
160
494
|
material.resolution.set( w, h );
|
|
495
|
+
material.sobolTexture = _sobolTarget.texture;
|
|
161
496
|
material.seed ++;
|
|
162
497
|
|
|
163
498
|
const tilesX = this.tiles.x || 1;
|
|
@@ -306,6 +641,7 @@
|
|
|
306
641
|
this._task = null;
|
|
307
642
|
this._currentTile = 0;
|
|
308
643
|
|
|
644
|
+
this._sobolTarget = new SobolNumberMapGenerator().generate( renderer );
|
|
309
645
|
this._primaryTarget = new three.WebGLRenderTarget( 1, 1, {
|
|
310
646
|
format: three.RGBAFormat,
|
|
311
647
|
type: three.FloatType,
|
|
@@ -337,6 +673,7 @@
|
|
|
337
673
|
this._primaryTarget.dispose();
|
|
338
674
|
this._blendTargets[ 0 ].dispose();
|
|
339
675
|
this._blendTargets[ 1 ].dispose();
|
|
676
|
+
this._sobolTarget.dispose();
|
|
340
677
|
|
|
341
678
|
this._fsQuad.dispose();
|
|
342
679
|
this._blendQuad.dispose();
|
|
@@ -1202,6 +1539,14 @@
|
|
|
1202
1539
|
|
|
1203
1540
|
}
|
|
1204
1541
|
|
|
1542
|
+
// we must hash the texture to determine uniqueness using the encoding, as well, because the
|
|
1543
|
+
// when rendering each texture to the texture array they must have a consistent color space.
|
|
1544
|
+
function getTextureHash( t ) {
|
|
1545
|
+
|
|
1546
|
+
return `${ t.source.uuid }:${ t.encoding }`;
|
|
1547
|
+
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1205
1550
|
// reduce the set of textures to just those with a unique source while retaining
|
|
1206
1551
|
// the order of the textures.
|
|
1207
1552
|
function reduceTexturesToUniqueSources( textures ) {
|
|
@@ -1211,9 +1556,10 @@
|
|
|
1211
1556
|
for ( let i = 0, l = textures.length; i < l; i ++ ) {
|
|
1212
1557
|
|
|
1213
1558
|
const tex = textures[ i ];
|
|
1214
|
-
|
|
1559
|
+
const hash = getTextureHash( tex );
|
|
1560
|
+
if ( ! sourceSet.has( hash ) ) {
|
|
1215
1561
|
|
|
1216
|
-
sourceSet.add(
|
|
1562
|
+
sourceSet.add( hash );
|
|
1217
1563
|
result.push( tex );
|
|
1218
1564
|
|
|
1219
1565
|
}
|
|
@@ -1284,8 +1630,8 @@
|
|
|
1284
1630
|
|
|
1285
1631
|
if ( key in material && material[ key ] ) {
|
|
1286
1632
|
|
|
1287
|
-
const
|
|
1288
|
-
return
|
|
1633
|
+
const hash = getTextureHash( material[ key ] );
|
|
1634
|
+
return uniqueTextureLookup[ hash ];
|
|
1289
1635
|
|
|
1290
1636
|
} else {
|
|
1291
1637
|
|
|
@@ -1375,6 +1721,12 @@
|
|
|
1375
1721
|
|
|
1376
1722
|
// get the list of textures with unique sources
|
|
1377
1723
|
const uniqueTextures = reduceTexturesToUniqueSources( textures );
|
|
1724
|
+
const uniqueTextureLookup = {};
|
|
1725
|
+
for ( let i = 0, l = uniqueTextures.length; i < l; i ++ ) {
|
|
1726
|
+
|
|
1727
|
+
uniqueTextureLookup[ getTextureHash( uniqueTextures[ i ] ) ] = i;
|
|
1728
|
+
|
|
1729
|
+
}
|
|
1378
1730
|
|
|
1379
1731
|
if ( image.width !== dimension ) {
|
|
1380
1732
|
|
|
@@ -1406,9 +1758,9 @@
|
|
|
1406
1758
|
// sample 1
|
|
1407
1759
|
// metalness & roughness
|
|
1408
1760
|
floatArray[ index ++ ] = getField( m, 'metalness', 0.0 );
|
|
1409
|
-
floatArray[ index ++ ] =
|
|
1761
|
+
floatArray[ index ++ ] = getTexture( m, 'metalnessMap' );
|
|
1410
1762
|
floatArray[ index ++ ] = getField( m, 'roughness', 0.0 );
|
|
1411
|
-
floatArray[ index ++ ] =
|
|
1763
|
+
floatArray[ index ++ ] = getTexture( m, 'roughnessMap' );
|
|
1412
1764
|
|
|
1413
1765
|
// sample 2
|
|
1414
1766
|
// transmission & emissiveIntensity
|
|
@@ -1586,7 +1938,7 @@
|
|
|
1586
1938
|
// sample 14
|
|
1587
1939
|
index ++; // matte
|
|
1588
1940
|
index ++; // shadow
|
|
1589
|
-
floatArray[ index ++ ] = Number( m.vertexColors ); // vertexColors
|
|
1941
|
+
floatArray[ index ++ ] = Number( m.vertexColors ) | ( Number( m.flatShading ) << 1 ); // vertexColors & flatShading
|
|
1590
1942
|
floatArray[ index ++ ] = Number( m.transparent ); // transparent
|
|
1591
1943
|
|
|
1592
1944
|
// map transform 15
|
|
@@ -2025,6 +2377,8 @@
|
|
|
2025
2377
|
const RECT_AREA_LIGHT = 0;
|
|
2026
2378
|
const CIRC_AREA_LIGHT = 1;
|
|
2027
2379
|
const SPOT_LIGHT = 2;
|
|
2380
|
+
const DIR_LIGHT = 3;
|
|
2381
|
+
const POINT_LIGHT = 4;
|
|
2028
2382
|
class LightsInfoUniformStruct {
|
|
2029
2383
|
|
|
2030
2384
|
constructor() {
|
|
@@ -2083,8 +2437,24 @@
|
|
|
2083
2437
|
|
|
2084
2438
|
// type
|
|
2085
2439
|
let type = RECT_AREA_LIGHT;
|
|
2086
|
-
if ( l.isRectAreaLight && l.isCircular )
|
|
2087
|
-
|
|
2440
|
+
if ( l.isRectAreaLight && l.isCircular ) {
|
|
2441
|
+
|
|
2442
|
+
type = CIRC_AREA_LIGHT;
|
|
2443
|
+
|
|
2444
|
+
} else if ( l.isSpotLight ) {
|
|
2445
|
+
|
|
2446
|
+
type = SPOT_LIGHT;
|
|
2447
|
+
|
|
2448
|
+
} else if ( l.isDirectionalLight ) {
|
|
2449
|
+
|
|
2450
|
+
type = DIR_LIGHT;
|
|
2451
|
+
|
|
2452
|
+
} else if ( l.isPointLight ) {
|
|
2453
|
+
|
|
2454
|
+
type = POINT_LIGHT;
|
|
2455
|
+
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2088
2458
|
floatArray[ baseIndex + ( index ++ ) ] = type;
|
|
2089
2459
|
|
|
2090
2460
|
// sample 2
|
|
@@ -2171,6 +2541,33 @@
|
|
|
2171
2541
|
// iesProfile
|
|
2172
2542
|
floatArray[ baseIndex + ( index ++ ) ] = iesTextures.indexOf( l.iesTexture );
|
|
2173
2543
|
|
|
2544
|
+
} else if ( l.isPointLight ) {
|
|
2545
|
+
|
|
2546
|
+
const worldPosition = l.getWorldPosition( u );
|
|
2547
|
+
floatArray[ baseIndex + ( index ++ ) ] = worldPosition.x;
|
|
2548
|
+
floatArray[ baseIndex + ( index ++ ) ] = worldPosition.y;
|
|
2549
|
+
floatArray[ baseIndex + ( index ++ ) ] = worldPosition.z;
|
|
2550
|
+
index ++;
|
|
2551
|
+
|
|
2552
|
+
// sample 4
|
|
2553
|
+
index += 4;
|
|
2554
|
+
|
|
2555
|
+
// sample 5
|
|
2556
|
+
index += 2;
|
|
2557
|
+
|
|
2558
|
+
floatArray[ baseIndex + ( index ++ ) ] = l.decay;
|
|
2559
|
+
floatArray[ baseIndex + ( index ++ ) ] = l.distance;
|
|
2560
|
+
|
|
2561
|
+
} else if ( l.isDirectionalLight ) {
|
|
2562
|
+
|
|
2563
|
+
const worldPosition = l.getWorldPosition( u );
|
|
2564
|
+
const targetPosition = l.target.getWorldPosition( v );
|
|
2565
|
+
|
|
2566
|
+
target.subVectors( worldPosition, targetPosition ).normalize();
|
|
2567
|
+
floatArray[ baseIndex + ( index ++ ) ] = target.x;
|
|
2568
|
+
floatArray[ baseIndex + ( index ++ ) ] = target.y;
|
|
2569
|
+
floatArray[ baseIndex + ( index ++ ) ] = target.z;
|
|
2570
|
+
|
|
2174
2571
|
}
|
|
2175
2572
|
|
|
2176
2573
|
}
|
|
@@ -2719,74 +3116,7 @@
|
|
|
2719
3116
|
|
|
2720
3117
|
}
|
|
2721
3118
|
|
|
2722
|
-
|
|
2723
|
-
uvec4 s0;
|
|
2724
|
-
|
|
2725
|
-
void rng_initialize(vec2 p, int frame) {
|
|
2726
|
-
|
|
2727
|
-
// white noise seed
|
|
2728
|
-
s0 = uvec4( p, uint( frame ), uint( p.x ) + uint( p.y ) );
|
|
2729
|
-
|
|
2730
|
-
}
|
|
2731
|
-
|
|
2732
|
-
// https://www.pcg-random.org/
|
|
2733
|
-
void pcg4d( inout uvec4 v ) {
|
|
2734
|
-
|
|
2735
|
-
v = v * 1664525u + 1013904223u;
|
|
2736
|
-
v.x += v.y * v.w;
|
|
2737
|
-
v.y += v.z * v.x;
|
|
2738
|
-
v.z += v.x * v.y;
|
|
2739
|
-
v.w += v.y * v.z;
|
|
2740
|
-
v = v ^ ( v >> 16u );
|
|
2741
|
-
v.x += v.y*v.w;
|
|
2742
|
-
v.y += v.z*v.x;
|
|
2743
|
-
v.z += v.x*v.y;
|
|
2744
|
-
v.w += v.y*v.z;
|
|
2745
|
-
|
|
2746
|
-
}
|
|
2747
|
-
|
|
2748
|
-
// returns [ 0, 1 ]
|
|
2749
|
-
float rand() {
|
|
2750
|
-
|
|
2751
|
-
pcg4d(s0);
|
|
2752
|
-
return float( s0.x ) / float( 0xffffffffu );
|
|
2753
|
-
|
|
2754
|
-
}
|
|
2755
|
-
|
|
2756
|
-
vec2 rand2() {
|
|
2757
|
-
|
|
2758
|
-
pcg4d( s0 );
|
|
2759
|
-
return vec2( s0.xy ) / float(0xffffffffu);
|
|
2760
|
-
|
|
2761
|
-
}
|
|
2762
|
-
|
|
2763
|
-
vec3 rand3() {
|
|
2764
|
-
|
|
2765
|
-
pcg4d(s0);
|
|
2766
|
-
return vec3( s0.xyz ) / float( 0xffffffffu );
|
|
2767
|
-
|
|
2768
|
-
}
|
|
2769
|
-
|
|
2770
|
-
vec4 rand4() {
|
|
2771
|
-
|
|
2772
|
-
pcg4d(s0);
|
|
2773
|
-
return vec4(s0)/float(0xffffffffu);
|
|
2774
|
-
|
|
2775
|
-
}
|
|
2776
|
-
|
|
2777
|
-
// https://github.com/mrdoob/three.js/blob/dev/src/math/Vector3.js#L724
|
|
2778
|
-
vec3 randDirection() {
|
|
2779
|
-
|
|
2780
|
-
vec2 r = rand2();
|
|
2781
|
-
float u = ( r.x - 0.5 ) * 2.0;
|
|
2782
|
-
float t = r.y * PI * 2.0;
|
|
2783
|
-
float f = sqrt( 1.0 - u * u );
|
|
2784
|
-
|
|
2785
|
-
return vec3( f * cos( t ), f * sin( t ), u );
|
|
2786
|
-
|
|
2787
|
-
}
|
|
2788
|
-
|
|
2789
|
-
vec2 triangleSample( vec2 a, vec2 b, vec2 c ) {
|
|
3119
|
+
vec2 sampleTriangle( vec2 a, vec2 b, vec2 c, vec2 r ) {
|
|
2790
3120
|
|
|
2791
3121
|
// get the edges of the triangle and the diagonal across the
|
|
2792
3122
|
// center of the parallelogram
|
|
@@ -2794,8 +3124,7 @@
|
|
|
2794
3124
|
vec2 e2 = c - b;
|
|
2795
3125
|
vec2 diag = normalize( e1 + e2 );
|
|
2796
3126
|
|
|
2797
|
-
// pick
|
|
2798
|
-
vec2 r = rand2();
|
|
3127
|
+
// pick the point in the parallelogram
|
|
2799
3128
|
if ( r.x + r.y > 1.0 ) {
|
|
2800
3129
|
|
|
2801
3130
|
r = vec2( 1.0 ) - r;
|
|
@@ -2806,40 +3135,48 @@
|
|
|
2806
3135
|
|
|
2807
3136
|
}
|
|
2808
3137
|
|
|
2809
|
-
|
|
2810
|
-
vec2 sampleAperture( int blades ) {
|
|
3138
|
+
vec2 sampleCircle( vec2 uv ) {
|
|
2811
3139
|
|
|
2812
|
-
|
|
3140
|
+
float angle = 2.0 * PI * uv.x;
|
|
3141
|
+
float radius = sqrt( uv.y );
|
|
3142
|
+
return vec2( cos( angle ), sin( angle ) ) * radius;
|
|
2813
3143
|
|
|
2814
|
-
|
|
2815
|
-
float angle = 2.0 * PI * r.x;
|
|
2816
|
-
float radius = sqrt( rand() );
|
|
2817
|
-
return vec2( cos( angle ), sin( angle ) ) * radius;
|
|
3144
|
+
}
|
|
2818
3145
|
|
|
2819
|
-
|
|
3146
|
+
vec3 sampleSphere( vec2 uv ) {
|
|
2820
3147
|
|
|
2821
|
-
|
|
3148
|
+
float u = ( uv.x - 0.5 ) * 2.0;
|
|
3149
|
+
float t = uv.y * PI * 2.0;
|
|
3150
|
+
float f = sqrt( 1.0 - u * u );
|
|
2822
3151
|
|
|
2823
|
-
|
|
2824
|
-
float anglePerSegment = 2.0 * PI / float( blades );
|
|
2825
|
-
float segment = floor( float( blades ) * r.x );
|
|
3152
|
+
return vec3( f * cos( t ), f * sin( t ), u );
|
|
2826
3153
|
|
|
2827
|
-
|
|
2828
|
-
float angle2 = angle1 + anglePerSegment;
|
|
2829
|
-
vec2 a = vec2( sin( angle1 ), cos( angle1 ) );
|
|
2830
|
-
vec2 b = vec2( 0.0, 0.0 );
|
|
2831
|
-
vec2 c = vec2( sin( angle2 ), cos( angle2 ) );
|
|
3154
|
+
}
|
|
2832
3155
|
|
|
2833
|
-
|
|
3156
|
+
vec2 sampleRegularNGon( int sides, vec3 uvw ) {
|
|
2834
3157
|
|
|
2835
|
-
|
|
3158
|
+
sides = max( sides, 3 );
|
|
3159
|
+
|
|
3160
|
+
vec3 r = uvw;
|
|
3161
|
+
float anglePerSegment = 2.0 * PI / float( sides );
|
|
3162
|
+
float segment = floor( float( sides ) * r.x );
|
|
3163
|
+
|
|
3164
|
+
float angle1 = anglePerSegment * segment;
|
|
3165
|
+
float angle2 = angle1 + anglePerSegment;
|
|
3166
|
+
vec2 a = vec2( sin( angle1 ), cos( angle1 ) );
|
|
3167
|
+
vec2 b = vec2( 0.0, 0.0 );
|
|
3168
|
+
vec2 c = vec2( sin( angle2 ), cos( angle2 ) );
|
|
3169
|
+
|
|
3170
|
+
return sampleTriangle( a, b, c, r.yz );
|
|
2836
3171
|
|
|
2837
3172
|
}
|
|
2838
3173
|
|
|
2839
|
-
|
|
3174
|
+
// samples an aperture shape with the given number of sides. 0 means circle
|
|
3175
|
+
vec2 sampleAperture( int blades, vec3 uvw ) {
|
|
2840
3176
|
|
|
2841
|
-
|
|
2842
|
-
|
|
3177
|
+
return blades == 0 ?
|
|
3178
|
+
sampleCircle( uvw.xy ) :
|
|
3179
|
+
sampleRegularNGon( blades, uvw );
|
|
2843
3180
|
|
|
2844
3181
|
}
|
|
2845
3182
|
|
|
@@ -2920,6 +3257,17 @@
|
|
|
2920
3257
|
|
|
2921
3258
|
}
|
|
2922
3259
|
|
|
3260
|
+
vec2 rotateVector( vec2 v, float t ) {
|
|
3261
|
+
|
|
3262
|
+
float ac = cos( t );
|
|
3263
|
+
float as = sin( t );
|
|
3264
|
+
return vec2(
|
|
3265
|
+
v.x * ac - v.y * as,
|
|
3266
|
+
v.x * as + v.y * ac
|
|
3267
|
+
);
|
|
3268
|
+
|
|
3269
|
+
}
|
|
3270
|
+
|
|
2923
3271
|
// Finds the point where the ray intersects the plane defined by u and v and checks if this point
|
|
2924
3272
|
// falls in the bounds of the rectangle on that same plane.
|
|
2925
3273
|
// Plane intersection: https://lousodrome.net/blog/light/2020/07/03/intersection-of-a-ray-and-a-plane/
|
|
@@ -2988,6 +3336,13 @@
|
|
|
2988
3336
|
|
|
2989
3337
|
}
|
|
2990
3338
|
|
|
3339
|
+
// tentFilter from Peter Shirley's 'Realistic Ray Tracing (2nd Edition)' book, pg. 60
|
|
3340
|
+
// erichlof/THREE.js-PathTracing-Renderer/
|
|
3341
|
+
float tentFilter( float x ) {
|
|
3342
|
+
|
|
3343
|
+
return x < 0.5 ? sqrt( 2.0 * x ) - 1.0 : 1.0 - sqrt( 2.0 - ( 2.0 * x ) );
|
|
3344
|
+
|
|
3345
|
+
}
|
|
2991
3346
|
`;
|
|
2992
3347
|
|
|
2993
3348
|
class PMREMCopyMaterial extends MaterialBase {
|
|
@@ -3567,6 +3922,7 @@
|
|
|
3567
3922
|
int sheenRoughnessMap;
|
|
3568
3923
|
|
|
3569
3924
|
bool vertexColors;
|
|
3925
|
+
bool flatShading;
|
|
3570
3926
|
bool transparent;
|
|
3571
3927
|
|
|
3572
3928
|
mat3 mapTransform;
|
|
@@ -3679,7 +4035,8 @@
|
|
|
3679
4035
|
|
|
3680
4036
|
m.matte = bool( s14.r );
|
|
3681
4037
|
m.castShadow = ! bool( s14.g );
|
|
3682
|
-
m.vertexColors = bool( s14.b );
|
|
4038
|
+
m.vertexColors = bool( int( s14.b ) & 1 );
|
|
4039
|
+
m.flatShading = bool( int( s14.b ) & 2 );
|
|
3683
4040
|
m.transparent = bool( s14.a );
|
|
3684
4041
|
|
|
3685
4042
|
uint firstTextureTransformIdx = i + 15u;
|
|
@@ -3711,6 +4068,8 @@
|
|
|
3711
4068
|
#define RECT_AREA_LIGHT_TYPE 0
|
|
3712
4069
|
#define CIRC_AREA_LIGHT_TYPE 1
|
|
3713
4070
|
#define SPOT_LIGHT_TYPE 2
|
|
4071
|
+
#define DIR_LIGHT_TYPE 3
|
|
4072
|
+
#define POINT_LIGHT_TYPE 4
|
|
3714
4073
|
|
|
3715
4074
|
struct LightsInfo {
|
|
3716
4075
|
|
|
@@ -3762,7 +4121,7 @@
|
|
|
3762
4121
|
l.v = s3.rgb;
|
|
3763
4122
|
l.area = s3.a;
|
|
3764
4123
|
|
|
3765
|
-
if ( l.type == SPOT_LIGHT_TYPE ) {
|
|
4124
|
+
if ( l.type == SPOT_LIGHT_TYPE || l.type == POINT_LIGHT_TYPE ) {
|
|
3766
4125
|
|
|
3767
4126
|
vec4 s4 = texelFetch1D( tex, i + 4u );
|
|
3768
4127
|
vec4 s5 = texelFetch1D( tex, i + 5u );
|
|
@@ -3815,14 +4174,14 @@
|
|
|
3815
4174
|
|
|
3816
4175
|
// trowbridge-reitz === GGX === GTR
|
|
3817
4176
|
|
|
3818
|
-
vec3 ggxDirection( vec3 incidentDir,
|
|
4177
|
+
vec3 ggxDirection( vec3 incidentDir, vec2 roughness, vec2 uv ) {
|
|
3819
4178
|
|
|
3820
4179
|
// TODO: try GGXVNDF implementation from reference [2], here. Needs to update ggxDistribution
|
|
3821
4180
|
// function below, as well
|
|
3822
4181
|
|
|
3823
4182
|
// Implementation from reference [1]
|
|
3824
4183
|
// stretch view
|
|
3825
|
-
vec3 V = normalize( vec3(
|
|
4184
|
+
vec3 V = normalize( vec3( roughness * incidentDir.xy, incidentDir.z ) );
|
|
3826
4185
|
|
|
3827
4186
|
// orthonormal basis
|
|
3828
4187
|
vec3 T1 = ( V.z < 0.9999 ) ? normalize( cross( V, vec3( 0.0, 0.0, 1.0 ) ) ) : vec3( 1.0, 0.0, 0.0 );
|
|
@@ -3830,16 +4189,16 @@ vec3 ggxDirection( vec3 incidentDir, float roughnessX, float roughnessY, float r
|
|
|
3830
4189
|
|
|
3831
4190
|
// sample point with polar coordinates (r, phi)
|
|
3832
4191
|
float a = 1.0 / ( 1.0 + V.z );
|
|
3833
|
-
float r = sqrt(
|
|
3834
|
-
float phi = (
|
|
4192
|
+
float r = sqrt( uv.x );
|
|
4193
|
+
float phi = ( uv.y < a ) ? uv.y / a * PI : PI + ( uv.y - a ) / ( 1.0 - a ) * PI;
|
|
3835
4194
|
float P1 = r * cos( phi );
|
|
3836
|
-
float P2 = r * sin( phi ) * ( (
|
|
4195
|
+
float P2 = r * sin( phi ) * ( ( uv.y < a ) ? 1.0 : V.z );
|
|
3837
4196
|
|
|
3838
4197
|
// compute normal
|
|
3839
4198
|
vec3 N = P1 * T1 + P2 * T2 + V * sqrt( max( 0.0, 1.0 - P1 * P1 - P2 * P2 ) );
|
|
3840
4199
|
|
|
3841
4200
|
// unstretch
|
|
3842
|
-
N = normalize( vec3(
|
|
4201
|
+
N = normalize( vec3( roughness * N.xy, max( 0.0, N.z ) ) );
|
|
3843
4202
|
|
|
3844
4203
|
return N;
|
|
3845
4204
|
|
|
@@ -4227,7 +4586,7 @@ float diffuseEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRec surf, out vec3 color )
|
|
|
4227
4586
|
|
|
4228
4587
|
vec3 diffuseDirection( vec3 wo, SurfaceRec surf ) {
|
|
4229
4588
|
|
|
4230
|
-
vec3 lightDirection =
|
|
4589
|
+
vec3 lightDirection = sampleSphere( sobol2( 11 ) );
|
|
4231
4590
|
lightDirection.z += 1.0;
|
|
4232
4591
|
lightDirection = normalize( lightDirection );
|
|
4233
4592
|
|
|
@@ -4281,10 +4640,8 @@ vec3 specularDirection( vec3 wo, SurfaceRec surf ) {
|
|
|
4281
4640
|
float filteredRoughness = surf.filteredRoughness;
|
|
4282
4641
|
vec3 halfVector = ggxDirection(
|
|
4283
4642
|
wo,
|
|
4284
|
-
filteredRoughness,
|
|
4285
|
-
|
|
4286
|
-
rand(),
|
|
4287
|
-
rand()
|
|
4643
|
+
vec2( filteredRoughness ),
|
|
4644
|
+
sobol2( 12 )
|
|
4288
4645
|
);
|
|
4289
4646
|
|
|
4290
4647
|
// apply to new ray by reflecting off the new normal
|
|
@@ -4321,10 +4678,8 @@ vec3 transmissionDirection( vec3 wo, SurfaceRec surf ) {
|
|
|
4321
4678
|
// sample ggx vndf distribution which gives a new normal
|
|
4322
4679
|
vec3 halfVector = ggxDirection(
|
|
4323
4680
|
wo,
|
|
4324
|
-
filteredRoughness,
|
|
4325
|
-
|
|
4326
|
-
rand(),
|
|
4327
|
-
rand()
|
|
4681
|
+
vec2( filteredRoughness ),
|
|
4682
|
+
sobol2( 13 )
|
|
4328
4683
|
);
|
|
4329
4684
|
|
|
4330
4685
|
|
|
@@ -4364,7 +4719,7 @@ vec3 transmissionDirection( vec3 wo, SurfaceRec surf ) {
|
|
|
4364
4719
|
|
|
4365
4720
|
float roughness = surf.roughness;
|
|
4366
4721
|
float eta = surf.eta;
|
|
4367
|
-
vec3 halfVector = normalize( vec3( 0.0, 0.0, 1.0 ) +
|
|
4722
|
+
vec3 halfVector = normalize( vec3( 0.0, 0.0, 1.0 ) + sampleSphere( sobol2( 13 ) ) * roughness );
|
|
4368
4723
|
vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
|
|
4369
4724
|
|
|
4370
4725
|
if ( surf.thinFilm ) {
|
|
@@ -4412,10 +4767,8 @@ vec3 clearcoatDirection( vec3 wo, SurfaceRec surf ) {
|
|
|
4412
4767
|
float filteredClearcoatRoughness = surf.filteredClearcoatRoughness;
|
|
4413
4768
|
vec3 halfVector = ggxDirection(
|
|
4414
4769
|
wo,
|
|
4415
|
-
filteredClearcoatRoughness,
|
|
4416
|
-
|
|
4417
|
-
rand(),
|
|
4418
|
-
rand()
|
|
4770
|
+
vec2( filteredClearcoatRoughness ),
|
|
4771
|
+
sobol2( 14 )
|
|
4419
4772
|
);
|
|
4420
4773
|
|
|
4421
4774
|
// apply to new ray by reflecting off the new normal
|
|
@@ -4604,7 +4957,7 @@ SampleRec bsdfSample( vec3 wo, vec3 clearcoatWo, mat3 normalBasis, mat3 invBasis
|
|
|
4604
4957
|
vec3 wi;
|
|
4605
4958
|
vec3 clearcoatWi;
|
|
4606
4959
|
|
|
4607
|
-
float r =
|
|
4960
|
+
float r = sobol( 15 );
|
|
4608
4961
|
if ( r <= cdf[0] ) { // diffuse
|
|
4609
4962
|
|
|
4610
4963
|
wi = diffuseDirection( wo, surf );
|
|
@@ -4660,13 +5013,13 @@ float envMapDirectionPdf( vec3 direction ) {
|
|
|
4660
5013
|
|
|
4661
5014
|
}
|
|
4662
5015
|
|
|
4663
|
-
float
|
|
5016
|
+
float sampleEnvMap( EquirectHdrInfo info, vec3 direction, out vec3 color ) {
|
|
4664
5017
|
|
|
4665
5018
|
vec2 uv = equirectDirectionToUv( direction );
|
|
4666
5019
|
color = texture2D( info.map, uv ).rgb;
|
|
4667
5020
|
|
|
4668
5021
|
float totalSum = info.totalSumWhole + info.totalSumDecimal;
|
|
4669
|
-
float lum =
|
|
5022
|
+
float lum = luminance( color );
|
|
4670
5023
|
ivec2 resolution = textureSize( info.map, 0 );
|
|
4671
5024
|
float pdf = lum / totalSum;
|
|
4672
5025
|
|
|
@@ -4674,10 +5027,9 @@ float envMapSample( vec3 direction, EquirectHdrInfo info, out vec3 color ) {
|
|
|
4674
5027
|
|
|
4675
5028
|
}
|
|
4676
5029
|
|
|
4677
|
-
float
|
|
5030
|
+
float sampleEnvMapProbability( EquirectHdrInfo info, vec2 r, out vec3 color, out vec3 direction ) {
|
|
4678
5031
|
|
|
4679
5032
|
// sample env map cdf
|
|
4680
|
-
vec2 r = rand2();
|
|
4681
5033
|
float v = texture2D( info.marginalWeights, vec2( r.x, 0.0 ) ).x;
|
|
4682
5034
|
float u = texture2D( info.conditionalWeights, vec2( r.y, v ) ).x;
|
|
4683
5035
|
vec2 uv = vec2( u, v );
|
|
@@ -4687,7 +5039,7 @@ float randomEnvMapSample( EquirectHdrInfo info, out vec3 color, out vec3 directi
|
|
|
4687
5039
|
color = texture2D( info.map, uv ).rgb;
|
|
4688
5040
|
|
|
4689
5041
|
float totalSum = info.totalSumWhole + info.totalSumDecimal;
|
|
4690
|
-
float lum =
|
|
5042
|
+
float lum = luminance( color );
|
|
4691
5043
|
ivec2 resolution = textureSize( info.map, 0 );
|
|
4692
5044
|
float pdf = lum / totalSum;
|
|
4693
5045
|
|
|
@@ -4766,6 +5118,7 @@ LightSampleRec lightsClosestHit( sampler2D lights, uint lightCount, vec3 rayOrig
|
|
|
4766
5118
|
|
|
4767
5119
|
float dist;
|
|
4768
5120
|
|
|
5121
|
+
// MIS / light intersection is not supported for punctual lights.
|
|
4769
5122
|
if(
|
|
4770
5123
|
( light.type == RECT_AREA_LIGHT_TYPE && intersectsRectangle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) ) ||
|
|
4771
5124
|
( light.type == CIRC_AREA_LIGHT_TYPE && intersectsCircle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) )
|
|
@@ -4784,38 +5137,6 @@ LightSampleRec lightsClosestHit( sampler2D lights, uint lightCount, vec3 rayOrig
|
|
|
4784
5137
|
|
|
4785
5138
|
}
|
|
4786
5139
|
|
|
4787
|
-
} else if ( light.type == SPOT_LIGHT_TYPE ) {
|
|
4788
|
-
|
|
4789
|
-
// TODO: forward path tracing sampling needs to be made consistent with direct light sampling logic
|
|
4790
|
-
// float radius = light.radius;
|
|
4791
|
-
// vec3 lightNormal = normalize( cross( light.u, light.v ) );
|
|
4792
|
-
// float angle = acos( light.coneCos );
|
|
4793
|
-
// float angleTan = tan( angle );
|
|
4794
|
-
// float startDistance = radius / max( angleTan, EPSILON );
|
|
4795
|
-
|
|
4796
|
-
// u = light.u / radius;
|
|
4797
|
-
// v = light.v / radius;
|
|
4798
|
-
|
|
4799
|
-
// if (
|
|
4800
|
-
// intersectsCircle( light.position - normal * startDistance, normal, u, v, rayOrigin, rayDirection, dist ) &&
|
|
4801
|
-
// ( dist < lightSampleRec.dist || ! lightSampleRec.hit )
|
|
4802
|
-
// ) {
|
|
4803
|
-
|
|
4804
|
-
// float cosTheta = dot( rayDirection, normal );
|
|
4805
|
-
// float spotAttenuation = light.iesProfile != - 1 ?
|
|
4806
|
-
// getPhotometricAttenuation( iesProfiles, light.iesProfile, rayDirection, normal, u, v )
|
|
4807
|
-
// : getSpotAttenuation( light.coneCos, light.penumbraCos, cosTheta );
|
|
4808
|
-
|
|
4809
|
-
// float distanceAttenuation = getDistanceAttenuation( dist, light.distance, light.decay );
|
|
4810
|
-
|
|
4811
|
-
// lightSampleRec.hit = true;
|
|
4812
|
-
// lightSampleRec.dist = dist;
|
|
4813
|
-
// lightSampleRec.direction = rayDirection;
|
|
4814
|
-
// lightSampleRec.emission = light.color * light.intensity * distanceAttenuation * spotAttenuation;
|
|
4815
|
-
// lightSampleRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
|
|
4816
|
-
|
|
4817
|
-
// }
|
|
4818
|
-
|
|
4819
5140
|
}
|
|
4820
5141
|
|
|
4821
5142
|
}
|
|
@@ -4824,7 +5145,7 @@ LightSampleRec lightsClosestHit( sampler2D lights, uint lightCount, vec3 rayOrig
|
|
|
4824
5145
|
|
|
4825
5146
|
}
|
|
4826
5147
|
|
|
4827
|
-
LightSampleRec randomAreaLightSample( Light light, vec3 rayOrigin ) {
|
|
5148
|
+
LightSampleRec randomAreaLightSample( Light light, vec3 rayOrigin, vec2 ruv ) {
|
|
4828
5149
|
|
|
4829
5150
|
LightSampleRec lightSampleRec;
|
|
4830
5151
|
lightSampleRec.hit = true;
|
|
@@ -4836,13 +5157,13 @@ LightSampleRec randomAreaLightSample( Light light, vec3 rayOrigin ) {
|
|
|
4836
5157
|
if( light.type == RECT_AREA_LIGHT_TYPE ) {
|
|
4837
5158
|
|
|
4838
5159
|
// rectangular area light
|
|
4839
|
-
randomPos = light.position + light.u * (
|
|
5160
|
+
randomPos = light.position + light.u * ( ruv.x - 0.5 ) + light.v * ( ruv.y - 0.5 );
|
|
4840
5161
|
|
|
4841
|
-
} else if( light.type ==
|
|
5162
|
+
} else if( light.type == CIRC_AREA_LIGHT_TYPE ) {
|
|
4842
5163
|
|
|
4843
5164
|
// circular area light
|
|
4844
|
-
float r = 0.5 * sqrt(
|
|
4845
|
-
float theta =
|
|
5165
|
+
float r = 0.5 * sqrt( ruv.x );
|
|
5166
|
+
float theta = ruv.y * 2.0 * PI;
|
|
4846
5167
|
float x = r * cos( theta );
|
|
4847
5168
|
float y = r * sin( theta );
|
|
4848
5169
|
|
|
@@ -4864,10 +5185,10 @@ LightSampleRec randomAreaLightSample( Light light, vec3 rayOrigin ) {
|
|
|
4864
5185
|
|
|
4865
5186
|
}
|
|
4866
5187
|
|
|
4867
|
-
LightSampleRec randomSpotLightSample( Light light, sampler2DArray iesProfiles, vec3 rayOrigin ) {
|
|
5188
|
+
LightSampleRec randomSpotLightSample( Light light, sampler2DArray iesProfiles, vec3 rayOrigin, vec2 ruv ) {
|
|
4868
5189
|
|
|
4869
|
-
float radius = light.radius * sqrt(
|
|
4870
|
-
float theta =
|
|
5190
|
+
float radius = light.radius * sqrt( ruv.x );
|
|
5191
|
+
float theta = ruv.y * 2.0 * PI;
|
|
4871
5192
|
float x = radius * cos( theta );
|
|
4872
5193
|
float y = radius * sin( theta );
|
|
4873
5194
|
|
|
@@ -4888,8 +5209,8 @@ LightSampleRec randomSpotLightSample( Light light, sampler2DArray iesProfiles, v
|
|
|
4888
5209
|
float cosTheta = dot( direction, normal );
|
|
4889
5210
|
|
|
4890
5211
|
float spotAttenuation = light.iesProfile != - 1 ?
|
|
4891
|
-
|
|
4892
|
-
|
|
5212
|
+
getPhotometricAttenuation( iesProfiles, light.iesProfile, direction, normal, u, v ) :
|
|
5213
|
+
getSpotAttenuation( light.coneCos, light.penumbraCos, cosTheta );
|
|
4893
5214
|
|
|
4894
5215
|
float distanceAttenuation = getDistanceAttenuation( dist, light.distance, light.decay );
|
|
4895
5216
|
LightSampleRec lightSampleRec;
|
|
@@ -4898,30 +5219,59 @@ LightSampleRec randomSpotLightSample( Light light, sampler2DArray iesProfiles, v
|
|
|
4898
5219
|
lightSampleRec.dist = dist;
|
|
4899
5220
|
lightSampleRec.direction = direction;
|
|
4900
5221
|
lightSampleRec.emission = light.color * light.intensity * distanceAttenuation * spotAttenuation;
|
|
4901
|
-
|
|
4902
|
-
// TODO: this makes the result consistent between MIS and non MIS paths but at radius 0 the pdf is infinite
|
|
4903
|
-
// and the intensity of the light is not correct
|
|
4904
5222
|
lightSampleRec.pdf = 1.0;
|
|
4905
|
-
// lightSampleRec.pdf = lightDistSq / ( light.area * cosTheta );
|
|
4906
5223
|
|
|
4907
5224
|
return lightSampleRec;
|
|
4908
5225
|
|
|
4909
5226
|
}
|
|
4910
5227
|
|
|
4911
|
-
LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles, uint lightCount, vec3 rayOrigin ) {
|
|
5228
|
+
LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles, uint lightCount, vec3 rayOrigin, vec3 ruv ) {
|
|
4912
5229
|
|
|
4913
5230
|
// pick a random light
|
|
4914
|
-
uint l = uint(
|
|
5231
|
+
uint l = uint( ruv.x * float( lightCount ) );
|
|
4915
5232
|
Light light = readLightInfo( lights, l );
|
|
4916
5233
|
|
|
4917
5234
|
if ( light.type == SPOT_LIGHT_TYPE ) {
|
|
4918
5235
|
|
|
4919
|
-
return randomSpotLightSample( light, iesProfiles, rayOrigin );
|
|
5236
|
+
return randomSpotLightSample( light, iesProfiles, rayOrigin, ruv.yz );
|
|
5237
|
+
|
|
5238
|
+
} else if ( light.type == POINT_LIGHT_TYPE ) {
|
|
5239
|
+
|
|
5240
|
+
vec3 lightRay = light.u - rayOrigin;
|
|
5241
|
+
float lightDist = length( lightRay );
|
|
5242
|
+
float cutoffDistance = light.distance;
|
|
5243
|
+
float distanceFalloff = 1.0 / max( pow( lightDist, light.decay ), 0.01 );
|
|
5244
|
+
if ( cutoffDistance > 0.0 ) {
|
|
5245
|
+
|
|
5246
|
+
distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDist / cutoffDistance ) ) );
|
|
5247
|
+
|
|
5248
|
+
}
|
|
5249
|
+
|
|
5250
|
+
LightSampleRec rec;
|
|
5251
|
+
rec.hit = true;
|
|
5252
|
+
rec.direction = normalize( lightRay );
|
|
5253
|
+
rec.dist = length( lightRay );
|
|
5254
|
+
rec.pdf = 1.0;
|
|
5255
|
+
rec.emission = light.color * light.intensity * distanceFalloff;
|
|
5256
|
+
rec.type = light.type;
|
|
5257
|
+
return rec;
|
|
5258
|
+
|
|
5259
|
+
} else if ( light.type == DIR_LIGHT_TYPE ) {
|
|
5260
|
+
|
|
5261
|
+
LightSampleRec rec;
|
|
5262
|
+
rec.hit = true;
|
|
5263
|
+
rec.dist = 1e10;
|
|
5264
|
+
rec.direction = light.u;
|
|
5265
|
+
rec.pdf = 1.0;
|
|
5266
|
+
rec.emission = light.color * light.intensity;
|
|
5267
|
+
rec.type = light.type;
|
|
5268
|
+
|
|
5269
|
+
return rec;
|
|
4920
5270
|
|
|
4921
5271
|
} else {
|
|
4922
5272
|
|
|
4923
5273
|
// sample the light
|
|
4924
|
-
return randomAreaLightSample( light, rayOrigin );
|
|
5274
|
+
return randomAreaLightSample( light, rayOrigin, ruv.yz );
|
|
4925
5275
|
|
|
4926
5276
|
}
|
|
4927
5277
|
|
|
@@ -4952,6 +5302,64 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
4952
5302
|
|
|
4953
5303
|
}
|
|
4954
5304
|
|
|
5305
|
+
`;
|
|
5306
|
+
|
|
5307
|
+
const shaderRandFunctions = /* glsl */`
|
|
5308
|
+
|
|
5309
|
+
// https://www.shadertoy.com/view/wltcRS
|
|
5310
|
+
uvec4 WHITE_NOISE_SEED;
|
|
5311
|
+
|
|
5312
|
+
void rng_initialize( vec2 p, int frame ) {
|
|
5313
|
+
|
|
5314
|
+
// white noise seed
|
|
5315
|
+
WHITE_NOISE_SEED = uvec4( p, uint( frame ), uint( p.x ) + uint( p.y ) );
|
|
5316
|
+
|
|
5317
|
+
}
|
|
5318
|
+
|
|
5319
|
+
// https://www.pcg-random.org/
|
|
5320
|
+
void pcg4d( inout uvec4 v ) {
|
|
5321
|
+
|
|
5322
|
+
v = v * 1664525u + 1013904223u;
|
|
5323
|
+
v.x += v.y * v.w;
|
|
5324
|
+
v.y += v.z * v.x;
|
|
5325
|
+
v.z += v.x * v.y;
|
|
5326
|
+
v.w += v.y * v.z;
|
|
5327
|
+
v = v ^ ( v >> 16u );
|
|
5328
|
+
v.x += v.y*v.w;
|
|
5329
|
+
v.y += v.z*v.x;
|
|
5330
|
+
v.z += v.x*v.y;
|
|
5331
|
+
v.w += v.y*v.z;
|
|
5332
|
+
|
|
5333
|
+
}
|
|
5334
|
+
|
|
5335
|
+
// returns [ 0, 1 ]
|
|
5336
|
+
float rand() {
|
|
5337
|
+
|
|
5338
|
+
pcg4d( WHITE_NOISE_SEED );
|
|
5339
|
+
return float( WHITE_NOISE_SEED.x ) / float( 0xffffffffu );
|
|
5340
|
+
|
|
5341
|
+
}
|
|
5342
|
+
|
|
5343
|
+
vec2 rand2() {
|
|
5344
|
+
|
|
5345
|
+
pcg4d( WHITE_NOISE_SEED );
|
|
5346
|
+
return vec2( WHITE_NOISE_SEED.xy ) / float(0xffffffffu);
|
|
5347
|
+
|
|
5348
|
+
}
|
|
5349
|
+
|
|
5350
|
+
vec3 rand3() {
|
|
5351
|
+
|
|
5352
|
+
pcg4d( WHITE_NOISE_SEED );
|
|
5353
|
+
return vec3( WHITE_NOISE_SEED.xyz ) / float( 0xffffffffu );
|
|
5354
|
+
|
|
5355
|
+
}
|
|
5356
|
+
|
|
5357
|
+
vec4 rand4() {
|
|
5358
|
+
|
|
5359
|
+
pcg4d( WHITE_NOISE_SEED );
|
|
5360
|
+
return vec4( WHITE_NOISE_SEED ) / float( 0xffffffffu );
|
|
5361
|
+
|
|
5362
|
+
}
|
|
4955
5363
|
`;
|
|
4956
5364
|
|
|
4957
5365
|
function copyArrayToArray( fromArray, fromStride, toArray, toStride, offset ) {
|
|
@@ -5213,6 +5621,7 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5213
5621
|
filterGlossyFactor: { value: 0.0 },
|
|
5214
5622
|
|
|
5215
5623
|
backgroundAlpha: { value: 1.0 },
|
|
5624
|
+
sobolTexture: { value: null },
|
|
5216
5625
|
},
|
|
5217
5626
|
|
|
5218
5627
|
vertexShader: /* glsl */`
|
|
@@ -5239,6 +5648,9 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5239
5648
|
vec4 envMapTexelToLinear( vec4 a ) { return a; }
|
|
5240
5649
|
#include <common>
|
|
5241
5650
|
|
|
5651
|
+
${ shaderRandFunctions }
|
|
5652
|
+
${ shaderSobolCommon }
|
|
5653
|
+
${ shaderSobolSampling }
|
|
5242
5654
|
${ threeMeshBvh.shaderStructs }
|
|
5243
5655
|
${ threeMeshBvh.shaderIntersectFunction }
|
|
5244
5656
|
${ shaderMaterialStructs }
|
|
@@ -5299,9 +5711,9 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5299
5711
|
|
|
5300
5712
|
}
|
|
5301
5713
|
|
|
5302
|
-
vec3 sampleBackground( vec3 direction ) {
|
|
5714
|
+
vec3 sampleBackground( vec3 direction, vec2 uv ) {
|
|
5303
5715
|
|
|
5304
|
-
vec3 sampleDir = normalize( direction + getHemisphereSample( direction,
|
|
5716
|
+
vec3 sampleDir = normalize( direction + getHemisphereSample( direction, uv ) * 0.5 * backgroundBlur );
|
|
5305
5717
|
|
|
5306
5718
|
#if FEATURE_BACKGROUND_MAP
|
|
5307
5719
|
|
|
@@ -5327,6 +5739,8 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5327
5739
|
|
|
5328
5740
|
color = vec3( 1.0 );
|
|
5329
5741
|
|
|
5742
|
+
// TODO: we should be using sobol sampling here instead of rand but the sobol bounce and path indices need to be incremented
|
|
5743
|
+
// and then reset.
|
|
5330
5744
|
for ( int i = 0; i < traversals; i ++ ) {
|
|
5331
5745
|
|
|
5332
5746
|
if ( bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist ) ) {
|
|
@@ -5453,14 +5867,6 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5453
5867
|
|
|
5454
5868
|
}
|
|
5455
5869
|
|
|
5456
|
-
// tentFilter from Peter Shirley's 'Realistic Ray Tracing (2nd Edition)' book, pg. 60
|
|
5457
|
-
// erichlof/THREE.js-PathTracing-Renderer/
|
|
5458
|
-
float tentFilter( float x ) {
|
|
5459
|
-
|
|
5460
|
-
return x < 0.5 ? sqrt( 2.0 * x ) - 1.0 : 1.0 - sqrt( 2.0 - ( 2.0 * x ) );
|
|
5461
|
-
|
|
5462
|
-
}
|
|
5463
|
-
|
|
5464
5870
|
vec3 ndcToRayOrigin( vec2 coord ) {
|
|
5465
5871
|
|
|
5466
5872
|
vec4 rayOrigin4 = cameraWorldMatrix * invProjectionMatrix * vec4( coord, - 1.0, 1.0 );
|
|
@@ -5472,13 +5878,13 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5472
5878
|
vec2 ssd = vec2( 1.0 ) / resolution;
|
|
5473
5879
|
|
|
5474
5880
|
// Jitter the camera ray by finding a uv coordinate at a random sample
|
|
5475
|
-
// around this pixel's UV coordinate
|
|
5476
|
-
vec2
|
|
5881
|
+
// around this pixel's UV coordinate for AA
|
|
5882
|
+
vec2 ruv = sobol2( 0 );
|
|
5883
|
+
vec2 jitteredUv = vUv + vec2( tentFilter( ruv.x ) * ssd.x, tentFilter( ruv.y ) * ssd.y );
|
|
5477
5884
|
|
|
5478
5885
|
#if CAMERA_TYPE == 2
|
|
5479
5886
|
|
|
5480
5887
|
// Equirectangular projection
|
|
5481
|
-
|
|
5482
5888
|
vec4 rayDirection4 = vec4( equirectUvToDirection( jitteredUv ), 0.0 );
|
|
5483
5889
|
vec4 rayOrigin4 = vec4( 0.0, 0.0, 0.0, 1.0 );
|
|
5484
5890
|
|
|
@@ -5492,20 +5898,17 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5492
5898
|
|
|
5493
5899
|
// get [- 1, 1] normalized device coordinates
|
|
5494
5900
|
vec2 ndc = 2.0 * jitteredUv - vec2( 1.0 );
|
|
5495
|
-
|
|
5496
5901
|
rayOrigin = ndcToRayOrigin( ndc );
|
|
5497
5902
|
|
|
5498
5903
|
#if CAMERA_TYPE == 1
|
|
5499
5904
|
|
|
5500
5905
|
// Orthographic projection
|
|
5501
|
-
|
|
5502
5906
|
rayDirection = ( cameraWorldMatrix * vec4( 0.0, 0.0, - 1.0, 0.0 ) ).xyz;
|
|
5503
5907
|
rayDirection = normalize( rayDirection );
|
|
5504
5908
|
|
|
5505
5909
|
#else
|
|
5506
5910
|
|
|
5507
5911
|
// Perspective projection
|
|
5508
|
-
|
|
5509
5912
|
rayDirection = normalize( mat3(cameraWorldMatrix) * ( invProjectionMatrix * vec4( ndc, 0.0, 1.0 ) ).xyz );
|
|
5510
5913
|
|
|
5511
5914
|
#endif
|
|
@@ -5519,17 +5922,17 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5519
5922
|
vec3 focalPoint = rayOrigin + normalize( rayDirection ) * physicalCamera.focusDistance;
|
|
5520
5923
|
|
|
5521
5924
|
// get the aperture sample
|
|
5522
|
-
|
|
5925
|
+
// if blades === 0 then we assume a circle
|
|
5926
|
+
vec3 shapeUVW= sobol3( 1 );
|
|
5927
|
+
int blades = physicalCamera.apertureBlades;
|
|
5928
|
+
float anamorphicRatio = physicalCamera.anamorphicRatio;
|
|
5929
|
+
vec2 apertureSample = blades == 0 ? sampleCircle( shapeUVW.xy ) : sampleRegularNGon( blades, shapeUVW );
|
|
5930
|
+
apertureSample *= physicalCamera.bokehSize * 0.5 * 1e-3;
|
|
5523
5931
|
|
|
5524
5932
|
// rotate the aperture shape
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
apertureSample.x * ac - apertureSample.y * as,
|
|
5529
|
-
apertureSample.x * as + apertureSample.y * ac
|
|
5530
|
-
);
|
|
5531
|
-
apertureSample.x *= saturate( physicalCamera.anamorphicRatio );
|
|
5532
|
-
apertureSample.y *= saturate( 1.0 / physicalCamera.anamorphicRatio );
|
|
5933
|
+
apertureSample =
|
|
5934
|
+
rotateVector( apertureSample, physicalCamera.apertureRotation ) *
|
|
5935
|
+
saturate( vec2( anamorphicRatio, 1.0 / anamorphicRatio ) );
|
|
5533
5936
|
|
|
5534
5937
|
// create the new ray
|
|
5535
5938
|
rayOrigin += ( cameraWorldMatrix * vec4( apertureSample, 0.0, 0.0 ) ).xyz;
|
|
@@ -5545,6 +5948,8 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5545
5948
|
void main() {
|
|
5546
5949
|
|
|
5547
5950
|
rng_initialize( gl_FragCoord.xy, seed );
|
|
5951
|
+
sobolPixelIndex = ( uint( gl_FragCoord.x ) << 16 ) | ( uint( gl_FragCoord.y ) );
|
|
5952
|
+
sobolPathIndex = uint( seed );
|
|
5548
5953
|
|
|
5549
5954
|
vec3 rayDirection;
|
|
5550
5955
|
vec3 rayOrigin;
|
|
@@ -5578,6 +5983,8 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5578
5983
|
|
|
5579
5984
|
for ( i = 0; i < bounces; i ++ ) {
|
|
5580
5985
|
|
|
5986
|
+
sobolBounceIndex ++;
|
|
5987
|
+
|
|
5581
5988
|
bool hit = bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist );
|
|
5582
5989
|
|
|
5583
5990
|
LightSampleRec lightHit = lightsClosestHit( lights.tex, lights.count, rayOrigin, rayDirection );
|
|
@@ -5592,9 +5999,8 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5592
5999
|
|
|
5593
6000
|
#if FEATURE_MIS
|
|
5594
6001
|
|
|
5595
|
-
// NOTE: we skip MIS for
|
|
5596
|
-
|
|
5597
|
-
if ( lightHit.type == SPOT_LIGHT_TYPE ) {
|
|
6002
|
+
// NOTE: we skip MIS for punctual lights since they are not supported in forward PT case
|
|
6003
|
+
if ( lightHit.type == SPOT_LIGHT_TYPE || lightHit.type == DIR_LIGHT_TYPE || lightHit.type == POINT_LIGHT_TYPE ) {
|
|
5598
6004
|
|
|
5599
6005
|
gl_FragColor.rgb += lightHit.emission * throughputColor;
|
|
5600
6006
|
|
|
@@ -5621,7 +6027,7 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5621
6027
|
|
|
5622
6028
|
if ( i == 0 || transmissiveRay ) {
|
|
5623
6029
|
|
|
5624
|
-
gl_FragColor.rgb += sampleBackground( envRotation3x3 * rayDirection ) * throughputColor;
|
|
6030
|
+
gl_FragColor.rgb += sampleBackground( envRotation3x3 * rayDirection, sobol2( 2 ) ) * throughputColor;
|
|
5625
6031
|
gl_FragColor.a = backgroundAlpha;
|
|
5626
6032
|
|
|
5627
6033
|
} else {
|
|
@@ -5630,7 +6036,7 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5630
6036
|
|
|
5631
6037
|
// get the PDF of the hit envmap point
|
|
5632
6038
|
vec3 envColor;
|
|
5633
|
-
float envPdf =
|
|
6039
|
+
float envPdf = sampleEnvMap( envMapInfo, envRotation3x3 * rayDirection, envColor );
|
|
5634
6040
|
envPdf /= float( lights.count + 1u );
|
|
5635
6041
|
|
|
5636
6042
|
// and weight the contribution
|
|
@@ -5715,7 +6121,7 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5715
6121
|
|| useAlphaTest && albedo.a < alphaTest
|
|
5716
6122
|
|
|
5717
6123
|
// opacity
|
|
5718
|
-
|| material.transparent && ! useAlphaTest && albedo.a <
|
|
6124
|
+
|| material.transparent && ! useAlphaTest && albedo.a < sobol( 3 )
|
|
5719
6125
|
) {
|
|
5720
6126
|
|
|
5721
6127
|
vec3 point = rayOrigin + rayDirection * dist;
|
|
@@ -5776,6 +6182,15 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5776
6182
|
}
|
|
5777
6183
|
|
|
5778
6184
|
// normal
|
|
6185
|
+
if ( material.flatShading ) {
|
|
6186
|
+
|
|
6187
|
+
// if we're rendering a flat shaded object then use the face normals - the face normal
|
|
6188
|
+
// is provided based on the side the ray hits the mesh so flip it to align with the
|
|
6189
|
+
// interpolated vertex normals.
|
|
6190
|
+
normal = faceNormal * side;
|
|
6191
|
+
|
|
6192
|
+
}
|
|
6193
|
+
|
|
5779
6194
|
vec3 baseNormal = normal;
|
|
5780
6195
|
if ( material.normalMap != - 1 ) {
|
|
5781
6196
|
|
|
@@ -5958,7 +6373,7 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5958
6373
|
vec3 clearcoatOutgoing = - normalize( clearcoatInvBasis * rayDirection );
|
|
5959
6374
|
sampleRec = bsdfSample( outgoing, clearcoatOutgoing, normalBasis, invBasis, clearcoatNormalBasis, clearcoatInvBasis, surfaceRec );
|
|
5960
6375
|
|
|
5961
|
-
isShadowRay = sampleRec.specularPdf <
|
|
6376
|
+
isShadowRay = sampleRec.specularPdf < sobol( 4 );
|
|
5962
6377
|
|
|
5963
6378
|
// adjust the hit point by the surface normal by a factor of some offset and the
|
|
5964
6379
|
// maximum component-wise value of the current point to accommodate floating point
|
|
@@ -5975,10 +6390,10 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
5975
6390
|
#if FEATURE_MIS
|
|
5976
6391
|
|
|
5977
6392
|
// uniformly pick a light or environment map
|
|
5978
|
-
if(
|
|
6393
|
+
if( sobol( 5 ) > 1.0 / float( lights.count + 1u ) ) {
|
|
5979
6394
|
|
|
5980
6395
|
// sample a light or environment
|
|
5981
|
-
LightSampleRec lightSampleRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin );
|
|
6396
|
+
LightSampleRec lightSampleRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin, sobol3( 6 ) );
|
|
5982
6397
|
|
|
5983
6398
|
bool isSampleBelowSurface = dot( faceNormal, lightSampleRec.direction ) < 0.0;
|
|
5984
6399
|
if ( isSampleBelowSurface ) {
|
|
@@ -6002,7 +6417,7 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
6002
6417
|
|
|
6003
6418
|
// weight the direct light contribution
|
|
6004
6419
|
float lightPdf = lightSampleRec.pdf / float( lights.count + 1u );
|
|
6005
|
-
float misWeight = misHeuristic( lightPdf, lightMaterialPdf );
|
|
6420
|
+
float misWeight = lightSampleRec.type == SPOT_LIGHT_TYPE || lightSampleRec.type == DIR_LIGHT_TYPE || lightSampleRec.type == POINT_LIGHT_TYPE ? 1.0 : misHeuristic( lightPdf, lightMaterialPdf );
|
|
6006
6421
|
gl_FragColor.rgb += lightSampleRec.emission * throughputColor * sampleColor * misWeight / lightPdf;
|
|
6007
6422
|
|
|
6008
6423
|
}
|
|
@@ -6013,7 +6428,7 @@ LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles,
|
|
|
6013
6428
|
|
|
6014
6429
|
// find a sample in the environment map to include in the contribution
|
|
6015
6430
|
vec3 envColor, envDirection;
|
|
6016
|
-
float envPdf =
|
|
6431
|
+
float envPdf = sampleEnvMapProbability( envMapInfo, sobol2( 7 ), envColor, envDirection );
|
|
6017
6432
|
envDirection = invEnvRotation3x3 * envDirection;
|
|
6018
6433
|
|
|
6019
6434
|
// this env sampling is not set up for transmissive sampling and yields overly bright
|