three-gpu-pathtracer 0.0.4 → 0.0.5

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.
@@ -0,0 +1,87 @@
1
+ export const shaderLightSampling = /* glsl */`
2
+
3
+ struct LightSampleRec {
4
+ bool hit;
5
+ float dist;
6
+ vec3 direction;
7
+ float pdf;
8
+ vec3 emission;
9
+ };
10
+
11
+ LightSampleRec lightsClosestHit( sampler2D lights, uint lightCount, vec3 rayOrigin, vec3 rayDirection ) {
12
+
13
+ LightSampleRec lightSampleRec;
14
+ lightSampleRec.hit = false;
15
+
16
+ uint l;
17
+ for ( l = 0u; l < lightCount; l++ ) {
18
+
19
+ Light light = readLightInfo( lights, l );
20
+
21
+ vec3 u = light.u;
22
+ vec3 v = light.v;
23
+
24
+ // check for backface
25
+ vec3 normal = normalize( cross( u, v ) );
26
+ if ( dot( normal, rayDirection ) < 0.0 ) {
27
+ continue;
28
+ }
29
+
30
+ u *= 1.0 / dot(u, u);
31
+ v *= 1.0 / dot(v, v);
32
+
33
+ float dist;
34
+ if ( intersectsRectangle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) ) {
35
+
36
+ if ( dist < lightSampleRec.dist || !lightSampleRec.hit ) {
37
+
38
+ lightSampleRec.hit = true;
39
+ lightSampleRec.dist = dist;
40
+ float cosTheta = dot( rayDirection, normal );
41
+ lightSampleRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
42
+ lightSampleRec.emission = light.color * light.intensity;
43
+ lightSampleRec.direction = rayDirection;
44
+ }
45
+
46
+ }
47
+
48
+ }
49
+
50
+ return lightSampleRec;
51
+
52
+ }
53
+
54
+ LightSampleRec randomRectAreaLightSample( Light light, vec3 rayOrigin ) {
55
+
56
+ LightSampleRec lightSampleRec;
57
+ lightSampleRec.hit = true;
58
+
59
+ lightSampleRec.emission = light.color * light.intensity;
60
+
61
+ vec3 randomPos = light.position + light.u * ( rand() - 0.5 ) + light.v * ( rand() - 0.5 );
62
+ vec3 toLight = randomPos - rayOrigin;
63
+ float lightDistSq = dot( toLight, toLight );
64
+ lightSampleRec.dist = length( toLight );
65
+
66
+ vec3 direction = normalize( toLight );
67
+ lightSampleRec.direction = direction;
68
+
69
+ vec3 lightNormal = normalize( cross( light.u, light.v ) );
70
+ lightSampleRec.pdf = lightDistSq / ( light.area * dot( direction, lightNormal ) );
71
+
72
+ return lightSampleRec;
73
+
74
+ }
75
+
76
+ LightSampleRec randomLightSample( sampler2D lights, uint lightCount, vec3 rayOrigin ) {
77
+
78
+ // pick a random light
79
+ uint l = uint( rand() * float( lightCount ) );
80
+ Light light = readLightInfo( lights, l );
81
+
82
+ // sample the light
83
+ return randomRectAreaLightSample( light, rayOrigin );
84
+
85
+ }
86
+
87
+ `;
@@ -13,12 +13,16 @@ struct SurfaceRec {
13
13
  vec3 emission;
14
14
  float transmission;
15
15
  float ior;
16
+ float clearcoat;
17
+ float clearcoatRoughness;
18
+ float filteredClearcoatRoughness;
16
19
  };
17
20
 
18
21
  struct SampleRec {
19
22
  float specularPdf;
20
23
  float pdf;
21
24
  vec3 direction;
25
+ vec3 clearcoatDirection;
22
26
  vec3 color;
23
27
  };
24
28
 
@@ -57,10 +61,10 @@ vec3 diffuseColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
57
61
  // specular
58
62
  float specularPDF( vec3 wo, vec3 wi, SurfaceRec surf ) {
59
63
 
60
- // See equation (17) in http://jcgt.org/published/0003/02/03/
64
+ // See equation (27) in http://jcgt.org/published/0003/02/03/
61
65
  float filteredRoughness = surf.filteredRoughness;
62
66
  vec3 halfVector = getHalfVector( wi, wo );
63
- return ggxPDF( wi, halfVector, filteredRoughness ) / ( 4.0 * dot( wi, halfVector ) );
67
+ return ggxPDF( wo, halfVector, filteredRoughness ) / ( 4.0 * dot( wi, halfVector ) );
64
68
 
65
69
  }
66
70
 
@@ -214,7 +218,97 @@ vec3 transmissionColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
214
218
 
215
219
  }
216
220
 
217
- float bsdfPdf( vec3 wo, vec3 wi, SurfaceRec surf, out float specularPdf ) {
221
+ // clearcoat
222
+ float clearcoatPDF( vec3 wo, vec3 wi, SurfaceRec surf ) {
223
+
224
+ // See equation (27) in http://jcgt.org/published/0003/02/03/
225
+ float filteredClearcoatRoughness = surf.filteredClearcoatRoughness;
226
+ vec3 halfVector = getHalfVector( wi, wo );
227
+ return ggxPDF( wo, halfVector, filteredClearcoatRoughness ) / ( 4.0 * dot( wi, halfVector ) );
228
+
229
+ }
230
+
231
+ vec3 clearcoatDirection( vec3 wo, SurfaceRec surf ) {
232
+
233
+ // sample ggx vndf distribution which gives a new normal
234
+ float filteredClearcoatRoughness = surf.filteredClearcoatRoughness;
235
+ vec3 halfVector = ggxDirection(
236
+ wo,
237
+ filteredClearcoatRoughness,
238
+ filteredClearcoatRoughness,
239
+ rand(),
240
+ rand()
241
+ );
242
+
243
+ // apply to new ray by reflecting off the new normal
244
+ return - reflect( wo, halfVector );
245
+
246
+ }
247
+
248
+ void clearcoatColor( inout vec3 color, vec3 wo, vec3 wi, SurfaceRec surf ) {
249
+
250
+ float ior = 1.5;
251
+ bool frontFace = surf.frontFace;
252
+ float filteredClearcoatRoughness = surf.filteredClearcoatRoughness;
253
+
254
+ vec3 halfVector = getHalfVector( wo, wi );
255
+ float iorRatio = frontFace ? 1.0 / ior : ior;
256
+ float G = ggxShadowMaskG2( wi, wo, filteredClearcoatRoughness );
257
+ float D = ggxDistribution( halfVector, filteredClearcoatRoughness );
258
+
259
+ float F = schlickFresnelFromIor( dot( wi, halfVector ), ior );
260
+ float cosTheta = min( wo.z, 1.0 );
261
+ float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
262
+ bool cannotRefract = iorRatio * sinTheta > 1.0;
263
+ if ( cannotRefract ) {
264
+
265
+ F = 1.0;
266
+
267
+ }
268
+
269
+ float fClearcoat = F * D * G / ( 4.0 * abs( wi.z * wo.z ) );
270
+
271
+ color = color * ( 1.0 - surf.clearcoat * F ) + fClearcoat * surf.clearcoat * wi.z;
272
+
273
+ }
274
+
275
+ void getLobeWeights( vec3 wo, vec3 clearcoatWo, SurfaceRec surf, out float diffuseWeight, out float specularWeight, out float transmissionWeight, out float clearcoatWeight ) {
276
+
277
+ float ior = surf.ior;
278
+ float metalness = surf.metalness;
279
+ float transmission = surf.transmission;
280
+ bool frontFace = surf.frontFace;
281
+
282
+ float ratio = frontFace ? 1.0 / ior : ior;
283
+ float cosTheta = min( wo.z, 1.0 );
284
+ float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
285
+ float reflectance = schlickFresnelFromIor( cosTheta, ratio );
286
+ bool cannotRefract = ratio * sinTheta > 1.0;
287
+ if ( cannotRefract ) {
288
+
289
+ reflectance = 1.0;
290
+
291
+ }
292
+
293
+ float transSpecularProb = mix( reflectance, 1.0, metalness );
294
+ float diffSpecularProb = 0.5 + 0.5 * metalness;
295
+
296
+ clearcoatWeight = surf.clearcoat * schlickFresnel( clearcoatWo.z, 0.04 );
297
+ diffuseWeight = ( 1.0 - transmission ) * ( 1.0 - diffSpecularProb ) * ( 1.0 - clearcoatWeight );
298
+ specularWeight = transmission * transSpecularProb + ( 1.0 - transmission ) * diffSpecularProb * ( 1.0 - clearcoatWeight );
299
+ transmissionWeight = transmission * ( 1.0 - transSpecularProb ) * ( 1.0 - clearcoatWeight );
300
+
301
+ float totalWeight = diffuseWeight + specularWeight + transmissionWeight + clearcoatWeight;
302
+ float invTotalWeight = 1.0 / totalWeight;
303
+
304
+ diffuseWeight *= invTotalWeight;
305
+ specularWeight *= invTotalWeight;
306
+ transmissionWeight *= invTotalWeight;
307
+ clearcoatWeight *= invTotalWeight;
308
+
309
+ }
310
+
311
+ float bsdfPdf( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, out float specularPdf, float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight ) {
218
312
 
219
313
  float ior = surf.ior;
220
314
  float metalness = surf.metalness;
@@ -235,46 +329,82 @@ float bsdfPdf( vec3 wo, vec3 wi, SurfaceRec surf, out float specularPdf ) {
235
329
  float spdf = 0.0;
236
330
  float dpdf = 0.0;
237
331
  float tpdf = 0.0;
332
+ float cpdf = 0.0;
238
333
 
239
334
  if ( wi.z < 0.0 ) {
240
335
 
241
- tpdf = transmissionPDF( wo, wi, surf );
336
+ if( transmissionWeight > 0.0 ) {
337
+
338
+ tpdf = transmissionPDF( wo, wi, surf );
339
+
340
+ }
242
341
 
243
342
  } else {
244
343
 
245
- spdf = specularPDF( wo, wi, surf );
246
- dpdf = diffusePDF( wo, wi, surf );
344
+ if( diffuseWeight > 0.0 ) {
345
+
346
+ dpdf = diffusePDF( wo, wi, surf );
347
+
348
+ }
349
+
350
+ if( specularWeight > 0.0 ) {
351
+
352
+ spdf = specularPDF( wo, wi, surf );
353
+
354
+ }
355
+
356
+ }
357
+
358
+ if( clearcoatWi.z >= 0.0 && clearcoatWeight > 0.0 ) {
359
+
360
+ cpdf = clearcoatPDF( clearcoatWo, clearcoatWi, surf );
247
361
 
248
362
  }
249
363
 
250
- float transSpecularProb = mix( reflectance, 1.0, metalness );
251
- float diffSpecularProb = 0.5 + 0.5 * metalness;
252
364
  float pdf =
253
- spdf * transmission * transSpecularProb
254
- + tpdf * transmission * ( 1.0 - transSpecularProb )
255
- + spdf * ( 1.0 - transmission ) * diffSpecularProb
256
- + dpdf * ( 1.0 - transmission ) * ( 1.0 - diffSpecularProb );
365
+ dpdf * diffuseWeight
366
+ + spdf * specularWeight
367
+ + tpdf * transmissionWeight
368
+ + cpdf * clearcoatWeight;
257
369
 
258
370
  // retrieve specular rays for the shadows flag
259
- specularPdf = spdf * transmission * transSpecularProb + spdf * ( 1.0 - transmission ) * diffSpecularProb;
371
+ specularPdf = spdf * specularWeight + cpdf * clearcoatWeight;
260
372
 
261
373
  return pdf;
262
374
 
263
375
  }
264
376
 
265
- vec3 bsdfColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
377
+ vec3 bsdfColor( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight ) {
266
378
 
267
379
  vec3 color = vec3( 0.0 );
268
380
  if ( wi.z < 0.0 ) {
269
381
 
270
- color = transmissionColor( wo, wi, surf );
382
+ if( transmissionWeight > 0.0 ) {
383
+
384
+ color = transmissionColor( wo, wi, surf );
385
+
386
+ }
271
387
 
272
388
  } else {
273
389
 
274
- color = diffuseColor( wo, wi, surf );
275
- color *= 1.0 - surf.transmission;
390
+ if( diffuseWeight > 0.0 ) {
391
+
392
+ color = diffuseColor( wo, wi, surf );
393
+ color *= 1.0 - surf.transmission;
394
+
395
+ }
396
+
397
+ if( specularWeight > 0.0 ) {
276
398
 
277
- color += specularColor( wo, wi, surf );
399
+ color += specularColor( wo, wi, surf );
400
+
401
+ }
402
+
403
+ }
404
+
405
+ if( clearcoatWi.z >= 0.0 && clearcoatWeight > 0.0 ) {
406
+
407
+ clearcoatColor( color, clearcoatWo, clearcoatWi, surf );
278
408
 
279
409
  }
280
410
 
@@ -282,63 +412,89 @@ vec3 bsdfColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
282
412
 
283
413
  }
284
414
 
285
- float bsdfResult( vec3 wo, vec3 wi, SurfaceRec surf, out vec3 color ) {
415
+ float bsdfResult( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, out vec3 color ) {
416
+
417
+ float diffuseWeight;
418
+ float specularWeight;
419
+ float transmissionWeight;
420
+ float clearcoatWeight;
421
+ getLobeWeights( wo, clearcoatWo, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
286
422
 
287
423
  float specularPdf;
288
- color = bsdfColor( wo, wi, surf );
289
- return bsdfPdf( wo, wi, surf, specularPdf );
424
+ color = bsdfColor( wo, clearcoatWo, wi, clearcoatWi, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
425
+ return bsdfPdf( wo, clearcoatWo, wi, clearcoatWi, surf, specularPdf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
290
426
 
291
427
  }
292
428
 
293
- SampleRec bsdfSample( vec3 wo, SurfaceRec surf ) {
429
+ SampleRec bsdfSample( vec3 wo, vec3 clearcoatWo, mat3 normalBasis, mat3 invBasis, mat3 clearcoatNormalBasis, mat3 clearcoatInvBasis, SurfaceRec surf ) {
294
430
 
295
- float ior = surf.ior;
296
- float metalness = surf.metalness;
297
- float transmission = surf.transmission;
298
- bool frontFace = surf.frontFace;
431
+ float diffuseWeight;
432
+ float specularWeight;
433
+ float transmissionWeight;
434
+ float clearcoatWeight;
435
+ getLobeWeights( wo, clearcoatWo, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
299
436
 
300
- float ratio = frontFace ? 1.0 / ior : ior;
301
- float cosTheta = min( wo.z, 1.0 );
302
- float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
303
- float reflectance = schlickFresnelFromIor( cosTheta, ratio );
304
- bool cannotRefract = ratio * sinTheta > 1.0;
305
- if ( cannotRefract ) {
437
+ float pdf[4];
438
+ pdf[0] = diffuseWeight;
439
+ pdf[1] = specularWeight;
440
+ pdf[2] = transmissionWeight;
441
+ pdf[3] = clearcoatWeight;
306
442
 
307
- reflectance = 1.0;
443
+ float cdf[4];
444
+ cdf[0] = pdf[0];
445
+ cdf[1] = pdf[1] + cdf[0];
446
+ cdf[2] = pdf[2] + cdf[1];
447
+ cdf[3] = pdf[3] + cdf[2];
308
448
 
309
- }
449
+ if( cdf[3] != 0.0 ) {
310
450
 
311
- SampleRec result;
312
- if ( rand() < transmission ) {
451
+ float invMaxCdf = 1.0 / cdf[3];
452
+ cdf[0] *= invMaxCdf;
453
+ cdf[1] *= invMaxCdf;
454
+ cdf[2] *= invMaxCdf;
455
+ cdf[3] *= invMaxCdf;
456
+
457
+ } else {
313
458
 
314
- float specularProb = mix( reflectance, 1.0, metalness );
315
- if ( rand() < specularProb ) {
459
+ cdf[0] = 1.0;
460
+ cdf[1] = 0.0;
461
+ cdf[2] = 0.0;
462
+ cdf[3] = 0.0;
316
463
 
317
- result.direction = specularDirection( wo, surf );
464
+ }
318
465
 
319
- } else {
466
+ vec3 wi;
467
+ vec3 clearcoatWi;
320
468
 
321
- result.direction = transmissionDirection( wo, surf );
469
+ float r = rand();
470
+ if ( r <= cdf[0] ) {
322
471
 
323
- }
472
+ wi = diffuseDirection( wo, surf );
473
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
324
474
 
325
- } else {
475
+ } else if ( r <= cdf[1] ) {
326
476
 
327
- float specularProb = 0.5 + 0.5 * metalness;
328
- if ( rand() < specularProb ) {
477
+ wi = specularDirection( wo, surf );
478
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
329
479
 
330
- result.direction = specularDirection( wo, surf );
480
+ } else if ( r <= cdf[2] ) {
331
481
 
332
- } else {
482
+ wi = transmissionDirection( wo, surf );
483
+ clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
333
484
 
334
- result.direction = diffuseDirection( wo, surf );
485
+ } else if ( r <= cdf[3] ) {
335
486
 
336
- }
487
+ clearcoatWi = clearcoatDirection( clearcoatWo, surf );
488
+ wi = normalize( invBasis * normalize( clearcoatNormalBasis * clearcoatWi ) );
337
489
 
338
490
  }
339
491
 
340
- result.pdf = bsdfPdf( wo, result.direction, surf, result.specularPdf );
341
- result.color = bsdfColor( wo, result.direction, surf );
492
+ SampleRec result;
493
+ result.pdf = bsdfPdf( wo, clearcoatWo, wi, clearcoatWi, surf, result.specularPdf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
494
+ result.color = bsdfColor( wo, clearcoatWo, wi, clearcoatWi, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
495
+ result.direction = wi;
496
+ result.clearcoatDirection = clearcoatWi;
497
+
342
498
  return result;
343
499
 
344
500
  }
@@ -41,6 +41,13 @@ export const shaderMaterialStructs = /* glsl */ `
41
41
  int normalMap;
42
42
  vec2 normalScale;
43
43
 
44
+ float clearcoat;
45
+ int clearcoatMap;
46
+ int clearcoatNormalMap;
47
+ vec2 clearcoatNormalScale;
48
+ float clearcoatRoughness;
49
+ int clearcoatRoughnessMap;
50
+
44
51
  int alphaMap;
45
52
 
46
53
  bool castShadow;
@@ -56,6 +63,9 @@ export const shaderMaterialStructs = /* glsl */ `
56
63
  mat3 transmissionMapTransform;
57
64
  mat3 emissiveMapTransform;
58
65
  mat3 normalMapTransform;
66
+ mat3 clearcoatMapTransform;
67
+ mat3 clearcoatNormalMapTransform;
68
+ mat3 clearcoatRoughnessMapTransform;
59
69
 
60
70
  };
61
71
 
@@ -76,7 +86,7 @@ export const shaderMaterialStructs = /* glsl */ `
76
86
 
77
87
  Material readMaterialInfo( sampler2D tex, uint index ) {
78
88
 
79
- uint i = index * 19u;
89
+ uint i = index * 26u;
80
90
 
81
91
  vec4 s0 = texelFetch1D( tex, i + 0u );
82
92
  vec4 s1 = texelFetch1D( tex, i + 1u );
@@ -85,6 +95,7 @@ export const shaderMaterialStructs = /* glsl */ `
85
95
  vec4 s4 = texelFetch1D( tex, i + 4u );
86
96
  vec4 s5 = texelFetch1D( tex, i + 5u );
87
97
  vec4 s6 = texelFetch1D( tex, i + 6u );
98
+ vec4 s7 = texelFetch1D( tex, i + 7u );
88
99
 
89
100
  Material m;
90
101
  m.color = s0.rgb;
@@ -106,16 +117,23 @@ export const shaderMaterialStructs = /* glsl */ `
106
117
  m.normalMap = int( round( s4.r ) );
107
118
  m.normalScale = s4.gb;
108
119
 
109
- m.alphaMap = int( round( s4.a ) );
120
+ m.clearcoat = s4.a;
121
+ m.clearcoatMap = int( round( s5.r ) );
122
+ m.clearcoatRoughness = s5.g;
123
+ m.clearcoatRoughnessMap = int( round( s5.b ) );
124
+ m.clearcoatNormalMap = int( round( s5.a ) );
125
+ m.clearcoatNormalScale = s6.rg;
126
+
127
+ m.alphaMap = int( round( s6.b ) );
110
128
 
111
- m.opacity = s5.r;
112
- m.alphaTest = s5.g;
113
- m.side = s5.b;
114
- m.matte = bool( s5.a );
129
+ m.opacity = s6.a;
130
+ m.alphaTest = s7.r;
131
+ m.side = s7.g;
132
+ m.matte = bool( s7.b );
115
133
 
116
- m.castShadow = ! bool( s6.r );
134
+ m.castShadow = ! bool( s7.a );
117
135
 
118
- uint firstTextureTransformIdx = i + 7u;
136
+ uint firstTextureTransformIdx = i + 8u;
119
137
 
120
138
  m.mapTransform = m.map == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx);
121
139
  m.metalnessMapTransform = m.metalnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 2u );
@@ -123,9 +141,51 @@ export const shaderMaterialStructs = /* glsl */ `
123
141
  m.transmissionMapTransform = m.transmissionMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 6u );
124
142
  m.emissiveMapTransform = m.emissiveMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 8u );
125
143
  m.normalMapTransform = m.normalMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 10u );
144
+ m.clearcoatMapTransform = m.clearcoatMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 12u );
145
+ m.clearcoatNormalMapTransform = m.clearcoatNormalMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 14u );
146
+ m.clearcoatRoughnessMapTransform = m.clearcoatRoughnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 16u );
126
147
 
127
148
  return m;
128
149
 
129
150
  }
130
151
 
131
152
  `;
153
+
154
+ export const shaderLightStruct = /* glsl */ `
155
+ struct Light {
156
+
157
+ vec3 position;
158
+
159
+ vec3 color;
160
+ float intensity;
161
+
162
+ vec3 u;
163
+ vec3 v;
164
+ float area;
165
+
166
+ };
167
+
168
+ Light readLightInfo( sampler2D tex, uint index ) {
169
+
170
+ uint i = index * 4u;
171
+
172
+ vec4 s0 = texelFetch1D( tex, i + 0u );
173
+ vec4 s1 = texelFetch1D( tex, i + 1u );
174
+ vec4 s2 = texelFetch1D( tex, i + 2u );
175
+ vec4 s3 = texelFetch1D( tex, i + 3u );
176
+
177
+ Light l;
178
+ l.position = s0.rgb;
179
+
180
+ l.color = s1.rgb;
181
+ l.intensity = s1.a;
182
+
183
+ l.u = s2.rgb;
184
+ l.v = s3.rgb;
185
+ l.area = s3.a;
186
+
187
+ return l;
188
+
189
+ }
190
+
191
+ `;
@@ -236,6 +236,47 @@ export const shaderUtils = /* glsl */`
236
236
 
237
237
  }
238
238
 
239
+ // Finds the point where the ray intersects the plane defined by u and v and checks if this point
240
+ // falls in the bounds of the rectangle on that same plane.
241
+ // Plane intersection: https://lousodrome.net/blog/light/2020/07/03/intersection-of-a-ray-and-a-plane/
242
+ bool intersectsRectangle( vec3 center, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, out float dist ) {
243
+
244
+ float t = dot( center - rayOrigin, normal ) / dot( rayDirection, normal );
245
+
246
+ if ( t > EPSILON ) {
247
+
248
+ vec3 p = rayOrigin + rayDirection * t;
249
+ vec3 vi = p - center;
250
+
251
+ // check if p falls inside the rectangle
252
+ float a1 = dot( u, vi );
253
+ if ( abs( a1 ) <= 0.5 ) {
254
+
255
+ float a2 = dot( v, vi );
256
+ if ( abs( a2 ) <= 0.5 ) {
257
+
258
+ dist = t;
259
+ return true;
260
+
261
+ }
262
+
263
+ }
264
+
265
+ }
266
+
267
+ return false;
268
+
269
+ }
270
+
271
+ // power heuristic for multiple importance sampling
272
+ float misHeuristic( float a, float b ) {
273
+
274
+ float aa = a * a;
275
+ float bb = b * b;
276
+ return aa / ( aa + bb );
277
+
278
+ }
279
+
239
280
  // An acos with input values bound to the range [-1, 1].
240
281
  float acosSafe( float x ) {
241
282
 
@@ -0,0 +1,83 @@
1
+ import { DataTexture, RGBAFormat, ClampToEdgeWrapping, FloatType, Vector3, Quaternion } from 'three';
2
+
3
+ const LIGHT_PIXELS = 4;
4
+
5
+ export class LightsTexture extends DataTexture {
6
+
7
+ constructor() {
8
+
9
+ super( new Float32Array( 4 ), 1, 1 );
10
+
11
+ this.format = RGBAFormat;
12
+ this.type = FloatType;
13
+ this.wrapS = ClampToEdgeWrapping;
14
+ this.wrapT = ClampToEdgeWrapping;
15
+ this.generateMipmaps = false;
16
+
17
+ }
18
+
19
+ updateFrom( lights ) {
20
+
21
+ let index = 0;
22
+ const pixelCount = lights.length * LIGHT_PIXELS;
23
+ const dimension = Math.ceil( Math.sqrt( pixelCount ) );
24
+
25
+ if ( this.image.width !== dimension ) {
26
+
27
+ this.dispose();
28
+
29
+ this.image.data = new Float32Array( dimension * dimension * 4 );
30
+ this.image.width = dimension;
31
+ this.image.height = dimension;
32
+
33
+ }
34
+
35
+ const floatArray = this.image.data;
36
+
37
+ const u = new Vector3();
38
+ const v = new Vector3();
39
+ const worldQuaternion = new Quaternion();
40
+
41
+ for ( let i = 0, l = lights.length; i < l; i ++ ) {
42
+
43
+ const l = lights[ i ];
44
+
45
+ // position
46
+ l.getWorldPosition( v );
47
+ floatArray[ index ++ ] = v.x;
48
+ floatArray[ index ++ ] = v.y;
49
+ floatArray[ index ++ ] = v.z;
50
+ index ++;
51
+
52
+ // color
53
+ floatArray[ index ++ ] = l.color.r;
54
+ floatArray[ index ++ ] = l.color.g;
55
+ floatArray[ index ++ ] = l.color.b;
56
+
57
+ // intensity
58
+ floatArray[ index ++ ] = l.intensity;
59
+
60
+ // u vector
61
+ l.getWorldQuaternion( worldQuaternion );
62
+ u.set( l.width, 0, 0 ).applyQuaternion( worldQuaternion );
63
+ floatArray[ index ++ ] = u.x;
64
+ floatArray[ index ++ ] = u.y;
65
+ floatArray[ index ++ ] = u.z;
66
+ index ++;
67
+
68
+ // v vector
69
+ v.set( 0, l.height, 0 ).applyQuaternion( worldQuaternion );
70
+ floatArray[ index ++ ] = v.x;
71
+ floatArray[ index ++ ] = v.y;
72
+ floatArray[ index ++ ] = v.z;
73
+
74
+ // area
75
+ floatArray[ index ++ ] = u.cross( v ).length();
76
+
77
+ }
78
+
79
+ this.needsUpdate = true;
80
+
81
+ }
82
+
83
+ }