three-gpu-pathtracer 0.0.23 → 0.0.25

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 (83) hide show
  1. package/README.md +53 -528
  2. package/build/index.module.js +319 -66
  3. package/build/index.module.js.map +1 -1
  4. package/build/index.umd.cjs +318 -64
  5. package/build/index.umd.cjs.map +1 -1
  6. package/package.json +35 -18
  7. package/src/core/PathTracingSceneGenerator.js +21 -2
  8. package/src/core/WebGLPathTracer.js +16 -7
  9. package/src/core/utils/mergeGeometries.js +14 -1
  10. package/src/core/utils/sceneUpdateUtils.js +8 -12
  11. package/src/detectors/PrecisionMaterial.js +9 -9
  12. package/src/index.d.ts +2 -1
  13. package/src/index.js +1 -0
  14. package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +2 -1
  15. package/src/materials/pathtracing/glsl/attenuate_hit_function.glsl.js +2 -1
  16. package/src/materials/pathtracing/glsl/camera_util_functions.glsl.js +1 -1
  17. package/src/materials/pathtracing/glsl/get_surface_record_function.glsl.js +2 -1
  18. package/src/materials/surface/AmbientOcclusionMaterial.js +2 -0
  19. package/src/objects/PhysicalSpotLight.js +2 -0
  20. package/src/shader/bsdf/bsdf_functions.glsl.js +0 -1
  21. package/src/shader/bvh/inside_fog_volume_function.glsl.js +1 -1
  22. package/src/shader/structs/material_struct.glsl.js +3 -1
  23. package/src/textures/BlueNoiseTexture.js +6 -6
  24. package/src/textures/ProceduralEquirectTexture.js +8 -7
  25. package/src/textures/turquinMetal.png +0 -0
  26. package/src/uniforms/EquirectHdrInfoUniform.js +9 -4
  27. package/src/uniforms/FloatAttributeTextureArray.js +11 -11
  28. package/src/uniforms/MaterialsTexture.js +13 -10
  29. package/src/webgpu/API.md +762 -0
  30. package/src/webgpu/AtlasTexture.js +471 -0
  31. package/src/webgpu/BlurredEnvMapGenerator.js +129 -0
  32. package/src/webgpu/EquirectBackgroundInfo.js +97 -0
  33. package/src/webgpu/EquirectHdrInfoNode.js +151 -0
  34. package/src/webgpu/LightsInfoNode.js +224 -0
  35. package/src/webgpu/MegaKernelPathTracer.js +277 -0
  36. package/src/webgpu/PathTracerBackend.js +216 -0
  37. package/src/webgpu/TurquinTexture.js +169 -0
  38. package/src/webgpu/WaveFrontPathTracer.js +586 -0
  39. package/src/webgpu/WebGPUPathTracer.js +1302 -0
  40. package/src/webgpu/compute/ComputeKernel.js +92 -0
  41. package/src/webgpu/compute/CopyBufferKernel.js +40 -0
  42. package/src/webgpu/compute/PathTracerMegaKernel.js +470 -0
  43. package/src/webgpu/compute/SampleDebugKernel.js +51 -0
  44. package/src/webgpu/compute/TallySampleCountsKernel.js +111 -0
  45. package/src/webgpu/compute/ZeroOutBufferKernel.js +35 -0
  46. package/src/webgpu/compute/ZeroOutKernel.js +31 -0
  47. package/src/webgpu/compute/wavefront/LogicKernel.js +349 -0
  48. package/src/webgpu/compute/wavefront/MaterialKernel.js +371 -0
  49. package/src/webgpu/compute/wavefront/PopulatePixelIndicesKernel.js +72 -0
  50. package/src/webgpu/compute/wavefront/QueueLengthToDispatchKernel.js +38 -0
  51. package/src/webgpu/compute/wavefront/ResetSlotsKernel.js +66 -0
  52. package/src/webgpu/compute/wavefront/TraceRayKernel.js +71 -0
  53. package/src/webgpu/compute/wavefront/TraceShadowRayKernel.js +67 -0
  54. package/src/webgpu/compute/wavefront/structs.js +155 -0
  55. package/src/webgpu/constants.js +62 -0
  56. package/src/webgpu/denoise/OIDNDenoiser.js +431 -0
  57. package/src/webgpu/index.d.ts +176 -0
  58. package/src/webgpu/index.js +11 -0
  59. package/src/webgpu/materials/GltfCompliantMaterial.js +608 -0
  60. package/src/webgpu/materials/GraphMaterial.js +331 -0
  61. package/src/webgpu/materials/PathtracingMaterial.js +89 -0
  62. package/src/webgpu/materials/RenderToScreenMaterial.js +150 -0
  63. package/src/webgpu/materials/debug/AtlasDebugMaterial.js +41 -0
  64. package/src/webgpu/materials/debug/SampleDensityMaterial.js +61 -0
  65. package/src/webgpu/nodes/PathtracerBVHComputeData.js +1037 -0
  66. package/src/webgpu/nodes/debugBounds.wgsl.js +228 -0
  67. package/src/webgpu/nodes/eon.wgsl.js +262 -0
  68. package/src/webgpu/nodes/ggx.wgsl.js +190 -0
  69. package/src/webgpu/nodes/lights.wgsl.js +203 -0
  70. package/src/webgpu/nodes/material.wgsl.js +990 -0
  71. package/src/webgpu/nodes/rand/bluedither.wgsl.js +92 -0
  72. package/src/webgpu/nodes/rand/pcg.wgsl.js +81 -0
  73. package/src/webgpu/nodes/rand/sobol.wgsl.js +287 -0
  74. package/src/webgpu/nodes/random.wgsl.js +20 -0
  75. package/src/webgpu/nodes/reset.wgsl.js +21 -0
  76. package/src/webgpu/nodes/sampling.wgsl.js +193 -0
  77. package/src/webgpu/nodes/sheen.wgsl.js +148 -0
  78. package/src/webgpu/nodes/structs.wgsl.js +272 -0
  79. package/src/webgpu/nodes/utils.wgsl.js +366 -0
  80. package/src/webgpu/shims/ArrayCameraShim.js +74 -0
  81. package/src/webgpu/shims/EquirectCameraShim.js +37 -0
  82. package/src/webgpu/shims/PhysicalCameraShim.js +151 -0
  83. package/src/webgpu/upscale/FSRUpscaler.js +108 -0
@@ -279,7 +279,20 @@
279
279
  const attr = geometry.getAttribute( key );
280
280
  if ( ! skip ) {
281
281
 
282
- copyAttributeContents( attr, targetAttribute, offset );
282
+ if ( key === 'color' && targetAttribute.itemSize !== attr.itemSize ) {
283
+
284
+ // make sure the color attribute is aligned with itemSize 3 to 4
285
+ for ( let index = offset, l = attr.count; index < l; index ++ ) {
286
+
287
+ attr.setXYZW( index, targetAttribute.getX( index ), targetAttribute.getY( index ), targetAttribute.getZ( index ), 1.0 );
288
+
289
+ }
290
+
291
+ } else {
292
+
293
+ copyAttributeContents( attr, targetAttribute, offset );
294
+
295
+ }
283
296
 
284
297
  }
285
298
 
@@ -1317,6 +1330,7 @@
1317
1330
  this._bvhWorker = null;
1318
1331
  this._pendingGenerate = null;
1319
1332
  this._buildAsync = false;
1333
+ this._materialUuids = null;
1320
1334
 
1321
1335
  }
1322
1336
 
@@ -1398,12 +1412,29 @@
1398
1412
  // generate the geometry
1399
1413
  const result = staticGeometryGenerator.generate( geometry );
1400
1414
  const materials = result.materials;
1415
+ let needsMaterialIndexUpdate = result.changeType !== NO_CHANGE || this._materialUuids === null || this._materialUuids.length !== length;
1416
+ if ( ! needsMaterialIndexUpdate ) {
1417
+
1418
+ for ( let i = 0, length = materials.length; i < length; i ++ ) {
1419
+
1420
+ const material = materials[ i ];
1421
+ if ( material.uuid !== this._materialUuids[ i ] ) {
1422
+
1423
+ needsMaterialIndexUpdate = true;
1424
+ break;
1425
+
1426
+ }
1427
+
1428
+ }
1429
+
1430
+ }
1431
+
1401
1432
  const textures = getTextures$1( materials );
1402
1433
  const { lights, iesTextures } = getLights$1( objects );
1403
-
1404
- if ( result.changeType !== NO_CHANGE ) {
1434
+ if ( needsMaterialIndexUpdate ) {
1405
1435
 
1406
1436
  updateMaterialIndexAttribute( geometry, materials, materials );
1437
+ this._materialUuids = materials.map( material => material.uuid );
1407
1438
 
1408
1439
  }
1409
1440
 
@@ -1447,6 +1478,7 @@
1447
1478
  return {
1448
1479
  bvhChanged: result.changeType !== NO_CHANGE,
1449
1480
  bvh: this.bvh,
1481
+ needsMaterialIndexUpdate,
1450
1482
  lights,
1451
1483
  iesTextures,
1452
1484
  geometry,
@@ -2216,6 +2248,12 @@
2216
2248
 
2217
2249
  }
2218
2250
 
2251
+ getPixelWeight( r, g, b ) {
2252
+
2253
+ return colorToLuminance( r, g, b );
2254
+
2255
+ }
2256
+
2219
2257
  dispose() {
2220
2258
 
2221
2259
  this.marginalWeights.dispose();
@@ -2256,10 +2294,9 @@
2256
2294
  const g = three.DataUtils.fromHalfFloat( data[ 4 * i + 1 ] );
2257
2295
  const b = three.DataUtils.fromHalfFloat( data[ 4 * i + 2 ] );
2258
2296
 
2259
- // the probability of the pixel being selected in this row is the
2260
- // scale of the luminance relative to the rest of the pixels.
2261
- // TODO: this should also account for the solid angle of the pixel when sampling
2262
- const weight = colorToLuminance( r, g, b );
2297
+ // the probability of the pixel being selected in its row is proportional
2298
+ // to its luminance relative to the other pixels.
2299
+ const weight = this.getPixelWeight( r, g, b, y, height );
2263
2300
  cumulativeRowWeight += weight;
2264
2301
  totalSumValue += weight;
2265
2302
 
@@ -2590,17 +2627,17 @@
2590
2627
  let maxValue = 1.0;
2591
2628
  switch ( fromArray.constructor ) {
2592
2629
 
2593
- case Uint8Array:
2594
- case Uint16Array:
2595
- case Uint32Array:
2596
- maxValue = 2 ** bpe - 1;
2597
- break;
2630
+ case Uint8Array:
2631
+ case Uint16Array:
2632
+ case Uint32Array:
2633
+ maxValue = 2 ** bpe - 1;
2634
+ break;
2598
2635
 
2599
- case Int8Array:
2600
- case Int16Array:
2601
- case Int32Array:
2602
- maxValue = 2 ** ( bpe - 1 ) - 1;
2603
- break;
2636
+ case Int8Array:
2637
+ case Int16Array:
2638
+ case Int32Array:
2639
+ maxValue = 2 ** ( bpe - 1 ) - 1;
2640
+ break;
2604
2641
 
2605
2642
  }
2606
2643
 
@@ -2851,20 +2888,16 @@
2851
2888
  function getLights( scene ) {
2852
2889
 
2853
2890
  const lights = [];
2854
- scene.traverse( c => {
2855
-
2856
- if ( c.visible ) {
2891
+ scene.traverseVisible( c => {
2857
2892
 
2858
- if (
2859
- c.isRectAreaLight ||
2860
- c.isSpotLight ||
2861
- c.isPointLight ||
2862
- c.isDirectionalLight
2863
- ) {
2864
-
2865
- lights.push( c );
2893
+ if (
2894
+ c.isRectAreaLight ||
2895
+ c.isSpotLight ||
2896
+ c.isPointLight ||
2897
+ c.isDirectionalLight
2898
+ ) {
2866
2899
 
2867
- }
2900
+ lights.push( c );
2868
2901
 
2869
2902
  }
2870
2903
 
@@ -2874,7 +2907,7 @@
2874
2907
 
2875
2908
  }
2876
2909
 
2877
- const MATERIAL_PIXELS = 45;
2910
+ const MATERIAL_PIXELS = 47;
2878
2911
  const MATERIAL_STRIDE = MATERIAL_PIXELS * 4;
2879
2912
 
2880
2913
  class MaterialFeatures {
@@ -3234,15 +3267,15 @@
3234
3267
 
3235
3268
  switch ( m.side ) {
3236
3269
 
3237
- case three.FrontSide:
3238
- floatArray[ index ++ ] = 1;
3239
- break;
3240
- case three.BackSide:
3241
- floatArray[ index ++ ] = - 1;
3242
- break;
3243
- case three.DoubleSide:
3244
- floatArray[ index ++ ] = 0;
3245
- break;
3270
+ case three.FrontSide:
3271
+ floatArray[ index ++ ] = 1;
3272
+ break;
3273
+ case three.BackSide:
3274
+ floatArray[ index ++ ] = - 1;
3275
+ break;
3276
+ case three.DoubleSide:
3277
+ floatArray[ index ++ ] = 0;
3278
+ break;
3246
3279
 
3247
3280
  }
3248
3281
 
@@ -3299,6 +3332,9 @@
3299
3332
  // specularIntensityMap transform 43
3300
3333
  index += writeTextureMatrixToArray( m, 'specularIntensityMap', floatArray, index );
3301
3334
 
3335
+ // alphaMap transform 45
3336
+ index += writeTextureMatrixToArray( m, 'alphaMap', floatArray, index );
3337
+
3302
3338
  }
3303
3339
 
3304
3340
  // check if the contents have changed
@@ -4112,12 +4148,12 @@
4112
4148
 
4113
4149
  switch ( channels ) {
4114
4150
 
4115
- case 1:
4116
- return three.RedFormat;
4117
- case 2:
4118
- return three.RGFormat;
4119
- default:
4120
- return three.RGBAFormat;
4151
+ case 1:
4152
+ return three.RedFormat;
4153
+ case 2:
4154
+ return three.RGFormat;
4155
+ default:
4156
+ return three.RGBAFormat;
4121
4157
 
4122
4158
  }
4123
4159
 
@@ -4378,6 +4414,7 @@
4378
4414
  mat3 iridescenceThicknessMapTransform;
4379
4415
  mat3 specularColorMapTransform;
4380
4416
  mat3 specularIntensityMapTransform;
4417
+ mat3 alphaMapTransform;
4381
4418
 
4382
4419
  };
4383
4420
 
@@ -4398,7 +4435,7 @@
4398
4435
 
4399
4436
  Material readMaterialInfo( sampler2D tex, uint index ) {
4400
4437
 
4401
- uint i = index * 45u;
4438
+ uint i = index * uint( MATERIAL_PIXELS );
4402
4439
 
4403
4440
  vec4 s0 = texelFetch1D( tex, i + 0u );
4404
4441
  vec4 s1 = texelFetch1D( tex, i + 1u );
@@ -4497,6 +4534,7 @@
4497
4534
  m.iridescenceThicknessMapTransform = m.iridescenceThicknessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 24u );
4498
4535
  m.specularColorMapTransform = m.specularColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 26u );
4499
4536
  m.specularIntensityMapTransform = m.specularIntensityMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 28u );
4537
+ m.alphaMapTransform = m.alphaMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 30u );
4500
4538
 
4501
4539
  return m;
4502
4540
 
@@ -5677,7 +5715,6 @@
5677
5715
  // See equation (1) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
5678
5716
  vec3 color = surf.sheenColor;
5679
5717
  color *= D * G / ( 4.0 * abs( cosThetaO * cosThetaI ) );
5680
- color *= wi.z;
5681
5718
 
5682
5719
  return color;
5683
5720
 
@@ -6263,7 +6300,7 @@
6263
6300
  // returns whether the given material is a fog material or not
6264
6301
  bool isMaterialFogVolume( sampler2D materials, uint materialIndex ) {
6265
6302
 
6266
- uint i = materialIndex * 45u;
6303
+ uint i = materialIndex * uint( MATERIAL_PIXELS );
6267
6304
  vec4 s14 = texelFetch1D( materials, i + 14u );
6268
6305
  return bool( int( s14.b ) & 4 );
6269
6306
 
@@ -6493,7 +6530,8 @@ bool bvhIntersectFogVolumeHit(
6493
6530
  // alphaMap
6494
6531
  if ( material.alphaMap != - 1 ) {
6495
6532
 
6496
- albedo.a *= texture2D( textures, vec3( uv, material.alphaMap ) ).x;
6533
+ vec3 uvPrime = material.alphaMapTransform * vec3( uv, 1 );
6534
+ albedo.a *= texture2D( textures, vec3( uvPrime.xy, material.alphaMap ) ).x;
6497
6535
 
6498
6536
  }
6499
6537
 
@@ -6635,7 +6673,7 @@ bool bvhIntersectFogVolumeHit(
6635
6673
  vec3 shapeUVW= rand3( 1 );
6636
6674
  int blades = physicalCamera.apertureBlades;
6637
6675
  float anamorphicRatio = physicalCamera.anamorphicRatio;
6638
- vec2 apertureSample = blades == 0 ? sampleCircle( shapeUVW.xy ) : sampleRegularPolygon( blades, shapeUVW );
6676
+ vec2 apertureSample = sampleAperture( blades, shapeUVW );
6639
6677
  apertureSample *= physicalCamera.bokehSize * 0.5 * 1e-3;
6640
6678
 
6641
6679
  // rotate the aperture shape
@@ -6805,7 +6843,8 @@ bool bvhIntersectFogVolumeHit(
6805
6843
  // alphaMap
6806
6844
  if ( material.alphaMap != - 1 ) {
6807
6845
 
6808
- albedo.a *= texture2D( textures, vec3( uv, material.alphaMap ) ).x;
6846
+ vec3 uvPrime = material.alphaMapTransform * vec3( uv, 1 );
6847
+ albedo.a *= texture2D( textures, vec3( uvPrime.xy, material.alphaMap ) ).x;
6809
6848
 
6810
6849
  }
6811
6850
 
@@ -7220,6 +7259,7 @@ bool bvhIntersectFogVolumeHit(
7220
7259
  ATTR_TANGENT: 1,
7221
7260
  ATTR_UV: 2,
7222
7261
  ATTR_COLOR: 3,
7262
+ MATERIAL_PIXELS: MATERIAL_PIXELS,
7223
7263
  },
7224
7264
 
7225
7265
  uniforms: {
@@ -8155,8 +8195,8 @@ bool bvhIntersectFogVolumeHit(
8155
8195
  constructor( width = 512, height = 512 ) {
8156
8196
 
8157
8197
  super(
8158
- new Float32Array( width * height * 4 ),
8159
- width, height, three.RGBAFormat, three.FloatType, three.EquirectangularReflectionMapping,
8198
+ new Uint16Array( width * height * 4 ),
8199
+ width, height, three.RGBAFormat, three.HalfFloatType, three.EquirectangularReflectionMapping,
8160
8200
  three.RepeatWrapping, three.ClampToEdgeWrapping, three.LinearFilter, three.LinearFilter,
8161
8201
  );
8162
8202
 
@@ -8188,10 +8228,10 @@ bool bvhIntersectFogVolumeHit(
8188
8228
 
8189
8229
  const i = y * width + x;
8190
8230
  const i4 = 4 * i;
8191
- data[ i4 + 0 ] = ( _color.r );
8192
- data[ i4 + 1 ] = ( _color.g );
8193
- data[ i4 + 2 ] = ( _color.b );
8194
- data[ i4 + 3 ] = ( 1.0 );
8231
+ data[ i4 + 0 ] = three.DataUtils.toHalfFloat( _color.r );
8232
+ data[ i4 + 1 ] = three.DataUtils.toHalfFloat( _color.g );
8233
+ data[ i4 + 2 ] = three.DataUtils.toHalfFloat( _color.b );
8234
+ data[ i4 + 3 ] = three.DataUtils.toHalfFloat( 1.0 );
8195
8235
 
8196
8236
  }
8197
8237
 
@@ -8592,6 +8632,8 @@ bool bvhIntersectFogVolumeHit(
8592
8632
 
8593
8633
  constructor( renderer ) {
8594
8634
 
8635
+ console.warn( 'WebGLPathTracer: This class has been deprecated and will be removed in a future release. Use "WebGPUPathTracer" instead.' );
8636
+
8595
8637
  // members
8596
8638
  this._renderer = renderer;
8597
8639
  this._generator = new PathTracingSceneGenerator();
@@ -8796,7 +8838,7 @@ bool bvhIntersectFogVolumeHit(
8796
8838
  }
8797
8839
 
8798
8840
  // update scene environment
8799
- material.environmentIntensity = scene.environmentIntensity ?? 1;
8841
+ material.environmentIntensity = scene.environment !== null ? ( scene.environmentIntensity ?? 1 ) : 0;
8800
8842
  material.environmentRotation.makeRotationFromEuler( scene.environmentRotation ).invert();
8801
8843
  if ( this._previousEnvironment !== scene.environment ) {
8802
8844
 
@@ -8816,10 +8858,6 @@ bool bvhIntersectFogVolumeHit(
8816
8858
 
8817
8859
  }
8818
8860
 
8819
- } else {
8820
-
8821
- material.environmentIntensity = 0;
8822
-
8823
8861
  }
8824
8862
 
8825
8863
  }
@@ -8837,6 +8875,7 @@ bool bvhIntersectFogVolumeHit(
8837
8875
  geometry,
8838
8876
  bvh,
8839
8877
  bvhChanged,
8878
+ needsMaterialIndexUpdate,
8840
8879
  } = results;
8841
8880
 
8842
8881
  this._materials = materials;
@@ -8854,6 +8893,10 @@ bool bvhIntersectFogVolumeHit(
8854
8893
  geometry.attributes.color,
8855
8894
  );
8856
8895
 
8896
+ }
8897
+
8898
+ if ( needsMaterialIndexUpdate ) {
8899
+
8857
8900
  material.materialIndexAttribute.updateFrom( geometry.attributes.materialIndex );
8858
8901
 
8859
8902
  }
@@ -8984,8 +9027,8 @@ bool bvhIntersectFogVolumeHit(
8984
9027
 
8985
9028
  dispose() {
8986
9029
 
8987
- this._renderQuad.dispose();
8988
- this._renderQuad.material.dispose();
9030
+ this._quad.dispose();
9031
+ this._quad.material.dispose();
8989
9032
  this._pathTracer.dispose();
8990
9033
 
8991
9034
  }
@@ -9013,6 +9056,12 @@ bool bvhIntersectFogVolumeHit(
9013
9056
 
9014
9057
  }
9015
9058
 
9059
+ getRenderTime() {
9060
+
9061
+ return this._clock.getElapsedTime() * 1e3;
9062
+
9063
+ }
9064
+
9016
9065
  }
9017
9066
 
9018
9067
  class EquirectCamera extends three.Camera {
@@ -9027,6 +9076,8 @@ bool bvhIntersectFogVolumeHit(
9027
9076
 
9028
9077
  }
9029
9078
 
9079
+ // TODO: this should extend IESSpotLight, but the class is only exported from "three/webgpu" and
9080
+ // the node library resolves light nodes by exact constructor so a subclass would not rasterize
9030
9081
  class PhysicalSpotLight extends three.SpotLight {
9031
9082
 
9032
9083
  constructor( ...args ) {
@@ -9357,8 +9408,211 @@ bool bvhIntersectFogVolumeHit(
9357
9408
 
9358
9409
  }
9359
9410
 
9411
+ class AmbientOcclusionMaterial extends MaterialBase {
9412
+
9413
+ get normalMap() {
9414
+
9415
+ return this.uniforms.normalMap.value;
9416
+
9417
+ }
9418
+
9419
+ set normalMap( v ) {
9420
+
9421
+ this.uniforms.normalMap.value = v;
9422
+ this.setDefine( 'USE_NORMALMAP', v ? null : '' );
9423
+
9424
+ }
9425
+
9426
+ get normalMapType() {
9427
+
9428
+ return three.TangentSpaceNormalMap;
9429
+
9430
+ }
9431
+
9432
+ set normalMapType( v ) {
9433
+
9434
+ if ( v !== three.TangentSpaceNormalMap ) {
9435
+
9436
+ throw new Error( 'AmbientOcclusionMaterial: Only tangent space normal map are supported' );
9437
+
9438
+ }
9439
+
9440
+ }
9441
+
9442
+ constructor( parameters ) {
9443
+
9444
+ super( {
9445
+
9446
+ defines: {
9447
+ SAMPLES: 10,
9448
+ MATERIAL_PIXELS: MATERIAL_PIXELS,
9449
+ },
9450
+
9451
+ uniforms: {
9452
+ bvh: { value: new threeMeshBvh.MeshBVHUniformStruct() },
9453
+ radius: { value: 1.0 },
9454
+ seed: { value: 0 },
9455
+
9456
+ normalMap: { value: null },
9457
+ normalScale: { value: new three.Vector2( 1, 1 ) },
9458
+ },
9459
+
9460
+ vertexShader: /* glsl */`
9461
+
9462
+ varying vec3 vNorm;
9463
+ varying vec3 vPos;
9464
+
9465
+ #if defined( USE_NORMALMAP ) && defined( USE_TANGENT )
9466
+
9467
+ varying vec2 vUv;
9468
+ varying vec4 vTan;
9469
+
9470
+ #endif
9471
+
9472
+ void main() {
9473
+
9474
+ vec4 mvPosition = vec4( position, 1.0 );
9475
+ mvPosition = modelViewMatrix * mvPosition;
9476
+ gl_Position = projectionMatrix * mvPosition;
9477
+
9478
+ mat3 modelNormalMatrix = transpose( inverse( mat3( modelMatrix ) ) );
9479
+ vNorm = normalize( modelNormalMatrix * normal );
9480
+ vPos = ( modelMatrix * vec4( position, 1.0 ) ).xyz;
9481
+
9482
+ #if defined( USE_NORMALMAP ) && defined( USE_TANGENT )
9483
+
9484
+ vUv = uv;
9485
+ vTan = tangent;
9486
+
9487
+ #endif
9488
+
9489
+ }
9490
+
9491
+ `,
9492
+
9493
+ fragmentShader: /* glsl */`
9494
+ #define RAY_OFFSET 1e-4
9495
+
9496
+ precision highp isampler2D;
9497
+ precision highp usampler2D;
9498
+ precision highp sampler2DArray;
9499
+ #include <common>
9500
+ #include <cube_uv_reflection_fragment>
9501
+
9502
+ // bvh
9503
+ ${ threeMeshBvh.BVHShaderGLSL.common_functions }
9504
+ ${ threeMeshBvh.BVHShaderGLSL.bvh_struct_definitions }
9505
+ ${ threeMeshBvh.BVHShaderGLSL.bvh_ray_functions }
9506
+
9507
+ // uniform structs
9508
+ ${ material_struct }
9509
+
9510
+ // rand
9511
+ ${ pcg_functions }
9512
+
9513
+ // common
9514
+ ${ shape_sampling_functions }
9515
+
9516
+ uniform BVH bvh;
9517
+ uniform int seed;
9518
+ uniform float radius;
9519
+
9520
+ varying vec3 vNorm;
9521
+ varying vec3 vPos;
9522
+
9523
+ #if defined(USE_NORMALMAP) && defined(USE_TANGENT)
9524
+
9525
+ uniform sampler2D normalMap;
9526
+ uniform vec2 normalScale;
9527
+ varying vec2 vUv;
9528
+ varying vec4 vTan;
9529
+
9530
+ #endif
9531
+
9532
+ void main() {
9533
+
9534
+ rng_initialize( gl_FragCoord.xy, seed );
9535
+
9536
+ // compute the flat face surface normal
9537
+ vec3 fdx = vec3( dFdx( vPos.x ), dFdx( vPos.y ), dFdx( vPos.z ) );
9538
+ vec3 fdy = vec3( dFdy( vPos.x ), dFdy( vPos.y ), dFdy( vPos.z ) );
9539
+ vec3 faceNormal = normalize( cross( fdx, fdy ) );
9540
+
9541
+ // find the max component to scale the offset to account for floating point error
9542
+ vec3 absPoint = abs( vPos );
9543
+ float maxPoint = max( absPoint.x, max( absPoint.y, absPoint.z ) );
9544
+ vec3 normal = vNorm;
9545
+
9546
+ #if defined( USE_NORMALMAP ) && defined( USE_TANGENT )
9547
+
9548
+ // some provided tangents can be malformed (0, 0, 0) causing the normal to be degenerate
9549
+ // resulting in NaNs and slow path tracing.
9550
+ if ( length( vTan.xyz ) > 0.0 ) {
9551
+
9552
+ vec2 uv = vUv;
9553
+ vec3 tangent = normalize( vTan.xyz );
9554
+ vec3 bitangent = normalize( cross( normal, tangent ) * vTan.w );
9555
+ mat3 vTBN = mat3( tangent, bitangent, normal );
9556
+
9557
+ vec3 texNormal = texture2D( normalMap, uv ).xyz * 2.0 - 1.0;
9558
+ texNormal.xy *= normalScale;
9559
+ normal = vTBN * texNormal;
9560
+
9561
+ }
9562
+
9563
+ #endif
9564
+
9565
+ normal *= gl_FrontFacing ? 1.0 : - 1.0;
9566
+
9567
+ vec3 rayOrigin = vPos + faceNormal * ( maxPoint + 1.0 ) * RAY_OFFSET;
9568
+ float accumulated = 0.0;
9569
+ for ( int i = 0; i < SAMPLES; i ++ ) {
9570
+
9571
+ // sample the cosine weighted hemisphere and discard the sample if it's below
9572
+ // the geometric surface
9573
+ vec3 rayDirection = sampleHemisphere( normalize( normal ), pcgRand4().xy );
9574
+
9575
+ // check if we hit the mesh and its within the specified radius
9576
+ float side = 1.0;
9577
+ float dist = 0.0;
9578
+ vec3 barycoord = vec3( 0.0 );
9579
+ vec3 outNormal = vec3( 0.0 );
9580
+ uvec4 faceIndices = uvec4( 0u );
9581
+
9582
+ // if the ray is above the geometry surface, and it doesn't hit another surface within the specified radius then
9583
+ // we consider it lit
9584
+ if (
9585
+ dot( rayDirection, faceNormal ) > 0.0 &&
9586
+ (
9587
+ ! bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, outNormal, barycoord, side, dist ) ||
9588
+ dist >= radius
9589
+ )
9590
+ ) {
9591
+
9592
+ accumulated += 1.0;
9593
+
9594
+ }
9595
+
9596
+ }
9597
+
9598
+ gl_FragColor.rgb = vec3( accumulated / float( SAMPLES ) );
9599
+ gl_FragColor.a = 1.0;
9600
+
9601
+ }
9602
+
9603
+ `
9604
+
9605
+ } );
9606
+
9607
+ this.setValues( parameters );
9608
+
9609
+ }
9610
+
9611
+ }
9612
+
9360
9613
  // core
9361
9614
 
9615
+ exports.AmbientOcclusionMaterial = AmbientOcclusionMaterial;
9362
9616
  exports.BlurredEnvMapGenerator = BlurredEnvMapGenerator;
9363
9617
  exports.DenoiseMaterial = DenoiseMaterial;
9364
9618
  exports.DynamicPathTracingSceneGenerator = DynamicPathTracingSceneGenerator;