three-gpu-pathtracer 0.0.16 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +3 -1
  2. package/build/index.module.js +1202 -471
  3. package/build/index.module.js.map +1 -1
  4. package/build/index.umd.cjs +1200 -470
  5. package/build/index.umd.cjs.map +1 -1
  6. package/package.json +5 -6
  7. package/src/core/DynamicPathTracingSceneGenerator.js +80 -35
  8. package/src/core/PathTracingRenderer.js +36 -38
  9. package/src/core/PathTracingSceneGenerator.js +11 -55
  10. package/src/materials/debug/GraphMaterial.js +1 -1
  11. package/src/materials/fullscreen/AlphaDisplayMaterial.js +1 -1
  12. package/src/materials/fullscreen/DenoiseMaterial.js +1 -1
  13. package/src/materials/fullscreen/GradientMapMaterial.js +1 -1
  14. package/src/materials/pathtracing/LambertPathTracingMaterial.js +5 -4
  15. package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +87 -50
  16. package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +21 -20
  17. package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +2 -2
  18. package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +12 -8
  19. package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +3 -2
  20. package/src/materials/pathtracing/glsl/traceScene.glsl.js +11 -13
  21. package/src/materials/surface/AmbientOcclusionMaterial.js +4 -3
  22. package/src/shader/bsdf/bsdfSampling.glsl.js +21 -17
  23. package/src/shader/common/bvhAnyHit.glsl.js +2 -2
  24. package/src/shader/common/fresnel.glsl.js +15 -9
  25. package/src/shader/common/intersectShapes.glsl.js +2 -2
  26. package/src/shader/rand/pcg.glsl.js +4 -4
  27. package/src/shader/rand/sobol.glsl.js +3 -3
  28. package/src/shader/rand/stratifiedTexture.glsl.js +45 -0
  29. package/src/shader/sampling/equirectSampling.glsl.js +10 -10
  30. package/src/shader/sampling/lightSampling.glsl.js +30 -37
  31. package/src/shader/structs/fogMaterialBvh.glsl.js +2 -2
  32. package/src/shader/structs/lightsStruct.glsl.js +15 -6
  33. package/src/shader/structs/materialStruct.glsl.js +16 -15
  34. package/src/textures/BlueNoiseTexture.js +87 -0
  35. package/src/textures/ProceduralEquirectTexture.js +6 -6
  36. package/src/textures/blueNoise/BlueNoiseGenerator.js +115 -0
  37. package/src/textures/blueNoise/BlueNoiseSamples.js +214 -0
  38. package/src/textures/blueNoise/utils.js +24 -0
  39. package/src/uniforms/EquirectHdrInfoUniform.js +59 -21
  40. package/src/uniforms/IESProfilesTexture.js +2 -2
  41. package/src/uniforms/LightsInfoUniformStruct.js +15 -9
  42. package/src/uniforms/MaterialsTexture.js +3 -1
  43. package/src/uniforms/RenderTarget2DArray.js +50 -3
  44. package/src/uniforms/StratifiedSamplesTexture.js +49 -0
  45. package/src/uniforms/stratified/StratifiedSampler.js +73 -0
  46. package/src/uniforms/stratified/StratifiedSamplerCombined.js +59 -0
  47. package/src/uniforms/utils.js +1 -1
  48. package/src/utils/BlurredEnvMapGenerator.js +4 -4
  49. package/src/utils/GeometryPreparationUtils.js +8 -95
  50. package/src/utils/IESLoader.js +7 -5
  51. package/src/utils/TextureUtils.js +15 -0
  52. package/src/workers/PathTracingSceneWorker.js +18 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-gpu-pathtracer",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "Path tracing renderer and utilities for three.js built on top of three-mesh-bvh.",
5
5
  "module": "src/index.js",
6
6
  "main": "build/index.umd.cjs",
@@ -28,9 +28,6 @@
28
28
  "path",
29
29
  "tracer"
30
30
  ],
31
- "dependencies": {
32
- "three-mesh-bvh": "^0.5.19"
33
- },
34
31
  "devDependencies": {
35
32
  "@lookingglass/webxr": "^0.3.1",
36
33
  "canvas-capture": "^2.0.5",
@@ -44,11 +41,13 @@
44
41
  "puppeteer": "^15.4.0",
45
42
  "rollup": "^2.70.0",
46
43
  "simple-git": "^3.10.0",
47
- "three": "^0.147.0",
44
+ "three": "^0.160.0",
45
+ "three-mesh-bvh": "0.7.2",
48
46
  "yargs": "^17.5.1"
49
47
  },
50
48
  "peerDependencies": {
51
- "three": ">=0.139.2",
49
+ "three": ">=0.151.0",
50
+ "three-mesh-bvh": "0.7.0",
52
51
  "xatlas-web": "^0.1.0"
53
52
  },
54
53
  "scripts": {
@@ -1,7 +1,16 @@
1
- import { BufferGeometry } from 'three';
2
- import { StaticGeometryGenerator, MeshBVH } from 'three-mesh-bvh';
1
+ import { BufferGeometry, MeshBasicMaterial, BufferAttribute, Mesh } from 'three';
2
+ import { StaticGeometryGenerator, MeshBVH, SAH } from 'three-mesh-bvh';
3
3
  import { setCommonAttributes, getGroupMaterialIndicesAttribute } from '../utils/GeometryPreparationUtils.js';
4
4
 
5
+ const dummyMaterial = new MeshBasicMaterial();
6
+ export function getDummyMesh() {
7
+
8
+ const emptyGeometry = new BufferGeometry();
9
+ emptyGeometry.setAttribute( 'position', new BufferAttribute( new Float32Array( 9 ), 3 ) );
10
+ return new Mesh( emptyGeometry, dummyMaterial );
11
+
12
+ }
13
+
5
14
  export class DynamicPathTracingSceneGenerator {
6
15
 
7
16
  get initialized() {
@@ -10,9 +19,29 @@ export class DynamicPathTracingSceneGenerator {
10
19
 
11
20
  }
12
21
 
13
- constructor( scene ) {
22
+ constructor( objects ) {
23
+
24
+ // ensure the objects is an array
25
+ if ( ! Array.isArray( objects ) ) {
26
+
27
+ objects = [ objects ];
28
+
29
+ }
30
+
31
+ // use a dummy object for a fallback
32
+ const finalObjects = [ ...objects ];
33
+ if ( finalObjects.length === 0 ) {
34
+
35
+ finalObjects.push( getDummyMesh() );
36
+
37
+ }
38
+
39
+ // options
40
+ this.bvhOptions = {};
41
+ this.attributes = [ 'position', 'normal', 'tangent', 'color', 'uv', 'uv2' ];
14
42
 
15
- this.objects = Array.isArray( scene ) ? scene : [ scene ];
43
+ // state
44
+ this.objects = finalObjects;
16
45
  this.bvh = null;
17
46
  this.geometry = new BufferGeometry();
18
47
  this.materials = null;
@@ -36,59 +65,75 @@ export class DynamicPathTracingSceneGenerator {
36
65
 
37
66
  dispose() {}
38
67
 
39
- generate() {
68
+ prepScene() {
40
69
 
41
- const { objects, staticGeometryGenerator, geometry } = this;
42
- if ( this.bvh === null ) {
70
+ if ( this.bvh !== null ) {
43
71
 
44
- const attributes = [ 'position', 'normal', 'tangent', 'uv', 'color' ];
72
+ return;
45
73
 
46
- for ( let i = 0, l = objects.length; i < l; i ++ ) {
74
+ }
47
75
 
48
- objects[ i ].traverse( c => {
76
+ const { objects, staticGeometryGenerator, geometry, lights, attributes } = this;
77
+ for ( let i = 0, l = objects.length; i < l; i ++ ) {
49
78
 
50
- if ( c.isMesh ) {
79
+ objects[ i ].traverse( c => {
51
80
 
52
- const normalMapRequired = ! ! c.material.normalMap;
53
- setCommonAttributes( c.geometry, { attributes, normalMapRequired } );
81
+ if ( c.isMesh ) {
54
82
 
55
- } else if ( c.isRectAreaLight || c.isSpotLight ) {
83
+ const normalMapRequired = ! ! c.material.normalMap;
84
+ setCommonAttributes( c.geometry, { attributes, normalMapRequired } );
56
85
 
57
- this.lights.push( c );
86
+ } else if (
87
+ c.isRectAreaLight ||
88
+ c.isSpotLight ||
89
+ c.isPointLight ||
90
+ c.isDirectionalLight
91
+ ) {
58
92
 
59
- }
93
+ lights.push( c );
60
94
 
61
- } );
95
+ }
62
96
 
63
- }
97
+ } );
64
98
 
65
- const textureSet = new Set();
66
- const materials = staticGeometryGenerator.getMaterials();
67
- materials.forEach( material => {
99
+ }
68
100
 
69
- for ( const key in material ) {
101
+ const textureSet = new Set();
102
+ const materials = staticGeometryGenerator.getMaterials();
103
+ materials.forEach( material => {
70
104
 
71
- const value = material[ key ];
72
- if ( value && value.isTexture ) {
105
+ for ( const key in material ) {
73
106
 
74
- textureSet.add( value );
107
+ const value = material[ key ];
108
+ if ( value && value.isTexture ) {
75
109
 
76
- }
110
+ textureSet.add( value );
77
111
 
78
112
  }
79
113
 
80
- } );
114
+ }
81
115
 
82
- staticGeometryGenerator.attributes = attributes;
83
- staticGeometryGenerator.generate( geometry );
116
+ } );
117
+
118
+ staticGeometryGenerator.attributes = attributes;
119
+ staticGeometryGenerator.generate( geometry );
84
120
 
85
- const materialIndexAttribute = getGroupMaterialIndicesAttribute( geometry, materials, materials );
86
- geometry.setAttribute( 'materialIndex', materialIndexAttribute );
87
- geometry.clearGroups();
121
+ const materialIndexAttribute = getGroupMaterialIndicesAttribute( geometry, materials, materials );
122
+ geometry.setAttribute( 'materialIndex', materialIndexAttribute );
123
+ geometry.clearGroups();
124
+
125
+ this.materials = materials;
126
+ this.textures = Array.from( textureSet );
127
+
128
+ }
129
+
130
+ generate() {
131
+
132
+ const { objects, staticGeometryGenerator, geometry, bvhOptions } = this;
133
+ if ( this.bvh === null ) {
88
134
 
89
- this.bvh = new MeshBVH( geometry );
90
- this.materials = materials;
91
- this.textures = Array.from( textureSet );
135
+ this.prepScene();
136
+ this.bvh = new MeshBVH( geometry, { strategy: SAH, maxLeafTris: 1, ...bvhOptions } );
92
137
 
93
138
  return {
94
139
  lights: this.lights,
@@ -1,11 +1,8 @@
1
- import { RGBAFormat, FloatType, Color, Vector2, WebGLRenderTarget, NoBlending, NormalBlending, Vector4 } from 'three';
1
+ import { RGBAFormat, FloatType, HalfFloatType, Color, Vector2, WebGLRenderTarget, NoBlending, NormalBlending, Vector4 } from 'three';
2
2
  import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
3
3
  import { BlendMaterial } from '../materials/fullscreen/BlendMaterial.js';
4
4
  import { SobolNumberMapGenerator } from '../utils/SobolNumberMapGenerator.js';
5
5
 
6
- const _scissor = new Vector4();
7
- const _viewport = new Vector4();
8
-
9
6
  function* renderTask() {
10
7
 
11
8
  const {
@@ -47,12 +44,21 @@ function* renderTask() {
47
44
  const h = _primaryTarget.height;
48
45
  material.resolution.set( w * subW, h * subH );
49
46
  material.sobolTexture = _sobolTarget.texture;
47
+ material.stratifiedTexture.init( 20, material.bounces + material.transmissiveBounces + 5 );
48
+ material.stratifiedTexture.next();
50
49
  material.seed ++;
51
50
 
52
51
  const tilesX = this.tiles.x || 1;
53
52
  const tilesY = this.tiles.y || 1;
54
53
  const totalTiles = tilesX * tilesY;
55
- const dprInv = ( 1 / _renderer.getPixelRatio() );
54
+
55
+ const pxSubW = Math.ceil( w * subW );
56
+ const pxSubH = Math.ceil( h * subH );
57
+ const pxSubX = Math.floor( subX * w );
58
+ const pxSubY = Math.floor( subY * h );
59
+
60
+ const pxTileW = Math.ceil( pxSubW / tilesX );
61
+ const pxTileH = Math.ceil( pxSubH / tilesY );
56
62
 
57
63
  for ( let y = 0; y < tilesY; y ++ ) {
58
64
 
@@ -101,40 +107,28 @@ function* renderTask() {
101
107
 
102
108
  }
103
109
 
110
+ // set the scissor and the viewport on the render target
111
+ // note that when using the webgl renderer set viewport the device pixel ratio
112
+ // is multiplied into the field causing some pixels to not be rendered
113
+ const reverseTy = tilesY - ty - 1;
114
+ _primaryTarget.scissor.set(
115
+ pxSubX + tx * pxTileW,
116
+ pxSubY + reverseTy * pxTileH,
117
+ Math.min( pxTileW, pxSubW - tx * pxTileW ),
118
+ Math.min( pxTileH, pxSubH - reverseTy * pxTileH ),
119
+ );
120
+
121
+ _primaryTarget.viewport.set(
122
+ pxSubX,
123
+ pxSubY,
124
+ pxSubW,
125
+ pxSubH,
126
+ );
127
+
104
128
  // three.js renderer takes values relative to the current pixel ratio
105
129
  _renderer.setRenderTarget( _primaryTarget );
106
130
  _renderer.setScissorTest( true );
107
131
 
108
- // set the scissor window for a subtile
109
- _scissor.x = tx * w / tilesX;
110
- _scissor.y = ( tilesY - ty - 1 ) * h / tilesY;
111
- _scissor.z = w / tilesX;
112
- _scissor.w = h / tilesY;
113
-
114
- // adjust for the subframe
115
- _scissor.x = subX * w + subW * _scissor.x;
116
- _scissor.y = subY * h + subH * _scissor.y;
117
- _scissor.z = subW * _scissor.z;
118
- _scissor.w = subH * _scissor.w;
119
-
120
- // round for floating point cases
121
- _scissor.x = _scissor.x;
122
- _scissor.y = _scissor.y;
123
- _scissor.z = _scissor.z;
124
- _scissor.w = _scissor.w;
125
-
126
- // multiply inverse of DPR in because threes multiplies it in
127
- _scissor.multiplyScalar( dprInv ).ceil();
128
-
129
- _viewport.x = subX * w;
130
- _viewport.y = subY * h;
131
- _viewport.z = subW * w;
132
- _viewport.w = subH * h;
133
- _viewport.multiplyScalar( dprInv ).ceil();
134
-
135
- _renderer.setScissor( _scissor );
136
- _renderer.setViewport( _viewport );
137
-
138
132
  _renderer.autoClear = false;
139
133
  _fsQuad.render( _renderer );
140
134
 
@@ -250,18 +244,22 @@ export class PathTracingRenderer {
250
244
  this._currentTile = 0;
251
245
 
252
246
  this._sobolTarget = new SobolNumberMapGenerator().generate( renderer );
247
+
248
+ // will be null if extension not supported
249
+ const floatLinearExtensionSupported = renderer.extensions.get( 'OES_texture_float_linear' );
250
+
253
251
  this._primaryTarget = new WebGLRenderTarget( 1, 1, {
254
252
  format: RGBAFormat,
255
- type: FloatType,
253
+ type: floatLinearExtensionSupported ? FloatType : HalfFloatType,
256
254
  } );
257
255
  this._blendTargets = [
258
256
  new WebGLRenderTarget( 1, 1, {
259
257
  format: RGBAFormat,
260
- type: FloatType,
258
+ type: floatLinearExtensionSupported ? FloatType : HalfFloatType,
261
259
  } ),
262
260
  new WebGLRenderTarget( 1, 1, {
263
261
  format: RGBAFormat,
264
- type: FloatType,
262
+ type: floatLinearExtensionSupported ? FloatType : HalfFloatType,
265
263
  } ),
266
264
  ];
267
265
 
@@ -1,68 +1,24 @@
1
- import { Mesh } from 'three';
2
- import { SAH, MeshBVH, StaticGeometryGenerator } from 'three-mesh-bvh';
3
- import { mergeMeshes } from '../utils/GeometryPreparationUtils.js';
1
+ import { DynamicPathTracingSceneGenerator } from './DynamicPathTracingSceneGenerator.js';
4
2
 
5
3
  export class PathTracingSceneGenerator {
6
4
 
7
- prepScene( scene ) {
8
-
9
- scene = Array.isArray( scene ) ? scene : [ scene ];
10
-
11
- const meshes = [];
12
- const lights = [];
13
-
14
- for ( let i = 0, l = scene.length; i < l; i ++ ) {
15
-
16
- scene[ i ].traverseVisible( c => {
17
-
18
- if ( c.isSkinnedMesh || c.isMesh && c.morphTargetInfluences ) {
19
-
20
- const generator = new StaticGeometryGenerator( c );
21
- generator.attributes = [ 'position', 'color', 'normal', 'tangent', 'uv', 'uv2' ];
22
- generator.applyWorldTransforms = false;
23
- const mesh = new Mesh(
24
- generator.generate(),
25
- c.material,
26
- );
27
- mesh.matrixWorld.copy( c.matrixWorld );
28
- mesh.matrix.copy( c.matrixWorld );
29
- mesh.matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
30
- meshes.push( mesh );
31
-
32
- } else if ( c.isMesh ) {
33
-
34
- meshes.push( c );
5
+ generate( scene, options = {} ) {
35
6
 
36
- } else if ( c.isRectAreaLight || c.isSpotLight || c.isDirectionalLight || c.isPointLight ) {
7
+ // ensure scene transforms are up to date
8
+ // TODO: remove this?
9
+ if ( Array.isArray( scene ) ) {
37
10
 
38
- lights.push( c );
11
+ scene.forEach( s => s.updateMatrixWorld( true ) );
39
12
 
40
- }
13
+ } else {
41
14
 
42
- } );
15
+ scene.updateMatrixWorld( true );
43
16
 
44
17
  }
45
18
 
46
- return {
47
- ...mergeMeshes( meshes, {
48
- attributes: [ 'position', 'normal', 'tangent', 'uv', 'color' ],
49
- } ),
50
- lights,
51
- };
52
-
53
- }
54
-
55
- generate( scene, options = {} ) {
56
-
57
- const { materials, textures, geometry, lights } = this.prepScene( scene );
58
- const bvhOptions = { strategy: SAH, ...options, maxLeafTris: 1 };
59
- return {
60
- scene,
61
- materials,
62
- textures,
63
- lights,
64
- bvh: new MeshBVH( geometry, bvhOptions ),
65
- };
19
+ const generator = new DynamicPathTracingSceneGenerator( scene );
20
+ generator.bvhOptions = options;
21
+ return generator.generate();
66
22
 
67
23
  }
68
24
 
@@ -198,7 +198,7 @@ export class GraphMaterial extends MaterialBase {
198
198
 
199
199
  }
200
200
 
201
- #include <encodings_fragment>
201
+ #include <colorspace_fragment>
202
202
 
203
203
  }
204
204
 
@@ -37,7 +37,7 @@ export class AlphaDisplayMaterial extends MaterialBase {
37
37
  gl_FragColor = vec4( texture( map, vUv ).a );
38
38
  gl_FragColor.a = 1.0;
39
39
 
40
- #include <encodings_fragment>
40
+ #include <colorspace_fragment>
41
41
 
42
42
  }`
43
43
 
@@ -126,7 +126,7 @@ export class DenoiseMaterial extends MaterialBase {
126
126
 
127
127
  gl_FragColor = smartDeNoise( map, vec2( vUv.x, vUv.y ), sigma, kSigma, threshold );
128
128
  #include <tonemapping_fragment>
129
- #include <encodings_fragment>
129
+ #include <colorspace_fragment>
130
130
  #include <premultiplied_alpha_fragment>
131
131
 
132
132
  }
@@ -69,7 +69,7 @@ export class GradientMapMaterial extends MaterialBase {
69
69
  gl_FragColor.rgb = vec3( mix( minColor, maxColor, t ) );
70
70
  gl_FragColor.a = 1.0;
71
71
 
72
- #include <encodings_fragment>
72
+ #include <colorspace_fragment>
73
73
 
74
74
  }`,
75
75
 
@@ -2,7 +2,7 @@ import { Matrix4, Color } from 'three';
2
2
  import { MaterialBase } from '../MaterialBase.js';
3
3
  import {
4
4
  MeshBVHUniformStruct, FloatVertexAttributeTexture, UIntVertexAttributeTexture,
5
- shaderStructs, shaderIntersectFunction,
5
+ BVHShaderGLSL,
6
6
  } from 'three-mesh-bvh';
7
7
 
8
8
  // uniforms
@@ -85,8 +85,9 @@ export class LambertPathTracingMaterial extends MaterialBase {
85
85
  #include <common>
86
86
  #include <cube_uv_reflection_fragment>
87
87
 
88
- ${ shaderStructs }
89
- ${ shaderIntersectFunction }
88
+ ${ BVHShaderGLSL.common_functions }
89
+ ${ BVHShaderGLSL.bvh_struct_definitions }
90
+ ${ BVHShaderGLSL.bvh_ray_functions }
90
91
 
91
92
  // uniform structs
92
93
  ${ materialStructGLSL }
@@ -193,7 +194,7 @@ export class LambertPathTracingMaterial extends MaterialBase {
193
194
  uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
194
195
  Material material = materials[ materialIndex ];
195
196
 
196
- if ( material.opacity < rand() ) {
197
+ if ( material.opacity < pcgRand() ) {
197
198
 
198
199
  vec3 point = rayOrigin + rayDirection * dist;
199
200
  rayOrigin += rayDirection * dist - faceNormal * RAY_OFFSET;