three-gpu-pathtracer 0.0.1 → 0.0.4

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 (36) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +678 -386
  3. package/build/index.module.js +3166 -1690
  4. package/build/index.module.js.map +1 -1
  5. package/build/index.umd.cjs +3176 -1692
  6. package/build/index.umd.cjs.map +1 -1
  7. package/package.json +60 -57
  8. package/src/core/DynamicPathTracingSceneGenerator.js +106 -0
  9. package/src/core/MaterialReducer.js +256 -256
  10. package/src/core/PathTracingRenderer.js +125 -28
  11. package/src/core/PathTracingSceneGenerator.js +52 -46
  12. package/src/core/PhysicalCamera.js +28 -0
  13. package/src/index.js +25 -21
  14. package/src/materials/AlphaDisplayMaterial.js +48 -0
  15. package/src/materials/AmbientOcclusionMaterial.js +197 -197
  16. package/src/materials/BlendMaterial.js +67 -0
  17. package/src/materials/LambertPathTracingMaterial.js +285 -285
  18. package/src/materials/MaterialBase.js +56 -56
  19. package/src/materials/PhysicalPathTracingMaterial.js +684 -370
  20. package/src/shader/shaderEnvMapSampling.js +67 -0
  21. package/src/shader/shaderGGXFunctions.js +108 -107
  22. package/src/shader/shaderMaterialSampling.js +345 -333
  23. package/src/shader/shaderStructs.js +131 -30
  24. package/src/shader/shaderUtils.js +246 -140
  25. package/src/uniforms/EquirectHdrInfoUniform.js +263 -0
  26. package/src/uniforms/MaterialsTexture.js +251 -0
  27. package/src/uniforms/PhysicalCameraUniform.js +36 -0
  28. package/src/uniforms/RenderTarget2DArray.js +93 -80
  29. package/src/utils/BlurredEnvMapGenerator.js +113 -0
  30. package/src/utils/GeometryPreparationUtils.js +194 -172
  31. package/src/utils/UVUnwrapper.js +101 -101
  32. package/src/workers/PathTracingSceneWorker.js +40 -0
  33. package/src/uniforms/EquirectPdfUniform.js +0 -132
  34. package/src/uniforms/MaterialStructArrayUniform.js +0 -18
  35. package/src/uniforms/MaterialStructUniform.js +0 -94
  36. package/src/viewers/PathTracingViewer.js +0 -259
@@ -1,101 +1,101 @@
1
- // https://github.com/mozilla/Spoke/commit/9701d647020e09d584885bd457eb225e9995c12f
2
- import XAtlas from 'xatlas-web';
3
- import { BufferGeometry, Float32BufferAttribute, Uint32BufferAttribute } from 'three';
4
-
5
- export class UVUnwrapper {
6
-
7
- constructor() {
8
-
9
- this._module = null;
10
-
11
- }
12
-
13
- async load() {
14
-
15
- const wasmurl = new URL( '../../node_modules/xatlas-web/dist/xatlas-web.wasm', import.meta.url );
16
- this._module = XAtlas( {
17
-
18
- locateFile( path ) {
19
-
20
- if ( path.endsWith( '.wasm' ) ) {
21
-
22
- return wasmurl.toString();
23
-
24
- }
25
-
26
- return path;
27
-
28
- }
29
-
30
- } );
31
- return this._module.ready;
32
-
33
- }
34
-
35
- generate( geometry ) {
36
-
37
- const xatlas = this._module;
38
- const originalVertexCount = geometry.attributes.position.count;
39
- const originalIndexCount = geometry.index.count;
40
-
41
- xatlas.createAtlas();
42
-
43
- const meshInfo = xatlas.createMesh( originalVertexCount, originalIndexCount, true, true );
44
- xatlas.HEAPU16.set( geometry.index.array, meshInfo.indexOffset / Uint16Array.BYTES_PER_ELEMENT );
45
- xatlas.HEAPF32.set( geometry.attributes.position.array, meshInfo.positionOffset / Float32Array.BYTES_PER_ELEMENT );
46
- xatlas.HEAPF32.set( geometry.attributes.normal.array, meshInfo.normalOffset / Float32Array.BYTES_PER_ELEMENT );
47
- xatlas.HEAPF32.set( geometry.attributes.uv.array, meshInfo.uvOffset / Float32Array.BYTES_PER_ELEMENT );
48
-
49
- const statusCode = xatlas.addMesh();
50
- if ( statusCode !== AddMeshStatus.Success ) {
51
-
52
- throw new Error( `UVUnwrapper: Error adding mesh. Status code ${ statusCode }` );
53
-
54
- }
55
-
56
- xatlas.generateAtlas();
57
-
58
- const meshData = xatlas.getMeshData( meshInfo.meshId );
59
- const oldPositionArray = geometry.attributes.position.array;
60
- const oldNormalArray = geometry.attributes.normal.array;
61
- const oldUvArray = geometry.attributes.uv.array;
62
-
63
- const newPositionArray = new Float32Array( meshData.newVertexCount * 3 );
64
- const newNormalArray = new Float32Array( meshData.newVertexCount * 3 );
65
- const newUvArray = new Float32Array( meshData.newVertexCount * 2 );
66
- const newUv2Array = new Float32Array( xatlas.HEAPF32.buffer, meshData.uvOffset, meshData.newVertexCount * 2 );
67
- const newIndexArray = new Uint32Array( xatlas.HEAPU32.buffer, meshData.indexOffset, meshData.newIndexCount );
68
- const originalIndexArray = new Uint32Array(
69
- xatlas.HEAPU32.buffer,
70
- meshData.originalIndexOffset,
71
- meshData.newVertexCount
72
- );
73
-
74
- for ( let i = 0; i < meshData.newVertexCount; i ++ ) {
75
-
76
- const originalIndex = originalIndexArray[ i ];
77
- newPositionArray[ i * 3 ] = oldPositionArray[ originalIndex * 3 ];
78
- newPositionArray[ i * 3 + 1 ] = oldPositionArray[ originalIndex * 3 + 1 ];
79
- newPositionArray[ i * 3 + 2 ] = oldPositionArray[ originalIndex * 3 + 2 ];
80
- newNormalArray[ i * 3 ] = oldNormalArray[ originalIndex * 3 ];
81
- newNormalArray[ i * 3 + 1 ] = oldNormalArray[ originalIndex * 3 + 1 ];
82
- newNormalArray[ i * 3 + 2 ] = oldNormalArray[ originalIndex * 3 + 2 ];
83
- newUvArray[ i * 2 ] = oldUvArray[ originalIndex * 2 ];
84
- newUvArray[ i * 2 + 1 ] = oldUvArray[ originalIndex * 2 + 1 ];
85
-
86
- }
87
-
88
- const newGeometry = new BufferGeometry();
89
- newGeometry.addAttribute( 'position', new Float32BufferAttribute( newPositionArray, 3 ) );
90
- newGeometry.addAttribute( 'normal', new Float32BufferAttribute( newNormalArray, 3 ) );
91
- newGeometry.addAttribute( 'uv', new Float32BufferAttribute( newUvArray, 2 ) );
92
- newGeometry.addAttribute( 'uv2', new Float32BufferAttribute( newUv2Array, 2 ) );
93
- newGeometry.setIndex( new Uint32BufferAttribute( newIndexArray, 1 ) );
94
-
95
- mesh.geometry = newGeometry;
96
-
97
- xatlas.destroyAtlas();
98
-
99
- }
100
-
101
- }
1
+ // https://github.com/mozilla/Spoke/commit/9701d647020e09d584885bd457eb225e9995c12f
2
+ import XAtlas from 'xatlas-web';
3
+ import { BufferGeometry, Float32BufferAttribute, Uint32BufferAttribute } from 'three';
4
+
5
+ export class UVUnwrapper {
6
+
7
+ constructor() {
8
+
9
+ this._module = null;
10
+
11
+ }
12
+
13
+ async load() {
14
+
15
+ const wasmurl = new URL( '../../node_modules/xatlas-web/dist/xatlas-web.wasm', import.meta.url );
16
+ this._module = XAtlas( {
17
+
18
+ locateFile( path ) {
19
+
20
+ if ( path.endsWith( '.wasm' ) ) {
21
+
22
+ return wasmurl.toString();
23
+
24
+ }
25
+
26
+ return path;
27
+
28
+ }
29
+
30
+ } );
31
+ return this._module.ready;
32
+
33
+ }
34
+
35
+ generate( geometry ) {
36
+
37
+ const xatlas = this._module;
38
+ const originalVertexCount = geometry.attributes.position.count;
39
+ const originalIndexCount = geometry.index.count;
40
+
41
+ xatlas.createAtlas();
42
+
43
+ const meshInfo = xatlas.createMesh( originalVertexCount, originalIndexCount, true, true );
44
+ xatlas.HEAPU16.set( geometry.index.array, meshInfo.indexOffset / Uint16Array.BYTES_PER_ELEMENT );
45
+ xatlas.HEAPF32.set( geometry.attributes.position.array, meshInfo.positionOffset / Float32Array.BYTES_PER_ELEMENT );
46
+ xatlas.HEAPF32.set( geometry.attributes.normal.array, meshInfo.normalOffset / Float32Array.BYTES_PER_ELEMENT );
47
+ xatlas.HEAPF32.set( geometry.attributes.uv.array, meshInfo.uvOffset / Float32Array.BYTES_PER_ELEMENT );
48
+
49
+ const statusCode = xatlas.addMesh();
50
+ if ( statusCode !== AddMeshStatus.Success ) {
51
+
52
+ throw new Error( `UVUnwrapper: Error adding mesh. Status code ${ statusCode }` );
53
+
54
+ }
55
+
56
+ xatlas.generateAtlas();
57
+
58
+ const meshData = xatlas.getMeshData( meshInfo.meshId );
59
+ const oldPositionArray = geometry.attributes.position.array;
60
+ const oldNormalArray = geometry.attributes.normal.array;
61
+ const oldUvArray = geometry.attributes.uv.array;
62
+
63
+ const newPositionArray = new Float32Array( meshData.newVertexCount * 3 );
64
+ const newNormalArray = new Float32Array( meshData.newVertexCount * 3 );
65
+ const newUvArray = new Float32Array( meshData.newVertexCount * 2 );
66
+ const newUv2Array = new Float32Array( xatlas.HEAPF32.buffer, meshData.uvOffset, meshData.newVertexCount * 2 );
67
+ const newIndexArray = new Uint32Array( xatlas.HEAPU32.buffer, meshData.indexOffset, meshData.newIndexCount );
68
+ const originalIndexArray = new Uint32Array(
69
+ xatlas.HEAPU32.buffer,
70
+ meshData.originalIndexOffset,
71
+ meshData.newVertexCount
72
+ );
73
+
74
+ for ( let i = 0; i < meshData.newVertexCount; i ++ ) {
75
+
76
+ const originalIndex = originalIndexArray[ i ];
77
+ newPositionArray[ i * 3 ] = oldPositionArray[ originalIndex * 3 ];
78
+ newPositionArray[ i * 3 + 1 ] = oldPositionArray[ originalIndex * 3 + 1 ];
79
+ newPositionArray[ i * 3 + 2 ] = oldPositionArray[ originalIndex * 3 + 2 ];
80
+ newNormalArray[ i * 3 ] = oldNormalArray[ originalIndex * 3 ];
81
+ newNormalArray[ i * 3 + 1 ] = oldNormalArray[ originalIndex * 3 + 1 ];
82
+ newNormalArray[ i * 3 + 2 ] = oldNormalArray[ originalIndex * 3 + 2 ];
83
+ newUvArray[ i * 2 ] = oldUvArray[ originalIndex * 2 ];
84
+ newUvArray[ i * 2 + 1 ] = oldUvArray[ originalIndex * 2 + 1 ];
85
+
86
+ }
87
+
88
+ const newGeometry = new BufferGeometry();
89
+ newGeometry.addAttribute( 'position', new Float32BufferAttribute( newPositionArray, 3 ) );
90
+ newGeometry.addAttribute( 'normal', new Float32BufferAttribute( newNormalArray, 3 ) );
91
+ newGeometry.addAttribute( 'uv', new Float32BufferAttribute( newUvArray, 2 ) );
92
+ newGeometry.addAttribute( 'uv2', new Float32BufferAttribute( newUv2Array, 2 ) );
93
+ newGeometry.setIndex( new Uint32BufferAttribute( newIndexArray, 1 ) );
94
+
95
+ mesh.geometry = newGeometry;
96
+
97
+ xatlas.destroyAtlas();
98
+
99
+ }
100
+
101
+ }
@@ -0,0 +1,40 @@
1
+ import { PathTracingSceneGenerator } from '../core/PathTracingSceneGenerator.js';
2
+ import { SAH } from 'three-mesh-bvh';
3
+ import { GenerateMeshBVHWorker } from 'three-mesh-bvh/src/workers/GenerateMeshBVHWorker.js';
4
+
5
+ export class PathTracingSceneWorker extends PathTracingSceneGenerator {
6
+
7
+ constructor() {
8
+
9
+ super();
10
+ this.bvhGenerator = new GenerateMeshBVHWorker();
11
+
12
+ }
13
+
14
+ generate( scene, options = {} ) {
15
+
16
+ const { bvhGenerator } = this;
17
+ const { geometry, materials, textures } = this.prepScene( scene );
18
+
19
+ const bvhOptions = { strategy: SAH, ...options, maxLeafTris: 1 };
20
+ const bvhPromise = bvhGenerator.generate( geometry, bvhOptions );
21
+ return bvhPromise.then( bvh => {
22
+
23
+ return {
24
+ scene,
25
+ materials,
26
+ textures,
27
+ bvh,
28
+ };
29
+
30
+ } );
31
+
32
+ }
33
+
34
+ dispose() {
35
+
36
+ this.bvhGenerator.dispose();
37
+
38
+ }
39
+
40
+ }
@@ -1,132 +0,0 @@
1
- import { Color, DataTexture, FloatType, RGFormat, LinearFilter } from 'three';
2
-
3
- function RGBEToLinear( r, g, b, e, target ) {
4
-
5
- const exp = e * 255.0 - 128.0;
6
- target.set( r, g, b ).multiplyScalar( Math.pow( exp ) );
7
- return target;
8
-
9
- }
10
-
11
- export class EquirectPdfUniform {
12
-
13
- constructor() {
14
-
15
- const marginalData = new DataTexture();
16
- marginalData.type = FloatType;
17
- marginalData.format = RGFormat;
18
- marginalData.minFilter = LinearFilter;
19
- marginalData.maxFilter = LinearFilter;
20
- marginalData.generateMipmaps = false;
21
-
22
- const conditionalData = new DataTexture();
23
- conditionalData.type = FloatType;
24
- conditionalData.format = RGFormat;
25
- conditionalData.minFilter = LinearFilter;
26
- conditionalData.maxFilter = LinearFilter;
27
- conditionalData.generateMipmaps = false;
28
-
29
- this.marginalData = marginalData;
30
- this.conditionalData = conditionalData;
31
-
32
- }
33
-
34
- updateFrom( hdr ) {
35
-
36
- // TODO: another reference implementation with a different approach:
37
- // https://github.com/nvpro-samples/vk_mini_samples/blob/main/hdr_sampling/src/hdr_env.cpp#L243
38
-
39
- // https://github.com/knightcrawler25/GLSL-PathTracer/blob/master/src/loaders/hdrloader.cpp
40
- const { width, height, data } = hdr.image;
41
- const color = new Color();
42
- const hsl = {};
43
-
44
- // track the importance of any given pixel in the image by tracking its weight relative to other pixels in the image
45
- const pdfConditional = new Float32Array( width * height );
46
- const cdfConditional = new Float32Array( width * height );
47
-
48
- const pdfMarginal = new Float32Array( height );
49
- const cdfMarginal = new Float32Array( height );
50
-
51
- let cumulativeWeightMarginal = 0.0;
52
- for ( let y = 0; y < height; y ++ ) {
53
-
54
- let cumulativeWeight = 0.0;
55
- for ( let x = 0; x < width; x ++ ) {
56
-
57
- const i = y * width + x;
58
- const r = data[ 4 * i + 0 ];
59
- const g = data[ 4 * i + 1 ];
60
- const b = data[ 4 * i + 2 ];
61
- const e = data[ 4 * i + 3 ];
62
-
63
- // the probability of the pixel being selected in this row is the
64
- // scale of the luminance relative to the rest of the pixels.
65
- // TODO: this should also account for the solid angle of the pixel when sampling
66
- const weight = RGBEToLinear( r, g, b, e, color ).getHSL( hsl ).l;
67
- cumulativeWeight += weight;
68
-
69
- pdfConditional[ i ] = weight;
70
- cdfConditional[ i ] = cumulativeWeight;
71
-
72
- }
73
-
74
- // scale the pdf and cdf to [0.0, 1.0]
75
- for ( let i = 0, l = pdfConditional.length; i < l; i ++ ) {
76
-
77
- pdfConditional[ i ] /= cumulativeWeight;
78
- cdfConditional[ i ] /= cumulativeWeight;
79
-
80
- }
81
-
82
- cumulativeWeightMarginal += cumulativeWeight;
83
-
84
- // compute the marginal pdf and cdf along the height of the map.
85
- pdfMarginal[ y ] = cumulativeWeight;
86
- cdfMarginal[ y ] = cumulativeWeightMarginal;
87
-
88
- }
89
-
90
- // scale the marginal pdf and cdf to [0.0, 1.0]
91
- for ( let i = 0, l = pdfMarginal.length; i < l; i ++ ) {
92
-
93
- pdfMarginal[ i ] /= cumulativeWeightMarginal;
94
- cdfMarginal[ i ] /= cumulativeWeightMarginal;
95
-
96
- }
97
-
98
- // compute a sorted index of distributions and the probabilities along them for both
99
- // the marginal and conditional data. These will be used to sample with a random number
100
- // to retrieve a uv value to sample in the environment map.
101
- const marginalDataArray = new Float32Array( height * 2 );
102
- const conditionalDataArray = new Float32Array( width * height * 2 );
103
-
104
- for ( let i = 0; i < height; i ++ ) {
105
-
106
- //const dist = ( i + 1 ) / height;
107
- const row = 0; // TODO: find the row that lies at the given cumulative distribution value above
108
- marginalDataArray[ 2 * i + 0 ] = row / height;
109
- marginalDataArray[ 2 * i + 1 ] = pdfMarginal[ i ];
110
-
111
- }
112
-
113
- for ( let y = 0; y < height; y ++ ) {
114
-
115
- for ( let x = 0; x < width; x ++ ) {
116
-
117
- const i = y * width + x;
118
- //const dist = ( x + 1 ) / width;
119
- const col = 0; // TODO: find the column in the given row that lies at the cumulative dist value above
120
- conditionalDataArray[ 2 * i + 0 ] = col / width;
121
- conditionalDataArray[ 2 * i + 1 ] = pdfConditional[ i ];
122
-
123
- }
124
-
125
- }
126
-
127
- this.marginalData.image = { width, height, data: marginalDataArray };
128
- this.conditionalData.image = { width, height, data: conditionalDataArray };
129
-
130
- }
131
-
132
- }
@@ -1,18 +0,0 @@
1
- import { MaterialStructUniform } from './MaterialStructUniform.js';
2
-
3
- export class MaterialStructArrayUniform extends Array {
4
-
5
- updateFrom( materials, textures ) {
6
-
7
- while ( this.length > materials.length ) this.pop();
8
- while ( this.length < materials.length ) this.push( new MaterialStructUniform() );
9
-
10
- for ( let i = 0, l = this.length; i < l; i ++ ) {
11
-
12
- this[ i ].updateFrom( materials[ i ], textures );
13
-
14
- }
15
-
16
- }
17
-
18
- }
@@ -1,94 +0,0 @@
1
- import { Color, Vector2 } from 'three';
2
- export class MaterialStructUniform {
3
-
4
- constructor() {
5
-
6
- this.init();
7
-
8
- }
9
-
10
- init() {
11
-
12
- this.color = new Color( 0xffffff );
13
- this.map = - 1;
14
-
15
- this.metalness = 1.0;
16
- this.metalnessMap = - 1;
17
-
18
- this.roughness = 1.0;
19
- this.roughnessMap = - 1;
20
-
21
- this.ior = 1.0;
22
- this.transmission = 0.0;
23
- this.transmissionMap = - 1;
24
-
25
- this.emissive = new Color( 0 );
26
- this.emissiveIntensity = 1.0;
27
- this.emissiveMap = - 1;
28
-
29
- this.normalMap = - 1;
30
- this.normalScale = new Vector2( 1, 1 );
31
-
32
- this.opacity = 1.0;
33
- this.alphaTest = 0.0;
34
-
35
- // TODO: Clearcoat
36
-
37
- // TODO: Sheen
38
-
39
- }
40
-
41
- updateFrom( material, textures = [] ) {
42
-
43
- this.init();
44
-
45
- // color
46
- if ( 'color' in material ) this.color.copy( material.color );
47
- else material.color.set( 0xffffff );
48
-
49
- this.map = textures.indexOf( material.map );
50
-
51
- // metalness
52
- if ( 'metalness' in material ) this.metalness = material.metalness;
53
- else this.metalness = 1.0;
54
-
55
- this.metalnessMap = textures.indexOf( material.metalnessMap );
56
-
57
- // roughness
58
- if ( 'roughness' in material ) this.roughness = material.roughness;
59
- else this.roughness = 1.0;
60
-
61
- this.roughnessMap = textures.indexOf( material.roughnessMap );
62
-
63
- // transmission
64
- if ( 'ior' in material ) this.ior = material.ior;
65
- else this.ior = 1.0;
66
-
67
- if ( 'transmission' in material ) this.transmission = material.transmission;
68
- else this.transmission = 0.0;
69
-
70
- if ( 'transmissionMap' in material ) this.transmissionMap = textures.indexOf( material.transmissionMap );
71
-
72
- // emission
73
- if ( 'emissive' in material ) this.emissive.copy( material.emissive );
74
- else this.emissive.set( 0 );
75
-
76
- if ( 'emissiveIntensity' in material ) this.emissiveIntensity = material.emissiveIntensity;
77
- else this.emissiveIntensity = 1.0;
78
-
79
- this.emissiveMap = textures.indexOf( material.emissiveMap );
80
-
81
- // normals
82
- this.normalMap = textures.indexOf( material.normalMap );
83
- if ( 'normalScale' in material ) this.normalScale.copy( material.normalScale );
84
- else this.normalScale.set( 1, 1 );
85
-
86
- // opacity
87
- this.opacity = material.opacity;
88
-
89
- // alpha test
90
- this.alphaTest = material.alphaTest;
91
-
92
- }
93
-
94
- }