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.
- package/README.md +3 -1
- package/build/index.module.js +1202 -471
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +1200 -470
- package/build/index.umd.cjs.map +1 -1
- package/package.json +5 -6
- package/src/core/DynamicPathTracingSceneGenerator.js +80 -35
- package/src/core/PathTracingRenderer.js +36 -38
- package/src/core/PathTracingSceneGenerator.js +11 -55
- package/src/materials/debug/GraphMaterial.js +1 -1
- package/src/materials/fullscreen/AlphaDisplayMaterial.js +1 -1
- package/src/materials/fullscreen/DenoiseMaterial.js +1 -1
- package/src/materials/fullscreen/GradientMapMaterial.js +1 -1
- package/src/materials/pathtracing/LambertPathTracingMaterial.js +5 -4
- package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +87 -50
- package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +21 -20
- package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +2 -2
- package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +12 -8
- package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +3 -2
- package/src/materials/pathtracing/glsl/traceScene.glsl.js +11 -13
- package/src/materials/surface/AmbientOcclusionMaterial.js +4 -3
- package/src/shader/bsdf/bsdfSampling.glsl.js +21 -17
- package/src/shader/common/bvhAnyHit.glsl.js +2 -2
- package/src/shader/common/fresnel.glsl.js +15 -9
- package/src/shader/common/intersectShapes.glsl.js +2 -2
- package/src/shader/rand/pcg.glsl.js +4 -4
- package/src/shader/rand/sobol.glsl.js +3 -3
- package/src/shader/rand/stratifiedTexture.glsl.js +45 -0
- package/src/shader/sampling/equirectSampling.glsl.js +10 -10
- package/src/shader/sampling/lightSampling.glsl.js +30 -37
- package/src/shader/structs/fogMaterialBvh.glsl.js +2 -2
- package/src/shader/structs/lightsStruct.glsl.js +15 -6
- package/src/shader/structs/materialStruct.glsl.js +16 -15
- package/src/textures/BlueNoiseTexture.js +87 -0
- package/src/textures/ProceduralEquirectTexture.js +6 -6
- package/src/textures/blueNoise/BlueNoiseGenerator.js +115 -0
- package/src/textures/blueNoise/BlueNoiseSamples.js +214 -0
- package/src/textures/blueNoise/utils.js +24 -0
- package/src/uniforms/EquirectHdrInfoUniform.js +59 -21
- package/src/uniforms/IESProfilesTexture.js +2 -2
- package/src/uniforms/LightsInfoUniformStruct.js +15 -9
- package/src/uniforms/MaterialsTexture.js +3 -1
- package/src/uniforms/RenderTarget2DArray.js +50 -3
- package/src/uniforms/StratifiedSamplesTexture.js +49 -0
- package/src/uniforms/stratified/StratifiedSampler.js +73 -0
- package/src/uniforms/stratified/StratifiedSamplerCombined.js +59 -0
- package/src/uniforms/utils.js +1 -1
- package/src/utils/BlurredEnvMapGenerator.js +4 -4
- package/src/utils/GeometryPreparationUtils.js +8 -95
- package/src/utils/IESLoader.js +7 -5
- package/src/utils/TextureUtils.js +15 -0
- 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,
|
|
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(
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
4
|
+
vec3 rayOrigin, vec3 rayDirection,
|
|
5
5
|
|
|
6
6
|
// output variables
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
76
|
+
// if ( totalInternalReflection( cosTheta, eta ) ) {
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
// return 1.0;
|
|
77
79
|
|
|
78
|
-
|
|
80
|
+
// }
|
|
79
81
|
|
|
80
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
27
|
+
float sampleEquirect( vec3 direction, inout vec3 color ) {
|
|
28
28
|
|
|
29
29
|
vec2 uv = equirectDirectionToUv( direction );
|
|
30
|
-
color = texture2D(
|
|
30
|
+
color = texture2D( envMapInfo.map, uv ).rgb;
|
|
31
31
|
|
|
32
|
-
float totalSum =
|
|
32
|
+
float totalSum = envMapInfo.totalSum;
|
|
33
33
|
float lum = luminance( color );
|
|
34
|
-
ivec2 resolution = textureSize(
|
|
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(
|
|
42
|
+
float sampleEquirectProbability( vec2 r, inout vec3 color, inout vec3 direction ) {
|
|
43
43
|
|
|
44
44
|
// sample env map cdf
|
|
45
|
-
float v = texture2D(
|
|
46
|
-
float u = texture2D(
|
|
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(
|
|
51
|
+
color = texture2D( envMapInfo.map, uv ).rgb;
|
|
52
52
|
|
|
53
|
-
float totalSum =
|
|
53
|
+
float totalSum = envMapInfo.totalSum;
|
|
54
54
|
float lum = luminance( color );
|
|
55
|
-
ivec2 resolution = textureSize(
|
|
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
|
|
45
|
+
bool intersectLightAtIndex( sampler2D lights, vec3 rayOrigin, vec3 rayDirection, uint l, inout LightRecord lightRec ) {
|
|
46
46
|
|
|
47
47
|
bool didHit = false;
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
106
|
+
float dist = sqrt( lightDistSq );
|
|
107
|
+
vec3 direction = toLight / dist;
|
|
108
|
+
vec3 lightNormal = normalize( cross( light.u, light.v ) );
|
|
123
109
|
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
203
|
+
result = rec;
|
|
213
204
|
|
|
214
205
|
} else {
|
|
215
206
|
|
|
216
207
|
// sample the light
|
|
217
|
-
|
|
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
|
-
|
|
18
|
+
vec3 rayOrigin, vec3 rayDirection,
|
|
19
19
|
usampler2D materialIndexAttribute, sampler2D materials,
|
|
20
|
-
|
|
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.
|
|
65
|
-
l.
|
|
66
|
-
l.
|
|
64
|
+
l.decay = s4.g;
|
|
65
|
+
l.distance = s4.b;
|
|
66
|
+
l.coneCos = s4.a;
|
|
67
67
|
|
|
68
|
-
l.
|
|
69
|
-
l.
|
|
70
|
-
|
|
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
|
-
|
|
188
|
-
m.
|
|
189
|
-
m.
|
|
190
|
-
m.
|
|
191
|
-
m.
|
|
192
|
-
m.
|
|
193
|
-
m.
|
|
194
|
-
m.
|
|
195
|
-
m.
|
|
196
|
-
m.
|
|
197
|
-
m.
|
|
198
|
-
m.
|
|
199
|
-
m.
|
|
200
|
-
m.
|
|
201
|
-
m.
|
|
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
|
+
}
|