three-gpu-pathtracer 0.0.16 → 0.0.18

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.
Files changed (52) hide show
  1. package/README.md +3 -1
  2. package/build/index.module.js +1202 -471
  3. package/build/index.module.js.map +1 -1
  4. package/build/index.umd.cjs +1200 -470
  5. package/build/index.umd.cjs.map +1 -1
  6. package/package.json +5 -6
  7. package/src/core/DynamicPathTracingSceneGenerator.js +80 -35
  8. package/src/core/PathTracingRenderer.js +36 -38
  9. package/src/core/PathTracingSceneGenerator.js +11 -55
  10. package/src/materials/debug/GraphMaterial.js +1 -1
  11. package/src/materials/fullscreen/AlphaDisplayMaterial.js +1 -1
  12. package/src/materials/fullscreen/DenoiseMaterial.js +1 -1
  13. package/src/materials/fullscreen/GradientMapMaterial.js +1 -1
  14. package/src/materials/pathtracing/LambertPathTracingMaterial.js +5 -4
  15. package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +87 -50
  16. package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +21 -20
  17. package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +2 -2
  18. package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +12 -8
  19. package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +3 -2
  20. package/src/materials/pathtracing/glsl/traceScene.glsl.js +11 -13
  21. package/src/materials/surface/AmbientOcclusionMaterial.js +4 -3
  22. package/src/shader/bsdf/bsdfSampling.glsl.js +21 -17
  23. package/src/shader/common/bvhAnyHit.glsl.js +2 -2
  24. package/src/shader/common/fresnel.glsl.js +15 -9
  25. package/src/shader/common/intersectShapes.glsl.js +2 -2
  26. package/src/shader/rand/pcg.glsl.js +4 -4
  27. package/src/shader/rand/sobol.glsl.js +3 -3
  28. package/src/shader/rand/stratifiedTexture.glsl.js +45 -0
  29. package/src/shader/sampling/equirectSampling.glsl.js +10 -10
  30. package/src/shader/sampling/lightSampling.glsl.js +30 -37
  31. package/src/shader/structs/fogMaterialBvh.glsl.js +2 -2
  32. package/src/shader/structs/lightsStruct.glsl.js +15 -6
  33. package/src/shader/structs/materialStruct.glsl.js +16 -15
  34. package/src/textures/BlueNoiseTexture.js +87 -0
  35. package/src/textures/ProceduralEquirectTexture.js +6 -6
  36. package/src/textures/blueNoise/BlueNoiseGenerator.js +115 -0
  37. package/src/textures/blueNoise/BlueNoiseSamples.js +214 -0
  38. package/src/textures/blueNoise/utils.js +24 -0
  39. package/src/uniforms/EquirectHdrInfoUniform.js +59 -21
  40. package/src/uniforms/IESProfilesTexture.js +2 -2
  41. package/src/uniforms/LightsInfoUniformStruct.js +15 -9
  42. package/src/uniforms/MaterialsTexture.js +3 -1
  43. package/src/uniforms/RenderTarget2DArray.js +50 -3
  44. package/src/uniforms/StratifiedSamplesTexture.js +49 -0
  45. package/src/uniforms/stratified/StratifiedSampler.js +73 -0
  46. package/src/uniforms/stratified/StratifiedSamplerCombined.js +59 -0
  47. package/src/uniforms/utils.js +1 -1
  48. package/src/utils/BlurredEnvMapGenerator.js +4 -4
  49. package/src/utils/GeometryPreparationUtils.js +8 -95
  50. package/src/utils/IESLoader.js +7 -5
  51. package/src/utils/TextureUtils.js +15 -0
  52. package/src/workers/PathTracingSceneWorker.js +18 -8
@@ -80,7 +80,7 @@ export const bsdfSamplingGLSL = /* glsl */`
80
80
  ${ iridescenceGLSL }
81
81
 
82
82
  // diffuse
83
- float diffuseEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
83
+ float diffuseEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
84
84
 
85
85
  // https://schuttejoe.github.io/post/disneybsdf/
86
86
  float fl = schlickFresnel( wi.z, 0.0 );
@@ -94,15 +94,17 @@ export const bsdfSamplingGLSL = /* glsl */`
94
94
 
95
95
  // TODO: subsurface approx?
96
96
 
97
- float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
97
+ // float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
98
+ float F = disneyFresnel( wo, wi, wh, surf.f0, surf.eta, surf.metalness );
98
99
  color = ( 1.0 - F ) * transFactor * metalFactor * wi.z * surf.color * ( retro + lambert ) / PI;
100
+
99
101
  return wi.z / PI;
100
102
 
101
103
  }
102
104
 
103
105
  vec3 diffuseDirection( vec3 wo, SurfaceRecord surf ) {
104
106
 
105
- vec3 lightDirection = sampleSphere( sobol2( 11 ) );
107
+ vec3 lightDirection = sampleSphere( rand2( 11 ) );
106
108
  lightDirection.z += 1.0;
107
109
  lightDirection = normalize( lightDirection );
108
110
 
@@ -111,7 +113,7 @@ export const bsdfSamplingGLSL = /* glsl */`
111
113
  }
112
114
 
113
115
  // specular
114
- float specularEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
116
+ float specularEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
115
117
 
116
118
  // if roughness is set to 0 then D === NaN which results in black pixels
117
119
  float metalness = surf.metalness;
@@ -147,7 +149,7 @@ export const bsdfSamplingGLSL = /* glsl */`
147
149
  vec3 halfVector = ggxDirection(
148
150
  wo,
149
151
  vec2( roughness ),
150
- sobol2( 12 )
152
+ rand2( 12 )
151
153
  );
152
154
 
153
155
  // apply to new ray by reflecting off the new normal
@@ -158,7 +160,7 @@ export const bsdfSamplingGLSL = /* glsl */`
158
160
 
159
161
  // transmission
160
162
  /*
161
- float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
163
+ float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
162
164
 
163
165
  // See section 4.2 in https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
164
166
 
@@ -184,7 +186,7 @@ export const bsdfSamplingGLSL = /* glsl */`
184
186
  vec3 halfVector = ggxDirection(
185
187
  wo,
186
188
  vec2( filteredRoughness ),
187
- sobol2( 13 )
189
+ rand2( 13 )
188
190
  );
189
191
 
190
192
  vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
@@ -201,12 +203,13 @@ export const bsdfSamplingGLSL = /* glsl */`
201
203
 
202
204
  // TODO: This is just using a basic cosine-weighted specular distribution with an
203
205
  // incorrect PDF value at the moment. Update it to correctly use a GGX distribution
204
- float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, out vec3 color ) {
206
+ float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
205
207
 
206
208
  color = surf.transmission * surf.color;
207
209
 
208
210
  // PDF
209
- float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
211
+ // float F = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
212
+ float F = disneyFresnel( wo, wi, wh, surf.f0, surf.eta, surf.metalness );
210
213
  if ( F >= 1.0 ) {
211
214
 
212
215
  return 0.0;
@@ -221,7 +224,7 @@ export const bsdfSamplingGLSL = /* glsl */`
221
224
 
222
225
  float roughness = surf.filteredRoughness;
223
226
  float eta = surf.eta;
224
- vec3 halfVector = normalize( vec3( 0.0, 0.0, 1.0 ) + sampleSphere( sobol2( 13 ) ) * roughness );
227
+ vec3 halfVector = normalize( vec3( 0.0, 0.0, 1.0 ) + sampleSphere( rand2( 13 ) ) * roughness );
225
228
  vec3 lightDirection = refract( normalize( - wo ), halfVector, eta );
226
229
 
227
230
  if ( surf.thinFilm ) {
@@ -262,7 +265,7 @@ export const bsdfSamplingGLSL = /* glsl */`
262
265
  vec3 halfVector = ggxDirection(
263
266
  wo,
264
267
  vec2( roughness ),
265
- sobol2( 14 )
268
+ rand2( 14 )
266
269
  );
267
270
 
268
271
  // apply to new ray by reflecting off the new normal
@@ -292,12 +295,13 @@ export const bsdfSamplingGLSL = /* glsl */`
292
295
  // bsdf
293
296
  void getLobeWeights(
294
297
  vec3 wo, vec3 wi, vec3 wh, vec3 clearcoatWo, SurfaceRecord surf,
295
- out float diffuseWeight, out float specularWeight, out float transmissionWeight, out float clearcoatWeight
298
+ inout float diffuseWeight, inout float specularWeight, inout float transmissionWeight, inout float clearcoatWeight
296
299
  ) {
297
300
 
298
301
  float metalness = surf.metalness;
299
302
  float transmission = surf.transmission;
300
- float fEstimate = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
303
+ // float fEstimate = evaluateFresnelWeight( dot( wo, wh ), surf.eta, surf.f0 );
304
+ float fEstimate = disneyFresnel( wo, wi, wh, surf.f0, surf.eta, surf.metalness );
301
305
 
302
306
  float transSpecularProb = mix( max( 0.25, fEstimate ), 1.0, metalness );
303
307
  float diffSpecularProb = 0.5 + 0.5 * metalness;
@@ -316,7 +320,7 @@ export const bsdfSamplingGLSL = /* glsl */`
316
320
 
317
321
  float bsdfEval(
318
322
  vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRecord surf,
319
- float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight, out float specularPdf, out vec3 color
323
+ float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight, inout float specularPdf, inout vec3 color
320
324
  ) {
321
325
 
322
326
  float metalness = surf.metalness;
@@ -379,7 +383,7 @@ export const bsdfSamplingGLSL = /* glsl */`
379
383
 
380
384
  }
381
385
 
382
- float bsdfResult( vec3 worldWo, vec3 worldWi, SurfaceRecord surf, out vec3 color ) {
386
+ float bsdfResult( vec3 worldWo, vec3 worldWi, SurfaceRecord surf, inout vec3 color ) {
383
387
 
384
388
  if ( surf.volumeParticle ) {
385
389
 
@@ -413,7 +417,7 @@ export const bsdfSamplingGLSL = /* glsl */`
413
417
  ScatterRecord sampleRec;
414
418
  sampleRec.specularPdf = 0.0;
415
419
  sampleRec.pdf = 1.0 / ( 4.0 * PI );
416
- sampleRec.direction = sampleSphere( sobol2( 16 ) );
420
+ sampleRec.direction = sampleSphere( rand2( 16 ) );
417
421
  sampleRec.color = surf.color / ( 4.0 * PI );
418
422
  return sampleRec;
419
423
 
@@ -465,7 +469,7 @@ export const bsdfSamplingGLSL = /* glsl */`
465
469
  vec3 wi;
466
470
  vec3 clearcoatWi;
467
471
 
468
- float r = sobol( 15 );
472
+ float r = rand( 15 );
469
473
  if ( r <= cdf[0] ) { // diffuse
470
474
 
471
475
  wi = diffuseDirection( wo, surf );
@@ -1,10 +1,10 @@
1
1
  export const bvhAnyHitGLSL = /* glsl */`
2
2
 
3
3
  bool bvhIntersectAnyHit(
4
- BVH bvh, vec3 rayOrigin, vec3 rayDirection,
4
+ vec3 rayOrigin, vec3 rayDirection,
5
5
 
6
6
  // output variables
7
- out float side, out float dist
7
+ inout float side, inout float dist
8
8
  ) {
9
9
 
10
10
  uvec4 faceIndices;
@@ -69,30 +69,36 @@ export const fresnelGLSL = /* glsl */`
69
69
 
70
70
  }
71
71
 
72
- float evaluateFresnelWeight( float cosTheta, float eta, float f0 ) {
72
+ // TODO: disney fresnel was removed and replaced with this fresnel function to better align with
73
+ // the glTF but is causing blown out pixels. Should be revisited
74
+ // float evaluateFresnelWeight( float cosTheta, float eta, float f0 ) {
73
75
 
74
- if ( totalInternalReflection( cosTheta, eta ) ) {
76
+ // if ( totalInternalReflection( cosTheta, eta ) ) {
75
77
 
76
- return 1.0;
78
+ // return 1.0;
77
79
 
78
- }
80
+ // }
79
81
 
80
- return schlickFresnel( cosTheta, f0 );
82
+ // return schlickFresnel( cosTheta, f0 );
81
83
 
82
- }
84
+ // }
83
85
 
84
- /*
85
86
  // https://schuttejoe.github.io/post/disneybsdf/
86
87
  float disneyFresnel( vec3 wo, vec3 wi, vec3 wh, float f0, float eta, float metalness ) {
87
88
 
88
89
  float dotHV = dot( wo, wh );
89
- float dotHL = dot( wi, wh );
90
+ if ( totalInternalReflection( dotHV, eta ) ) {
90
91
 
92
+ return 1.0;
93
+
94
+ }
95
+
96
+ float dotHL = dot( wi, wh );
91
97
  float dielectricFresnel = dielectricFresnel( abs( dotHV ), eta );
92
98
  float metallicFresnel = schlickFresnel( dotHL, f0 );
93
99
 
94
100
  return mix( dielectricFresnel, metallicFresnel, metalness );
95
101
 
96
102
  }
97
- */
103
+
98
104
  `;
@@ -3,7 +3,7 @@ export const intersectShapesGLSL = /* glsl */`
3
3
  // Finds the point where the ray intersects the plane defined by u and v and checks if this point
4
4
  // falls in the bounds of the rectangle on that same plane.
5
5
  // Plane intersection: https://lousodrome.net/blog/light/2020/07/03/intersection-of-a-ray-and-a-plane/
6
- bool intersectsRectangle( vec3 center, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, out float dist ) {
6
+ bool intersectsRectangle( vec3 center, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, inout float dist ) {
7
7
 
8
8
  float t = dot( center - rayOrigin, normal ) / dot( rayDirection, normal );
9
9
 
@@ -34,7 +34,7 @@ export const intersectShapesGLSL = /* glsl */`
34
34
 
35
35
  // Finds the point where the ray intersects the plane defined by u and v and checks if this point
36
36
  // falls in the bounds of the circle on that same plane. See above URL for a description of the plane intersection algorithm.
37
- bool intersectsCircle( vec3 position, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, out float dist ) {
37
+ bool intersectsCircle( vec3 position, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, inout float dist ) {
38
38
 
39
39
  float t = dot( position - rayOrigin, normal ) / dot( rayDirection, normal );
40
40
 
@@ -27,28 +27,28 @@ export const pcgGLSL = /* glsl */`
27
27
  }
28
28
 
29
29
  // returns [ 0, 1 ]
30
- float rand() {
30
+ float pcgRand() {
31
31
 
32
32
  pcg4d( WHITE_NOISE_SEED );
33
33
  return float( WHITE_NOISE_SEED.x ) / float( 0xffffffffu );
34
34
 
35
35
  }
36
36
 
37
- vec2 rand2() {
37
+ vec2 pcgRand2() {
38
38
 
39
39
  pcg4d( WHITE_NOISE_SEED );
40
40
  return vec2( WHITE_NOISE_SEED.xy ) / float(0xffffffffu);
41
41
 
42
42
  }
43
43
 
44
- vec3 rand3() {
44
+ vec3 pcgRand3() {
45
45
 
46
46
  pcg4d( WHITE_NOISE_SEED );
47
47
  return vec3( WHITE_NOISE_SEED.xyz ) / float( 0xffffffffu );
48
48
 
49
49
  }
50
50
 
51
- vec4 rand4() {
51
+ vec4 pcgRand4() {
52
52
 
53
53
  pcg4d( WHITE_NOISE_SEED );
54
54
  return vec4( WHITE_NOISE_SEED ) / float( 0xffffffffu );
@@ -214,9 +214,9 @@ export const sobolSamplingGLSL = /* glsl */`
214
214
 
215
215
  // Seeds
216
216
  uniform sampler2D sobolTexture;
217
- uint sobolPixelIndex;
218
- uint sobolPathIndex;
219
- uint sobolBounceIndex;
217
+ uint sobolPixelIndex = 0u;
218
+ uint sobolPathIndex = 0u;
219
+ uint sobolBounceIndex = 0u;
220
220
 
221
221
  uint sobolGetSeed( uint bounce, uint effect ) {
222
222
 
@@ -0,0 +1,45 @@
1
+ export const stratifiedTextureGLSL = /* glsl */`
2
+
3
+ uniform sampler2D stratifiedTexture;
4
+ uniform sampler2D stratifiedOffsetTexture;
5
+
6
+ uint sobolPixelIndex = 0u;
7
+ uint sobolPathIndex = 0u;
8
+ uint sobolBounceIndex = 0u;
9
+ vec4 pixelSeed = vec4( 0 );
10
+
11
+ vec4 rand4( int v ) {
12
+
13
+ ivec2 uv = ivec2( v, sobolBounceIndex );
14
+ vec4 stratifiedSample = texelFetch( stratifiedTexture, uv, 0 );
15
+ return fract( stratifiedSample + pixelSeed.r ); // blue noise + stratified samples
16
+
17
+ }
18
+
19
+ vec3 rand3( int v ) {
20
+
21
+ return rand4( v ).xyz;
22
+
23
+ }
24
+
25
+ vec2 rand2( int v ) {
26
+
27
+ return rand4( v ).xy;
28
+
29
+ }
30
+
31
+ float rand( int v ) {
32
+
33
+ return rand4( v ).x;
34
+
35
+ }
36
+
37
+ void rng_initialize( vec2 screenCoord, int frame ) {
38
+
39
+ // tile the small noise texture across the entire screen
40
+ ivec2 noiseSize = ivec2( textureSize( stratifiedOffsetTexture, 0 ) );
41
+ pixelSeed = texelFetch( stratifiedOffsetTexture, ivec2( screenCoord.xy ) % noiseSize, 0 );
42
+
43
+ }
44
+
45
+ `;
@@ -24,14 +24,14 @@ export const equirectSamplingGLSL = /* glsl */`
24
24
  }
25
25
 
26
26
  // samples the color given env map with CDF and returns the pdf of the direction
27
- float sampleEquirect( EquirectHdrInfo info, vec3 direction, out vec3 color ) {
27
+ float sampleEquirect( vec3 direction, inout vec3 color ) {
28
28
 
29
29
  vec2 uv = equirectDirectionToUv( direction );
30
- color = texture2D( info.map, uv ).rgb;
30
+ color = texture2D( envMapInfo.map, uv ).rgb;
31
31
 
32
- float totalSum = info.totalSum;
32
+ float totalSum = envMapInfo.totalSum;
33
33
  float lum = luminance( color );
34
- ivec2 resolution = textureSize( info.map, 0 );
34
+ ivec2 resolution = textureSize( envMapInfo.map, 0 );
35
35
  float pdf = lum / totalSum;
36
36
 
37
37
  return float( resolution.x * resolution.y ) * pdf * equirectDirectionPdf( direction );
@@ -39,20 +39,20 @@ export const equirectSamplingGLSL = /* glsl */`
39
39
  }
40
40
 
41
41
  // samples a direction of the envmap with color and retrieves pdf
42
- float sampleEquirectProbability( EquirectHdrInfo info, vec2 r, out vec3 color, out vec3 direction ) {
42
+ float sampleEquirectProbability( vec2 r, inout vec3 color, inout vec3 direction ) {
43
43
 
44
44
  // sample env map cdf
45
- float v = texture2D( info.marginalWeights, vec2( r.x, 0.0 ) ).x;
46
- float u = texture2D( info.conditionalWeights, vec2( r.y, v ) ).x;
45
+ float v = texture2D( envMapInfo.marginalWeights, vec2( r.x, 0.0 ) ).x;
46
+ float u = texture2D( envMapInfo.conditionalWeights, vec2( r.y, v ) ).x;
47
47
  vec2 uv = vec2( u, v );
48
48
 
49
49
  vec3 derivedDirection = equirectUvToDirection( uv );
50
50
  direction = derivedDirection;
51
- color = texture2D( info.map, uv ).rgb;
51
+ color = texture2D( envMapInfo.map, uv ).rgb;
52
52
 
53
- float totalSum = info.totalSum;
53
+ float totalSum = envMapInfo.totalSum;
54
54
  float lum = luminance( color );
55
- ivec2 resolution = textureSize( info.map, 0 );
55
+ ivec2 resolution = textureSize( envMapInfo.map, 0 );
56
56
  float pdf = lum / totalSum;
57
57
 
58
58
  return float( resolution.x * resolution.y ) * pdf * equirectDirectionPdf( direction );
@@ -42,24 +42,17 @@ export const lightSamplingGLSL = /* glsl */`
42
42
 
43
43
  };
44
44
 
45
- bool lightsClosestHit( sampler2D lights, uint lightCount, vec3 rayOrigin, vec3 rayDirection, out LightRecord lightRec ) {
45
+ bool intersectLightAtIndex( sampler2D lights, vec3 rayOrigin, vec3 rayDirection, uint l, inout LightRecord lightRec ) {
46
46
 
47
47
  bool didHit = false;
48
- uint l;
49
- for ( l = 0u; l < lightCount; l ++ ) {
50
-
51
- Light light = readLightInfo( lights, l );
52
-
53
- vec3 u = light.u;
54
- vec3 v = light.v;
55
-
56
- // check for backface
57
- vec3 normal = normalize( cross( u, v ) );
58
- if ( dot( normal, rayDirection ) < 0.0 ) {
48
+ Light light = readLightInfo( lights, l );
59
49
 
60
- continue;
50
+ vec3 u = light.u;
51
+ vec3 v = light.v;
61
52
 
62
- }
53
+ // check for backface
54
+ vec3 normal = normalize( cross( u, v ) );
55
+ if ( dot( normal, rayDirection ) > 0.0 ) {
63
56
 
64
57
  u *= 1.0 / dot( u, u );
65
58
  v *= 1.0 / dot( v, v );
@@ -72,17 +65,13 @@ export const lightSamplingGLSL = /* glsl */`
72
65
  ( light.type == CIRC_AREA_LIGHT_TYPE && intersectsCircle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) )
73
66
  ) {
74
67
 
75
- if ( ! didHit || dist < lightRec.dist ) {
76
-
77
- float cosTheta = dot( rayDirection, normal );
78
- didHit = true;
79
- lightRec.dist = dist;
80
- lightRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
81
- lightRec.emission = light.color * light.intensity;
82
- lightRec.direction = rayDirection;
83
- lightRec.type = light.type;
84
-
85
- }
68
+ float cosTheta = dot( rayDirection, normal );
69
+ didHit = true;
70
+ lightRec.dist = dist;
71
+ lightRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
72
+ lightRec.emission = light.color * light.intensity;
73
+ lightRec.direction = rayDirection;
74
+ lightRec.type = light.type;
86
75
 
87
76
  }
88
77
 
@@ -94,11 +83,6 @@ export const lightSamplingGLSL = /* glsl */`
94
83
 
95
84
  LightRecord randomAreaLightSample( Light light, vec3 rayOrigin, vec2 ruv ) {
96
85
 
97
- LightRecord lightRec;
98
- lightRec.type = light.type;
99
-
100
- lightRec.emission = light.color * light.intensity;
101
-
102
86
  vec3 randomPos;
103
87
  if( light.type == RECT_AREA_LIGHT_TYPE ) {
104
88
 
@@ -119,12 +103,17 @@ export const lightSamplingGLSL = /* glsl */`
119
103
 
120
104
  vec3 toLight = randomPos - rayOrigin;
121
105
  float lightDistSq = dot( toLight, toLight );
122
- lightRec.dist = sqrt( lightDistSq );
106
+ float dist = sqrt( lightDistSq );
107
+ vec3 direction = toLight / dist;
108
+ vec3 lightNormal = normalize( cross( light.u, light.v ) );
123
109
 
124
- vec3 direction = toLight / lightRec.dist;
110
+ LightRecord lightRec;
111
+ lightRec.type = light.type;
112
+ lightRec.emission = light.color * light.intensity;
113
+ lightRec.dist = dist;
125
114
  lightRec.direction = direction;
126
115
 
127
- vec3 lightNormal = normalize( cross( light.u, light.v ) );
116
+ // TODO: the denominator is potentially zero
128
117
  lightRec.pdf = lightDistSq / ( light.area * dot( direction, lightNormal ) );
129
118
 
130
119
  return lightRec;
@@ -172,13 +161,15 @@ export const lightSamplingGLSL = /* glsl */`
172
161
 
173
162
  LightRecord randomLightSample( sampler2D lights, sampler2DArray iesProfiles, uint lightCount, vec3 rayOrigin, vec3 ruv ) {
174
163
 
164
+ LightRecord result;
165
+
175
166
  // pick a random light
176
167
  uint l = uint( ruv.x * float( lightCount ) );
177
168
  Light light = readLightInfo( lights, l );
178
169
 
179
170
  if ( light.type == SPOT_LIGHT_TYPE ) {
180
171
 
181
- return randomSpotLightSample( light, iesProfiles, rayOrigin, ruv.yz );
172
+ result = randomSpotLightSample( light, iesProfiles, rayOrigin, ruv.yz );
182
173
 
183
174
  } else if ( light.type == POINT_LIGHT_TYPE ) {
184
175
 
@@ -198,7 +189,7 @@ export const lightSamplingGLSL = /* glsl */`
198
189
  rec.pdf = 1.0;
199
190
  rec.emission = light.color * light.intensity * distanceFalloff;
200
191
  rec.type = light.type;
201
- return rec;
192
+ result = rec;
202
193
 
203
194
  } else if ( light.type == DIR_LIGHT_TYPE ) {
204
195
 
@@ -209,15 +200,17 @@ export const lightSamplingGLSL = /* glsl */`
209
200
  rec.emission = light.color * light.intensity;
210
201
  rec.type = light.type;
211
202
 
212
- return rec;
203
+ result = rec;
213
204
 
214
205
  } else {
215
206
 
216
207
  // sample the light
217
- return randomAreaLightSample( light, rayOrigin, ruv.yz );
208
+ result = randomAreaLightSample( light, rayOrigin, ruv.yz );
218
209
 
219
210
  }
220
211
 
212
+ return result;
213
+
221
214
  }
222
215
 
223
216
  `;
@@ -15,9 +15,9 @@ bool isMaterialFogVolume( sampler2D materials, uint materialIndex ) {
15
15
 
16
16
  // returns true if we're within the first fog volume we hit
17
17
  bool bvhIntersectFogVolumeHit(
18
- BVH bvh, vec3 rayOrigin, vec3 rayDirection,
18
+ vec3 rayOrigin, vec3 rayDirection,
19
19
  usampler2D materialIndexAttribute, sampler2D materials,
20
- out Material material
20
+ inout Material material
21
21
  ) {
22
22
 
23
23
  material.fogVolume = false;
@@ -61,13 +61,22 @@ export const lightsStructGLSL = /* glsl */`
61
61
  vec4 s4 = texelFetch1D( tex, i + 4u );
62
62
  vec4 s5 = texelFetch1D( tex, i + 5u );
63
63
  l.radius = s4.r;
64
- l.near = s4.g;
65
- l.decay = s4.b;
66
- l.distance = s4.a;
64
+ l.decay = s4.g;
65
+ l.distance = s4.b;
66
+ l.coneCos = s4.a;
67
67
 
68
- l.coneCos = s5.r;
69
- l.penumbraCos = s5.g;
70
- l.iesProfile = int( round ( s5.b ) );
68
+ l.penumbraCos = s5.r;
69
+ l.iesProfile = int( round( s5.g ) );
70
+
71
+ } else {
72
+
73
+ l.radius = 0.0;
74
+ l.decay = 0.0;
75
+ l.distance = 0.0;
76
+
77
+ l.coneCos = 0.0;
78
+ l.penumbraCos = 0.0;
79
+ l.iesProfile = - 1;
71
80
 
72
81
  }
73
82
 
@@ -184,21 +184,22 @@ export const materialStructGLSL = /* glsl */ `
184
184
 
185
185
  uint firstTextureTransformIdx = i + 15u;
186
186
 
187
- m.mapTransform = m.map == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx );
188
- m.metalnessMapTransform = m.metalnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 2u );
189
- m.roughnessMapTransform = m.roughnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 4u );
190
- m.transmissionMapTransform = m.transmissionMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 6u );
191
- m.emissiveMapTransform = m.emissiveMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 8u );
192
- m.normalMapTransform = m.normalMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 10u );
193
- m.clearcoatMapTransform = m.clearcoatMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 12u );
194
- m.clearcoatNormalMapTransform = m.clearcoatNormalMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 14u );
195
- m.clearcoatRoughnessMapTransform = m.clearcoatRoughnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 16u );
196
- m.sheenColorMapTransform = m.sheenColorMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 18u );
197
- m.sheenRoughnessMapTransform = m.sheenRoughnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 20u );
198
- m.iridescenceMapTransform = m.iridescenceMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 22u );
199
- m.iridescenceThicknessMapTransform = m.iridescenceThicknessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 24u );
200
- m.specularColorMapTransform = m.specularColorMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 26u );
201
- m.specularIntensityMapTransform = m.specularIntensityMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 28u );
187
+ // mat3( 1.0 ) is an identity matrix
188
+ m.mapTransform = m.map == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx );
189
+ m.metalnessMapTransform = m.metalnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 2u );
190
+ m.roughnessMapTransform = m.roughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 4u );
191
+ m.transmissionMapTransform = m.transmissionMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 6u );
192
+ m.emissiveMapTransform = m.emissiveMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 8u );
193
+ m.normalMapTransform = m.normalMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 10u );
194
+ m.clearcoatMapTransform = m.clearcoatMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 12u );
195
+ m.clearcoatNormalMapTransform = m.clearcoatNormalMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 14u );
196
+ m.clearcoatRoughnessMapTransform = m.clearcoatRoughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 16u );
197
+ m.sheenColorMapTransform = m.sheenColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 18u );
198
+ m.sheenRoughnessMapTransform = m.sheenRoughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 20u );
199
+ m.iridescenceMapTransform = m.iridescenceMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 22u );
200
+ m.iridescenceThicknessMapTransform = m.iridescenceThicknessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 24u );
201
+ m.specularColorMapTransform = m.specularColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 26u );
202
+ m.specularIntensityMapTransform = m.specularIntensityMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 28u );
202
203
 
203
204
  return m;
204
205
 
@@ -0,0 +1,87 @@
1
+ import { DataTexture, FloatType, NearestFilter, RGBAFormat, RGFormat, RedFormat } from 'three';
2
+ import { BlueNoiseGenerator } from './blueNoise/BlueNoiseGenerator.js';
3
+
4
+ function getStride( channels ) {
5
+
6
+ if ( channels >= 3 ) {
7
+
8
+ return 4;
9
+
10
+ } else {
11
+
12
+ return channels;
13
+
14
+ }
15
+
16
+ }
17
+
18
+ function getFormat( channels ) {
19
+
20
+ switch ( channels ) {
21
+
22
+ case 1:
23
+ return RedFormat;
24
+ case 2:
25
+ return RGFormat;
26
+ default:
27
+ return RGBAFormat;
28
+
29
+ }
30
+
31
+ }
32
+
33
+ export class BlueNoiseTexture extends DataTexture {
34
+
35
+ constructor( size = 64, channels = 1 ) {
36
+
37
+ super( new Float32Array( 4 ), 1, 1, RGBAFormat, FloatType );
38
+ this.minFilter = NearestFilter;
39
+ this.magFilter = NearestFilter;
40
+
41
+ this.size = size;
42
+ this.channels = channels;
43
+ this.update();
44
+
45
+ }
46
+
47
+ update() {
48
+
49
+ const channels = this.channels;
50
+ const size = this.size;
51
+ const generator = new BlueNoiseGenerator();
52
+ generator.channels = channels;
53
+ generator.size = size;
54
+
55
+ const stride = getStride( channels );
56
+ const format = getFormat( stride );
57
+ if ( this.image.width !== size || format !== this.format ) {
58
+
59
+ this.image.width = size;
60
+ this.image.height = size;
61
+ this.image.data = new Float32Array( ( size ** 2 ) * stride );
62
+ this.format = format;
63
+ this.dispose();
64
+
65
+ }
66
+
67
+ const data = this.image.data;
68
+ for ( let i = 0, l = channels; i < l; i ++ ) {
69
+
70
+ const result = generator.generate();
71
+ const bin = result.data;
72
+ const maxValue = result.maxValue;
73
+
74
+ for ( let j = 0, l2 = bin.length; j < l2; j ++ ) {
75
+
76
+ const value = bin[ j ] / maxValue;
77
+ data[ j * stride + i ] = value;
78
+
79
+ }
80
+
81
+ }
82
+
83
+ this.needsUpdate = true;
84
+
85
+ }
86
+
87
+ }