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
@@ -2,11 +2,11 @@ import {
2
2
  WebGLArrayRenderTarget,
3
3
  RGBAFormat,
4
4
  UnsignedByteType,
5
- MeshBasicMaterial,
6
5
  Color,
7
6
  RepeatWrapping,
8
7
  LinearFilter,
9
8
  NoToneMapping,
9
+ ShaderMaterial,
10
10
  } from 'three';
11
11
  import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
12
12
  import { reduceTexturesToUniqueSources } from './utils.js';
@@ -31,7 +31,7 @@ export class RenderTarget2DArray extends WebGLArrayRenderTarget {
31
31
 
32
32
  };
33
33
 
34
- const fsQuad = new FullScreenQuad( new MeshBasicMaterial() );
34
+ const fsQuad = new FullScreenQuad( new CopyMaterial() );
35
35
  this.fsQuad = fsQuad;
36
36
 
37
37
  }
@@ -66,7 +66,6 @@ export class RenderTarget2DArray extends WebGLArrayRenderTarget {
66
66
  texture.matrix.identity();
67
67
 
68
68
  fsQuad.material.map = texture;
69
- fsQuad.material.transparent = true;
70
69
 
71
70
  renderer.setRenderTarget( this, i );
72
71
  fsQuad.render( renderer );
@@ -95,3 +94,51 @@ export class RenderTarget2DArray extends WebGLArrayRenderTarget {
95
94
  }
96
95
 
97
96
  }
97
+
98
+ class CopyMaterial extends ShaderMaterial {
99
+
100
+ get map() {
101
+
102
+ return this.uniforms.map.value;
103
+
104
+ }
105
+ set map( v ) {
106
+
107
+ this.uniforms.map.value = v;
108
+
109
+ }
110
+
111
+ constructor() {
112
+
113
+ super( {
114
+ uniforms: {
115
+
116
+ map: { value: null },
117
+
118
+ },
119
+
120
+ vertexShader: /* glsl */`
121
+ varying vec2 vUv;
122
+ void main() {
123
+
124
+ vUv = uv;
125
+ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
126
+
127
+ }
128
+ `,
129
+
130
+ fragmentShader: /* glsl */`
131
+ uniform sampler2D map;
132
+ varying vec2 vUv;
133
+ void main() {
134
+
135
+ gl_FragColor = texture2D( map, vUv );
136
+
137
+ }
138
+ `
139
+ } );
140
+
141
+ }
142
+
143
+
144
+ }
@@ -0,0 +1,49 @@
1
+ import { DataTexture, FloatType, NearestFilter, RGBAFormat } from 'three';
2
+ import { StratifiedSamplerCombined } from './stratified/StratifiedSamplerCombined.js';
3
+
4
+ export class StratifiedSamplesTexture extends DataTexture {
5
+
6
+ constructor( count = 1, depth = 1, strata = 8 ) {
7
+
8
+ super( new Float32Array( 1 ), 1, 1, RGBAFormat, FloatType );
9
+ this.minFilter = NearestFilter;
10
+ this.magFilter = NearestFilter;
11
+
12
+ this.strata = strata;
13
+ this.sampler = null;
14
+
15
+ this.init( count, depth, strata );
16
+
17
+ }
18
+
19
+ init( count, depth, strata = this.strata ) {
20
+
21
+ const { image } = this;
22
+ if ( image.width === depth && image.height === count ) {
23
+
24
+ return;
25
+
26
+ }
27
+
28
+ const dimensions = new Array( count * depth ).fill( 4 );
29
+ const sampler = new StratifiedSamplerCombined( strata, dimensions );
30
+
31
+ image.width = depth;
32
+ image.height = count;
33
+ image.data = sampler.samples;
34
+
35
+ this.sampler = sampler;
36
+
37
+ this.dispose();
38
+ this.next();
39
+
40
+ }
41
+
42
+ next() {
43
+
44
+ this.sampler.next();
45
+ this.needsUpdate = true;
46
+
47
+ }
48
+
49
+ }
@@ -0,0 +1,73 @@
1
+ // Stratified Sampling based on implementation from hoverinc pathtracer
2
+ // - https://github.com/hoverinc/ray-tracing-renderer
3
+ // - http://www.pbr-book.org/3ed-2018/Sampling_and_Reconstruction/Stratified_Sampling.html
4
+
5
+ export function shuffle( arr ) {
6
+
7
+ for ( let i = arr.length - 1; i > 0; i -- ) {
8
+
9
+ const j = Math.floor( Math.random() * ( i + 1 ) );
10
+ const x = arr[ i ];
11
+ arr[ i ] = arr[ j ];
12
+ arr[ j ] = x;
13
+
14
+ }
15
+
16
+ return arr;
17
+
18
+ }
19
+
20
+ // strataCount : The number of bins per dimension
21
+ // dimensions : The number of dimensions to generate stratified values for
22
+ export class StratifiedSampler {
23
+
24
+ constructor( strataCount, dimensions ) {
25
+
26
+ const l = strataCount ** dimensions;
27
+ const strata = new Uint16Array( l );
28
+ let index = l;
29
+
30
+ // each integer represents a statum bin
31
+ for ( let i = 0; i < l; i ++ ) {
32
+
33
+ strata[ i ] = i;
34
+
35
+ }
36
+
37
+ this.samples = new Float32Array( dimensions );
38
+
39
+ this.strataCount = strataCount;
40
+
41
+ this.restart = function () {
42
+
43
+ index = 0;
44
+
45
+ };
46
+
47
+ this.next = function () {
48
+
49
+ const { samples } = this;
50
+
51
+ if ( index >= strata.length ) {
52
+
53
+ shuffle( strata );
54
+ this.restart();
55
+
56
+ }
57
+
58
+ let stratum = strata[ index ++ ];
59
+
60
+ for ( let i = 0; i < dimensions; i ++ ) {
61
+
62
+ samples[ i ] = ( stratum % strataCount + Math.random() ) / strataCount;
63
+ stratum = Math.floor( stratum / strataCount );
64
+
65
+ }
66
+
67
+ return samples;
68
+
69
+ };
70
+
71
+ }
72
+
73
+ }
@@ -0,0 +1,59 @@
1
+ // Stratified Sampling based on implementation from hoverinc pathtracer
2
+ // - https://github.com/hoverinc/ray-tracing-renderer
3
+ // - http://www.pbr-book.org/3ed-2018/Sampling_and_Reconstruction/Stratified_Sampling.html
4
+
5
+ import { StratifiedSampler } from './StratifiedSampler.js';
6
+
7
+ // Stratified set of data with each tuple stratified separately and combined
8
+ export class StratifiedSamplerCombined {
9
+
10
+ constructor( strataCount, listOfDimensions ) {
11
+
12
+ let totalDim = 0;
13
+ for ( const dim of listOfDimensions ) {
14
+
15
+ totalDim += dim;
16
+
17
+ }
18
+
19
+ const combined = new Float32Array( totalDim );
20
+ const strataObjs = [];
21
+ let offset = 0;
22
+ for ( const dim of listOfDimensions ) {
23
+
24
+ const sampler = new StratifiedSampler( strataCount, dim );
25
+ sampler.samples = new Float32Array( combined.buffer, offset, sampler.samples.length );
26
+ offset += sampler.samples.length * 4;
27
+ strataObjs.push( sampler );
28
+
29
+ }
30
+
31
+ this.samples = combined;
32
+
33
+ this.strataCount = strataCount;
34
+
35
+ this.next = function () {
36
+
37
+ for ( const strata of strataObjs ) {
38
+
39
+ strata.next();
40
+
41
+ }
42
+
43
+ return combined;
44
+
45
+ };
46
+
47
+ this.restart = function () {
48
+
49
+ for ( const strata of strataObjs ) {
50
+
51
+ strata.restart();
52
+
53
+ }
54
+
55
+ };
56
+
57
+ }
58
+
59
+ }
@@ -2,7 +2,7 @@
2
2
  // when rendering each texture to the texture array they must have a consistent color space.
3
3
  export function getTextureHash( t ) {
4
4
 
5
- return `${ t.source.uuid }:${ t.encoding }`;
5
+ return `${ t.source.uuid }:${ t.colorSpace }`;
6
6
 
7
7
  }
8
8
 
@@ -1,4 +1,4 @@
1
- import { WebGLRenderTarget, RGBAFormat, FloatType, PMREMGenerator, DataTexture, EquirectangularReflectionMapping } from 'three';
1
+ import { WebGLRenderTarget, RGBAFormat, HalfFloatType, PMREMGenerator, DataTexture, EquirectangularReflectionMapping } from 'three';
2
2
  import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
3
3
  import { MaterialBase } from '../materials/MaterialBase.js';
4
4
  import { utilsGLSL } from '../shader/common/utils.glsl.js';
@@ -58,7 +58,7 @@ export class BlurredEnvMapGenerator {
58
58
  this.renderer = renderer;
59
59
  this.pmremGenerator = new PMREMGenerator( renderer );
60
60
  this.copyQuad = new FullScreenQuad( new PMREMCopyMaterial() );
61
- this.renderTarget = new WebGLRenderTarget( 1, 1, { type: FloatType, format: RGBAFormat } );
61
+ this.renderTarget = new WebGLRenderTarget( 1, 1, { type: HalfFloatType, format: RGBAFormat } );
62
62
 
63
63
  }
64
64
 
@@ -95,10 +95,10 @@ export class BlurredEnvMapGenerator {
95
95
  renderer.autoClear = prevClear;
96
96
 
97
97
  // read the data back
98
- const buffer = new Float32Array( width * height * 4 );
98
+ const buffer = new Uint16Array( width * height * 4 );
99
99
  renderer.readRenderTargetPixels( renderTarget, 0, 0, width, height, buffer );
100
100
 
101
- const result = new DataTexture( buffer, width, height, RGBAFormat, FloatType );
101
+ const result = new DataTexture( buffer, width, height, RGBAFormat, HalfFloatType );
102
102
  result.minFilter = texture.minFilter;
103
103
  result.magFilter = texture.magFilter;
104
104
  result.wrapS = texture.wrapS;
@@ -1,5 +1,5 @@
1
1
  import { BufferAttribute } from 'three';
2
- import { mergeBufferGeometries, mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
2
+ import { mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
3
3
  export function getGroupMaterialIndicesAttribute( geometry, materials, allMaterials ) {
4
4
 
5
5
  const indexAttr = geometry.index;
@@ -54,25 +54,6 @@ export function getGroupMaterialIndicesAttribute( geometry, materials, allMateri
54
54
 
55
55
  }
56
56
 
57
- export function trimToAttributes( geometry, attributes ) {
58
-
59
- // trim any unneeded attributes
60
- if ( attributes ) {
61
-
62
- for ( const key in geometry.attributes ) {
63
-
64
- if ( ! attributes.includes( key ) ) {
65
-
66
- geometry.deleteAttribute( key );
67
-
68
- }
69
-
70
- }
71
-
72
- }
73
-
74
- }
75
-
76
57
  export function setCommonAttributes( geometry, options ) {
77
58
 
78
59
  const { attributes = [], normalMapRequired = false } = options;
@@ -90,6 +71,13 @@ export function setCommonAttributes( geometry, options ) {
90
71
 
91
72
  }
92
73
 
74
+ if ( ! geometry.attributes.uv2 && ( attributes && attributes.includes( 'uv2' ) ) ) {
75
+
76
+ const vertCount = geometry.attributes.position.count;
77
+ geometry.setAttribute( 'uv2', new BufferAttribute( new Float32Array( vertCount * 2 ), 2, false ) );
78
+
79
+ }
80
+
93
81
  if ( ! geometry.attributes.tangent && ( attributes && attributes.includes( 'tangent' ) ) ) {
94
82
 
95
83
  if ( normalMapRequired ) {
@@ -137,78 +125,3 @@ export function setCommonAttributes( geometry, options ) {
137
125
  }
138
126
 
139
127
  }
140
-
141
- export function mergeMeshes( meshes, options = {} ) {
142
-
143
- options = { attributes: null, cloneGeometry: true, ...options };
144
-
145
- const transformedGeometry = [];
146
- const materialSet = new Set();
147
- for ( let i = 0, l = meshes.length; i < l; i ++ ) {
148
-
149
- // save any materials
150
- const mesh = meshes[ i ];
151
- if ( mesh.visible === false ) continue;
152
-
153
- if ( Array.isArray( mesh.material ) ) {
154
-
155
- mesh.material.forEach( m => materialSet.add( m ) );
156
-
157
- } else {
158
-
159
- materialSet.add( mesh.material );
160
-
161
- }
162
-
163
- }
164
-
165
- const materials = Array.from( materialSet );
166
- for ( let i = 0, l = meshes.length; i < l; i ++ ) {
167
-
168
- // ensure the matrix world is up to date
169
- const mesh = meshes[ i ];
170
- if ( mesh.visible === false ) continue;
171
-
172
- mesh.updateMatrixWorld();
173
-
174
- // apply the matrix world to the geometry
175
- const originalGeometry = meshes[ i ].geometry;
176
- const geometry = options.cloneGeometry ? originalGeometry.clone() : originalGeometry;
177
- geometry.applyMatrix4( mesh.matrixWorld );
178
-
179
- // ensure our geometry has common attributes
180
- setCommonAttributes( geometry, {
181
- attributes: options.attributes,
182
- normalMapRequired: ! ! mesh.material.normalMap,
183
- } );
184
- trimToAttributes( geometry, options.attributes );
185
-
186
- // create the material index attribute
187
- const materialIndexAttribute = getGroupMaterialIndicesAttribute( geometry, mesh.material, materials );
188
- geometry.setAttribute( 'materialIndex', materialIndexAttribute );
189
-
190
- transformedGeometry.push( geometry );
191
-
192
- }
193
-
194
- const textureSet = new Set();
195
- materials.forEach( material => {
196
-
197
- for ( const key in material ) {
198
-
199
- const value = material[ key ];
200
- if ( value && value.isTexture ) {
201
-
202
- textureSet.add( value );
203
-
204
- }
205
-
206
- }
207
-
208
- } );
209
-
210
- const geometry = mergeBufferGeometries( transformedGeometry, false );
211
- const textures = Array.from( textureSet );
212
- return { geometry, materials, textures };
213
-
214
- }
@@ -1,13 +1,15 @@
1
1
  import {
2
2
  DataTexture,
3
3
  FileLoader,
4
- FloatType,
4
+ HalfFloatType,
5
5
  LinearFilter,
6
6
  RedFormat,
7
7
  MathUtils,
8
8
  Loader,
9
9
  } from 'three';
10
10
 
11
+ import { toHalfFloatArray } from './TextureUtils.js';
12
+
11
13
  function IESLamp( text ) {
12
14
 
13
15
  const _self = this;
@@ -286,7 +288,7 @@ export class IESLoader extends Loader {
286
288
  loader.setPath( this.path );
287
289
  loader.setRequestHeader( this.requestHeader );
288
290
 
289
- const texture = new DataTexture( null, 360, 180, RedFormat, FloatType );
291
+ const texture = new DataTexture( null, 360, 180, RedFormat, HalfFloatType );
290
292
  texture.minFilter = LinearFilter;
291
293
  texture.magFilter = LinearFilter;
292
294
 
@@ -294,7 +296,7 @@ export class IESLoader extends Loader {
294
296
 
295
297
  const iesLamp = new IESLamp( text );
296
298
 
297
- texture.image.data = this._getIESValues( iesLamp );
299
+ texture.image.data = toHalfFloatArray( this._getIESValues( iesLamp ) );
298
300
  texture.needsUpdate = true;
299
301
 
300
302
  if ( onLoad !== undefined ) {
@@ -312,10 +314,10 @@ export class IESLoader extends Loader {
312
314
  parse( text ) {
313
315
 
314
316
  const iesLamp = new IESLamp( text );
315
- const texture = new DataTexture( null, 360, 180, RedFormat, FloatType );
317
+ const texture = new DataTexture( null, 360, 180, RedFormat, HalfFloatType );
316
318
  texture.minFilter = LinearFilter;
317
319
  texture.magFilter = LinearFilter;
318
- texture.image.data = this._getIESValues( iesLamp );
320
+ texture.image.data = toHalfFloatArray( this._getIESValues( iesLamp ) );
319
321
  texture.needsUpdate = true;
320
322
 
321
323
  return texture;
@@ -0,0 +1,15 @@
1
+ import { DataUtils } from 'three';
2
+
3
+
4
+ export function toHalfFloatArray( f32Array ) {
5
+
6
+ const f16Array = new Uint16Array( f32Array.length );
7
+ for ( let i = 0, n = f32Array.length; i < n; ++ i ) {
8
+
9
+ f16Array[ i ] = DataUtils.toHalfFloat( f32Array[ i ] );
10
+
11
+ }
12
+
13
+ return f16Array;
14
+
15
+ }
@@ -1,23 +1,34 @@
1
- import { PathTracingSceneGenerator } from '../core/PathTracingSceneGenerator.js';
2
- import { SAH } from 'three-mesh-bvh';
3
1
  import { GenerateMeshBVHWorker } from 'three-mesh-bvh/src/workers/GenerateMeshBVHWorker.js';
2
+ import { DynamicPathTracingSceneGenerator } from '../core/DynamicPathTracingSceneGenerator.js';
4
3
 
5
- export class PathTracingSceneWorker extends PathTracingSceneGenerator {
4
+ export class PathTracingSceneWorker {
6
5
 
7
6
  constructor() {
8
7
 
9
- super();
10
8
  this.bvhGenerator = new GenerateMeshBVHWorker();
11
9
 
12
10
  }
13
11
 
14
12
  generate( scene, options = {} ) {
15
13
 
14
+ // ensure scene transforms are up to date
15
+ // TODO: remove this?
16
+ if ( Array.isArray( scene ) ) {
17
+
18
+ scene.forEach( s => s.updateMatrixWorld( true ) );
19
+
20
+ } else {
21
+
22
+ scene.updateMatrixWorld( true );
23
+
24
+ }
25
+
16
26
  const { bvhGenerator } = this;
17
- const { geometry, materials, textures, lights, spotLights } = this.prepScene( scene );
27
+ const sceneGenerator = new DynamicPathTracingSceneGenerator( scene );
28
+ sceneGenerator.prepScene();
18
29
 
19
- const bvhOptions = { strategy: SAH, ...options, maxLeafTris: 1 };
20
- const bvhPromise = bvhGenerator.generate( geometry, bvhOptions );
30
+ const { geometry, materials, textures, lights } = sceneGenerator;
31
+ const bvhPromise = bvhGenerator.generate( geometry, options );
21
32
  return bvhPromise.then( bvh => {
22
33
 
23
34
  return {
@@ -25,7 +36,6 @@ export class PathTracingSceneWorker extends PathTracingSceneGenerator {
25
36
  materials,
26
37
  textures,
27
38
  lights,
28
- spotLights,
29
39
  bvh,
30
40
  };
31
41