three-gpu-pathtracer 0.0.2 → 0.0.3

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.
@@ -1,22 +1,23 @@
1
- import { Matrix4, Matrix3, Color } from 'three';
1
+ import { Matrix4, Matrix3, Color, Vector2 } from 'three';
2
2
  import { MaterialBase } from './MaterialBase.js';
3
3
  import {
4
4
  MeshBVHUniformStruct, FloatVertexAttributeTexture, UIntVertexAttributeTexture,
5
5
  shaderStructs, shaderIntersectFunction,
6
6
  } from 'three-mesh-bvh';
7
7
  import { shaderMaterialStructs } from '../shader/shaderStructs.js';
8
- import { MaterialStructArrayUniform } from '../uniforms/MaterialStructArrayUniform.js';
8
+ import { MaterialsTexture } from '../uniforms/MaterialsTexture.js';
9
9
  import { RenderTarget2DArray } from '../uniforms/RenderTarget2DArray.js';
10
10
  import { shaderMaterialSampling } from '../shader/shaderMaterialSampling.js';
11
+ import { shaderEnvMapSampling } from '../shader/shaderEnvMapSampling.js';
11
12
  import { shaderUtils } from '../shader/shaderUtils.js';
12
13
  import { PhysicalCameraUniform } from '../uniforms/PhysicalCameraUniform.js';
14
+ import { EquirectHdrInfoUniform } from '../uniforms/EquirectHdrInfoUniform.js';
13
15
 
14
16
  export class PhysicalPathTracingMaterial extends MaterialBase {
15
17
 
16
- // three.js relies on this field to add env map functions and defines
17
- get envMap() {
18
+ onBeforeRender() {
18
19
 
19
- return this.environmentMap;
20
+ this.setDefine( 'FEATURE_DOF', this.physicalCamera.bokehSize === 0 ? 0 : 1 );
20
21
 
21
22
  }
22
23
 
@@ -28,13 +29,15 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
28
29
  depthWrite: false,
29
30
 
30
31
  defines: {
31
- DOF_SUPPORT: 1,
32
+ FEATURE_MIS: 1,
33
+ FEATURE_DOF: 1,
34
+ FEATURE_GRADIENT_BG: 0,
32
35
  TRANSPARENT_TRAVERSALS: 5,
33
- MATERIAL_LENGTH: 0,
34
- GRADIENT_BG: 0,
35
36
  },
36
37
 
37
38
  uniforms: {
39
+ resolution: { value: new Vector2() },
40
+
38
41
  bounces: { value: 3 },
39
42
  physicalCamera: { value: new PhysicalCameraUniform() },
40
43
 
@@ -43,23 +46,22 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
43
46
  tangentAttribute: { value: new FloatVertexAttributeTexture() },
44
47
  uvAttribute: { value: new FloatVertexAttributeTexture() },
45
48
  materialIndexAttribute: { value: new UIntVertexAttributeTexture() },
46
- materials: { value: new MaterialStructArrayUniform() },
49
+ materials: { value: new MaterialsTexture() },
47
50
  textures: { value: new RenderTarget2DArray().texture },
48
51
  cameraWorldMatrix: { value: new Matrix4() },
49
52
  invProjectionMatrix: { value: new Matrix4() },
50
- environmentBlur: { value: 0.2 },
53
+ backgroundBlur: { value: 0.0 },
51
54
  environmentIntensity: { value: 2.0 },
52
- environmentMap: { value: null },
53
55
  environmentRotation: { value: new Matrix3() },
56
+ envMapInfo: { value: new EquirectHdrInfoUniform() },
57
+
54
58
  seed: { value: 0 },
55
59
  opacity: { value: 1 },
56
60
  filterGlossyFactor: { value: 0.0 },
57
61
 
58
- gradientTop: { value: new Color( 0xbfd8ff ) },
59
- gradientBottom: { value: new Color( 0xffffff ) },
60
-
61
62
  bgGradientTop: { value: new Color( 0x111111 ) },
62
63
  bgGradientBottom: { value: new Color( 0x000000 ) },
64
+ backgroundAlpha: { value: 1.0 },
63
65
  },
64
66
 
65
67
  vertexShader: /* glsl */`
@@ -78,14 +80,13 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
78
80
  `,
79
81
 
80
82
  fragmentShader: /* glsl */`
81
- #define RAY_OFFSET 1e-5
83
+ #define RAY_OFFSET 1e-4
82
84
 
83
85
  precision highp isampler2D;
84
86
  precision highp usampler2D;
85
87
  precision highp sampler2DArray;
86
88
  vec4 envMapTexelToLinear( vec4 a ) { return a; }
87
89
  #include <common>
88
- #include <cube_uv_reflection_fragment>
89
90
 
90
91
  ${ shaderStructs }
91
92
  ${ shaderIntersectFunction }
@@ -93,35 +94,27 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
93
94
 
94
95
  ${ shaderUtils }
95
96
  ${ shaderMaterialSampling }
97
+ ${ shaderEnvMapSampling }
96
98
 
97
- #ifdef USE_ENVMAP
98
-
99
- uniform float environmentBlur;
100
- uniform sampler2D environmentMap;
101
99
  uniform mat3 environmentRotation;
100
+ uniform float backgroundBlur;
101
+ uniform float backgroundAlpha;
102
102
 
103
- #else
104
-
105
- uniform vec3 gradientTop;
106
- uniform vec3 gradientBottom;
107
-
108
- #endif
109
-
110
- #if GRADIENT_BG
103
+ #if FEATURE_GRADIENT_BG
111
104
 
112
105
  uniform vec3 bgGradientTop;
113
106
  uniform vec3 bgGradientBottom;
114
107
 
115
108
  #endif
116
109
 
117
- #if DOF_SUPPORT
110
+ #if FEATURE_DOF
118
111
 
119
112
  uniform PhysicalCamera physicalCamera;
120
113
 
121
114
  #endif
122
115
 
116
+ uniform vec2 resolution;
123
117
  uniform int bounces;
124
-
125
118
  uniform mat4 cameraWorldMatrix;
126
119
  uniform mat4 invProjectionMatrix;
127
120
  uniform sampler2D normalAttribute;
@@ -133,10 +126,149 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
133
126
  uniform float filterGlossyFactor;
134
127
  uniform int seed;
135
128
  uniform float opacity;
136
- uniform Material materials[ MATERIAL_LENGTH ];
129
+ uniform sampler2D materials;
130
+
131
+ uniform EquirectHdrInfo envMapInfo;
132
+
137
133
  uniform sampler2DArray textures;
138
134
  varying vec2 vUv;
139
135
 
136
+ vec3 sampleBackground( vec3 direction ) {
137
+
138
+ #if FEATURE_GRADIENT_BG
139
+
140
+ direction = normalize( direction + randDirection() * 0.05 );
141
+
142
+ float value = ( direction.y + 1.0 ) / 2.0;
143
+ value = pow( value, 2.0 );
144
+
145
+ return mix( bgGradientBottom, bgGradientTop, value );
146
+
147
+ #else
148
+
149
+ vec3 sampleDir = normalize( direction + getHemisphereSample( direction, rand2() ) * 0.5 * backgroundBlur );
150
+ return environmentIntensity * sampleEquirectEnvMapColor( sampleDir, envMapInfo.map );
151
+
152
+ #endif
153
+
154
+ }
155
+
156
+ // step through multiple surface hits and accumulate color attenuation based on transmissive surfaces
157
+ bool attenuateHit( BVH bvh, vec3 rayOrigin, vec3 rayDirection, int traversals, out vec3 color ) {
158
+
159
+ // hit results
160
+ uvec4 faceIndices = uvec4( 0u );
161
+ vec3 faceNormal = vec3( 0.0, 0.0, 1.0 );
162
+ vec3 barycoord = vec3( 0.0 );
163
+ float side = 1.0;
164
+ float dist = 0.0;
165
+
166
+ color = vec3( 1.0 );
167
+
168
+ for ( int i = 0; i < traversals; i ++ ) {
169
+
170
+ if ( bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist ) ) {
171
+
172
+ // TODO: attenuate the contribution based on the PDF of the resulting ray including refraction values
173
+ // Should be able to work using the material BSDF functions which will take into account specularity, etc.
174
+ // TODO: should we account for emissive surfaces here?
175
+
176
+ vec2 uv = textureSampleBarycoord( uvAttribute, barycoord, faceIndices.xyz ).xy;
177
+ uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
178
+ Material material = readMaterialInfo( materials, materialIndex );
179
+
180
+ // Opacity Test
181
+
182
+ // albedo
183
+ vec4 albedo = vec4( material.color, material.opacity );
184
+ if ( material.map != - 1 ) {
185
+
186
+ albedo *= texture2D( textures, vec3( uv, material.map ) );
187
+
188
+ }
189
+
190
+ // transmission
191
+ float transmission = material.transmission;
192
+ if ( material.transmissionMap != - 1 ) {
193
+
194
+ transmission *= texture2D( textures, vec3( uv, material.transmissionMap ) ).r;
195
+
196
+ }
197
+
198
+ // metalness
199
+ float metalness = material.metalness;
200
+ if ( material.metalnessMap != - 1 ) {
201
+
202
+ metalness *= texture2D( textures, vec3( uv, material.metalnessMap ) ).b;
203
+
204
+ }
205
+
206
+ float alphaTest = material.alphaTest;
207
+ bool useAlphaTest = alphaTest != 0.0;
208
+ float transmissionFactor = ( 1.0 - metalness ) * transmission;
209
+ if (
210
+ transmissionFactor < rand() && ! (
211
+ // material sidedness
212
+ material.side != 0.0 && side == material.side
213
+
214
+ // alpha test
215
+ || useAlphaTest && albedo.a < alphaTest
216
+
217
+ // opacity
218
+ || ! useAlphaTest && albedo.a < rand()
219
+ )
220
+ ) {
221
+
222
+ return true;
223
+
224
+ }
225
+
226
+ // only attenuate on the way in
227
+ bool isBelowSurface = dot( rayDirection, faceNormal ) < 0.0;
228
+ if ( isBelowSurface ) {
229
+
230
+ color *= albedo.rgb;
231
+
232
+ }
233
+
234
+ // adjust the ray to the new surface
235
+ vec3 point = rayOrigin + rayDirection * dist;
236
+ vec3 absPoint = abs( point );
237
+ float maxPoint = max( absPoint.x, max( absPoint.y, absPoint.z ) );
238
+ rayOrigin = point + faceNormal * ( maxPoint + 1.0 ) * ( isBelowSurface ? - RAY_OFFSET : RAY_OFFSET );
239
+
240
+ } else {
241
+
242
+ return false;
243
+
244
+ }
245
+
246
+ }
247
+
248
+ return true;
249
+
250
+ }
251
+
252
+ // returns whether the ray hit anything, not just the first surface. Could be optimized to not check the full hierarchy.
253
+ bool anyHit( BVH bvh, vec3 rayOrigin, vec3 rayDirection ) {
254
+
255
+ uvec4 faceIndices = uvec4( 0u );
256
+ vec3 faceNormal = vec3( 0.0, 0.0, 1.0 );
257
+ vec3 barycoord = vec3( 0.0 );
258
+ float side = 1.0;
259
+ float dist = 0.0;
260
+ return bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist );
261
+
262
+ }
263
+
264
+ // tentFilter from Peter Shirley's 'Realistic Ray Tracing (2nd Edition)' book, pg. 60
265
+ // erichlof/THREE.js-PathTracing-Renderer/
266
+ float tentFilter( float x ) {
267
+
268
+ return x < 0.5 ? sqrt( 2.0 * x ) - 1.0 : 1.0 - sqrt( 2.0 - ( 2.0 * x ) );
269
+
270
+ }
271
+
140
272
  void main() {
141
273
 
142
274
  rng_initialize( gl_FragCoord.xy, seed );
@@ -146,34 +278,58 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
146
278
  vec3 rayOrigin, rayDirection;
147
279
  ndcToCameraRay( ndc, cameraWorldMatrix, invProjectionMatrix, rayOrigin, rayDirection );
148
280
 
149
- #if DOF_SUPPORT
281
+ // Jitter the camera ray by finding a new subpixel point to point to from the camera origin
282
+ // This is better than just jittering the camera position since it actually results in divergent
283
+ // rays providing better coverage for the pixel
284
+ {
150
285
 
151
- // depth of field
152
- vec3 focalPoint = rayOrigin + normalize( rayDirection ) * physicalCamera.focusDistance;
286
+ // TODO: the complexity here could be improved
287
+ vec3 cameraOrigin = ( cameraWorldMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ) ).xyz;
288
+ vec3 ss00, ss01, ss10, temp;
289
+ ndcToCameraRay( vec2( - 1.0, - 1.0 ), cameraWorldMatrix, invProjectionMatrix, ss00, temp );
290
+ ndcToCameraRay( vec2( - 1.0, 1.0 ), cameraWorldMatrix, invProjectionMatrix, ss01, temp );
291
+ ndcToCameraRay( vec2( 1.0, - 1.0 ), cameraWorldMatrix, invProjectionMatrix, ss10, temp );
153
292
 
154
- // get the aperture sample
155
- vec2 apertureSample = sampleAperture( physicalCamera.apertureBlades ) * physicalCamera.bokehSize * 0.5 * 1e-3;
293
+ vec3 ssdX = ( ss10 - ss00 ) / resolution.x;
294
+ vec3 ssdY = ( ss01 - ss00 ) / resolution.y;
295
+ rayOrigin += tentFilter( rand() ) * ssdX + tentFilter( rand() ) * ssdY;
296
+ rayDirection = normalize( rayOrigin - cameraOrigin );
297
+
298
+ }
299
+
300
+ #if FEATURE_DOF
301
+ {
302
+
303
+ // depth of field
304
+ vec3 focalPoint = rayOrigin + normalize( rayDirection ) * physicalCamera.focusDistance;
305
+
306
+ // get the aperture sample
307
+ vec2 apertureSample = sampleAperture( physicalCamera.apertureBlades ) * physicalCamera.bokehSize * 0.5 * 1e-3;
308
+
309
+ // rotate the aperture shape
310
+ float ac = cos( physicalCamera.apertureRotation );
311
+ float as = sin( physicalCamera.apertureRotation );
312
+ apertureSample = vec2(
313
+ apertureSample.x * ac - apertureSample.y * as,
314
+ apertureSample.x * as + apertureSample.y * ac
315
+ );
316
+ apertureSample.x *= saturate( physicalCamera.anamorphicRatio );
317
+ apertureSample.y *= saturate( 1.0 / physicalCamera.anamorphicRatio );
156
318
 
157
- // rotate the aperture shape
158
- float ac = cos( physicalCamera.apertureRotation );
159
- float as = sin( physicalCamera.apertureRotation );
160
- apertureSample = vec2(
161
- apertureSample.x * ac - apertureSample.y * as,
162
- apertureSample.x * as + apertureSample.y * ac
163
- );
164
- apertureSample.x *= saturate( physicalCamera.anamorphicRatio );
165
- apertureSample.y *= saturate( 1.0 / physicalCamera.anamorphicRatio );
319
+ // create the new ray
320
+ rayOrigin += ( cameraWorldMatrix * vec4( apertureSample, 0.0, 0.0 ) ).xyz;
321
+ rayDirection = focalPoint - rayOrigin;
166
322
 
323
+ }
167
324
  #endif
325
+ rayDirection = normalize( rayDirection );
168
326
 
169
- // create the new ray
170
- rayOrigin += ( cameraWorldMatrix * vec4( apertureSample, 0.0, 0.0 ) ).xyz;
171
- rayDirection = focalPoint - rayOrigin;
327
+ // inverse environment rotation
328
+ mat3 invEnvironmentRotation = inverse( environmentRotation );
172
329
 
173
- // Lambertian render
330
+ // final color
174
331
  gl_FragColor = vec4( 0.0 );
175
-
176
- vec3 throughputColor = vec3( 1.0 );
332
+ gl_FragColor.a = 1.0;
177
333
 
178
334
  // hit results
179
335
  uvec4 faceIndices = uvec4( 0u );
@@ -181,50 +337,60 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
181
337
  vec3 barycoord = vec3( 0.0 );
182
338
  float side = 1.0;
183
339
  float dist = 0.0;
340
+
341
+ // path tracing state
184
342
  float accumulatedRoughness = 0.0;
185
- int i;
343
+ bool transmissiveRay = true;
186
344
  int transparentTraversals = TRANSPARENT_TRAVERSALS;
345
+ vec3 throughputColor = vec3( 1.0 );
346
+ SampleRec sampleRec;
347
+ int i;
348
+
187
349
  for ( i = 0; i < bounces; i ++ ) {
188
350
 
189
351
  if ( ! bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist ) ) {
190
352
 
191
- #if GRADIENT_BG
353
+ if ( i == 0 || transmissiveRay ) {
192
354
 
193
- if ( i == 0 ) {
355
+ gl_FragColor.rgb += sampleBackground( environmentRotation * rayDirection ) * throughputColor;
356
+ gl_FragColor.a = backgroundAlpha;
194
357
 
195
- rayDirection = normalize( rayDirection + randDirection() * 0.05 );
196
- float value = ( rayDirection.y + 1.0 ) / 2.0;
358
+ } else {
197
359
 
198
- value = pow( value, 2.0 );
360
+ #if FEATURE_MIS
199
361
 
200
- gl_FragColor = vec4( mix( bgGradientBottom, bgGradientTop, value ), 1.0 );
201
- break;
362
+ // get the PDF of the hit envmap point
363
+ vec3 envColor;
364
+ float envPdf = envMapSample( environmentRotation * rayDirection, envMapInfo, envColor );
202
365
 
203
- }
366
+ // and weight the contribution
367
+ float misWeight = misHeuristic( sampleRec.pdf, envPdf );
368
+ gl_FragColor.rgb += environmentIntensity * envColor * throughputColor * misWeight;
204
369
 
205
- #endif
370
+ #else
206
371
 
207
- #ifdef USE_ENVMAP
372
+ gl_FragColor.rgb +=
373
+ environmentIntensity *
374
+ sampleEquirectEnvMapColor( environmentRotation * rayDirection, envMapInfo.map ) *
375
+ throughputColor;
208
376
 
209
- vec3 skyColor = textureCubeUV( environmentMap, environmentRotation * rayDirection, environmentBlur ).rgb;
377
+ #endif
210
378
 
211
- #else
379
+ }
380
+ break;
212
381
 
213
- rayDirection = normalize( rayDirection );
214
- float value = ( rayDirection.y + 1.0 ) / 2.0;
215
- vec3 skyColor = mix( gradientBottom, gradientTop, value );
382
+ }
216
383
 
217
- #endif
384
+ uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
385
+ Material material = readMaterialInfo( materials, materialIndex );
218
386
 
219
- gl_FragColor += vec4( skyColor * throughputColor * environmentIntensity, 1.0 );
387
+ if ( material.matte && i == 0 ) {
220
388
 
389
+ gl_FragColor = vec4( 0.0 );
221
390
  break;
222
391
 
223
392
  }
224
393
 
225
- uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
226
- Material material = materials[ materialIndex ];
227
-
228
394
  vec2 uv = textureSampleBarycoord( uvAttribute, barycoord, faceIndices.xyz ).xy;
229
395
 
230
396
  // albedo
@@ -256,7 +422,9 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
256
422
  ) {
257
423
 
258
424
  vec3 point = rayOrigin + rayDirection * dist;
259
- rayOrigin += rayDirection * dist - faceNormal * RAY_OFFSET;
425
+ vec3 absPoint = abs( point );
426
+ float maxPoint = max( absPoint.x, max( absPoint.y, absPoint.z ) );
427
+ rayOrigin = point - ( maxPoint + 1.0 ) * faceNormal * RAY_OFFSET;
260
428
 
261
429
  // only allow a limited number of transparency discards otherwise we could
262
430
  // crash the context with too long a loop.
@@ -361,7 +529,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
361
529
  mat3 invBasis = inverse( normalBasis );
362
530
 
363
531
  vec3 outgoing = - normalize( invBasis * rayDirection );
364
- SampleRec sampleRec = bsdfSample( outgoing, surfaceRec );
532
+ sampleRec = bsdfSample( outgoing, surfaceRec );
365
533
 
366
534
  // adjust the hit point by the surface normal by a factor of some offset and the
367
535
  // maximum component-wise value of the current point to accommodate floating point
@@ -374,6 +542,49 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
374
542
  bool isBelowSurface = dot( rayDirection, faceNormal ) < 0.0;
375
543
  rayOrigin = point + faceNormal * ( maxPoint + 1.0 ) * ( isBelowSurface ? - RAY_OFFSET : RAY_OFFSET );
376
544
 
545
+ // direct env map sampling
546
+ #if FEATURE_MIS
547
+ {
548
+
549
+ // find a sample in the environment map to include in the contribution
550
+ vec3 envColor, envDirection;
551
+ float envPdf = randomEnvMapSample( envMapInfo, envColor, envDirection );
552
+ envDirection = invEnvironmentRotation * envDirection;
553
+
554
+ // this env sampling is not set up for transmissive sampling and yields overly bright
555
+ // results so we ignore the sample in this case.
556
+ // TODO: this should be improved but how? The env samples could traverse a few layers?
557
+ bool isSampleBelowSurface = dot( faceNormal, envDirection ) < 0.0;
558
+ if ( isSampleBelowSurface ) {
559
+
560
+ envPdf = 0.0;
561
+
562
+ }
563
+
564
+ // check if a ray could even reach the surface
565
+ vec3 attenuatedColor;
566
+ if (
567
+ envPdf > 0.0 &&
568
+ isDirectionValid( envDirection, normal, faceNormal ) &&
569
+ ! attenuateHit( bvh, rayOrigin, envDirection, bounces - i, attenuatedColor )
570
+ ) {
571
+
572
+ // get the material pdf
573
+ vec3 sampleColor;
574
+ float envMaterialPdf = bsdfResult( outgoing, normalize( invBasis * envDirection ), surfaceRec, sampleColor );
575
+ if ( envMaterialPdf > 0.0 ) {
576
+
577
+ // weight the direct light contribution
578
+ float misWeight = misHeuristic( envPdf, envMaterialPdf );
579
+ gl_FragColor.rgb += attenuatedColor * environmentIntensity * envColor * throughputColor * sampleColor * misWeight / envPdf;
580
+
581
+ }
582
+
583
+ }
584
+
585
+ }
586
+ #endif
587
+
377
588
  // accumulate a roughness value to offset diffuse, specular, diffuse rays that have high contribution
378
589
  // to a single pixel resulting in fireflies
379
590
  if ( ! isBelowSurface ) {
@@ -381,6 +592,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
381
592
  // determine if this is a rough normal or not by checking how far off straight up it is
382
593
  vec3 halfVector = normalize( outgoing + sampleRec.direction );
383
594
  accumulatedRoughness += sin( acos( halfVector.z ) );
595
+ transmissiveRay = false;
384
596
 
385
597
  }
386
598
 
@@ -405,7 +617,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
405
617
 
406
618
  }
407
619
 
408
- gl_FragColor.a = opacity;
620
+ gl_FragColor.a *= opacity;
409
621
 
410
622
  }
411
623
 
@@ -0,0 +1,67 @@
1
+ export const shaderEnvMapSampling = /* glsl */`
2
+
3
+ vec3 sampleEquirectEnvMapColor( vec3 direction, sampler2D map ) {
4
+
5
+ return texture2D( map, equirectDirectionToUv( direction ) ).rgb;
6
+
7
+ }
8
+
9
+ float envMapDirectionPdf( vec3 direction ) {
10
+
11
+ vec2 uv = equirectDirectionToUv( direction );
12
+ float theta = uv.y * PI;
13
+ float sinTheta = sin( theta );
14
+ if ( sinTheta == 0.0 ) {
15
+
16
+ return 0.0;
17
+
18
+ }
19
+
20
+ return 1.0 / ( 2.0 * PI * PI * sinTheta );
21
+
22
+ }
23
+
24
+ float envMapSample( vec3 direction, EquirectHdrInfo info, out vec3 color ) {
25
+
26
+ vec2 uv = equirectDirectionToUv( direction );
27
+ color = texture2D( info.map, uv ).rgb;
28
+
29
+ float totalSum = texture2D( info.totalSum, vec2( 0.0 ) ).r;
30
+ float lum = colorToLuminance( color );
31
+ ivec2 resolution = textureSize( info.map, 0 );
32
+ float pdf = lum / totalSum;
33
+
34
+ return float( resolution.x * resolution.y ) * pdf * envMapDirectionPdf( direction );
35
+
36
+ }
37
+
38
+ float randomEnvMapSample( EquirectHdrInfo info, out vec3 color, out vec3 direction ) {
39
+
40
+ // sample env map cdf
41
+ vec2 r = rand2();
42
+ float v = texture2D( info.marginalWeights, vec2( r.x, 0.0 ) ).x;
43
+ float u = texture2D( info.conditionalWeights, vec2( r.y, v ) ).x;
44
+ vec2 uv = vec2( u, v );
45
+
46
+ vec3 derivedDirection = equirectUvToDirection( uv );
47
+ direction = derivedDirection;
48
+ color = texture2D( info.map, uv ).rgb;
49
+
50
+ float totalSum = texture2D( info.totalSum, vec2( 0.0 ) ).r;
51
+ float lum = colorToLuminance( color );
52
+ ivec2 resolution = textureSize( info.map, 0 );
53
+ float pdf = lum / totalSum;
54
+
55
+ return float( resolution.x * resolution.y ) * pdf * envMapDirectionPdf( direction );
56
+
57
+ }
58
+
59
+ float misHeuristic( float a, float b ) {
60
+
61
+ float aa = a * a;
62
+ float bb = a * b;
63
+ return aa / ( bb + aa );
64
+
65
+ }
66
+
67
+ `;
@@ -278,6 +278,13 @@ vec3 bsdfColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
278
278
 
279
279
  }
280
280
 
281
+ float bsdfResult( vec3 wo, vec3 wi, SurfaceRec surf, out vec3 color ) {
282
+
283
+ color = bsdfColor( wo, wi, surf );
284
+ return bsdfPdf( wo, wi, surf );
285
+
286
+ }
287
+
281
288
  SampleRec bsdfSample( vec3 wo, SurfaceRec surf ) {
282
289
 
283
290
  float ior = surf.ior;