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,80 +1,93 @@
1
- import {
2
- WebGLArrayRenderTarget,
3
- RGBAFormat,
4
- UnsignedByteType,
5
- MeshBasicMaterial,
6
- Color,
7
- RepeatWrapping,
8
- LinearFilter,
9
- NoToneMapping,
10
- } from 'three';
11
- import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
12
-
13
- const prevColor = new Color();
14
- export class RenderTarget2DArray extends WebGLArrayRenderTarget {
15
-
16
- constructor( ...args ) {
17
-
18
- super( ...args );
19
-
20
- const tex = this.texture;
21
- tex.format = RGBAFormat;
22
- tex.type = UnsignedByteType;
23
- tex.minFilter = LinearFilter;
24
- tex.magFilter = LinearFilter;
25
- tex.wrapS = RepeatWrapping;
26
- tex.wrapT = RepeatWrapping;
27
- tex.setTextures = ( ...args ) => {
28
-
29
- this.setTextures( ...args );
30
-
31
- };
32
-
33
- const fsQuad = new FullScreenQuad( new MeshBasicMaterial() );
34
- this.fsQuad = fsQuad;
35
-
36
- }
37
-
38
- setTextures( renderer, width, height, textures ) {
39
-
40
- // save previous renderer state
41
- const prevRenderTarget = renderer.getRenderTarget();
42
- const prevToneMapping = renderer.toneMapping;
43
- const prevAlpha = renderer.getClearAlpha();
44
- renderer.getClearColor( prevColor );
45
-
46
- // resize the render target
47
- const depth = textures.length;
48
- this.setSize( width, height, depth );
49
- renderer.setClearColor( 0, 0 );
50
- renderer.toneMapping = NoToneMapping;
51
-
52
- // render each texture into each layer of the target
53
- const fsQuad = this.fsQuad;
54
- for ( let i = 0, l = depth; i < l; i ++ ) {
55
-
56
- const texture = textures[ i ];
57
- fsQuad.material.map = texture;
58
- fsQuad.material.transparent = true;
59
-
60
- renderer.setRenderTarget( this, i );
61
- fsQuad.render( renderer );
62
-
63
- }
64
-
65
- // reset the renderer
66
- fsQuad.material.map = null;
67
- renderer.setClearColor( prevColor, prevAlpha );
68
- renderer.setRenderTarget( prevRenderTarget );
69
- renderer.toneMapping = prevToneMapping;
70
-
71
- }
72
-
73
- dispose() {
74
-
75
- super.dispose();
76
- this.fsQuad.dispose();
77
-
78
- }
79
-
80
- }
1
+ import {
2
+ WebGLArrayRenderTarget,
3
+ RGBAFormat,
4
+ UnsignedByteType,
5
+ MeshBasicMaterial,
6
+ Color,
7
+ RepeatWrapping,
8
+ LinearFilter,
9
+ NoToneMapping,
10
+ } from 'three';
11
+ import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
12
+
13
+ const prevColor = new Color();
14
+ export class RenderTarget2DArray extends WebGLArrayRenderTarget {
15
+
16
+ constructor( ...args ) {
17
+
18
+ super( ...args );
19
+
20
+ const tex = this.texture;
21
+ tex.format = RGBAFormat;
22
+ tex.type = UnsignedByteType;
23
+ tex.minFilter = LinearFilter;
24
+ tex.magFilter = LinearFilter;
25
+ tex.wrapS = RepeatWrapping;
26
+ tex.wrapT = RepeatWrapping;
27
+ tex.setTextures = ( ...args ) => {
28
+
29
+ this.setTextures( ...args );
30
+
31
+ };
32
+
33
+ const fsQuad = new FullScreenQuad( new MeshBasicMaterial() );
34
+ this.fsQuad = fsQuad;
35
+
36
+ }
37
+
38
+ setTextures( renderer, width, height, textures ) {
39
+
40
+ // save previous renderer state
41
+ const prevRenderTarget = renderer.getRenderTarget();
42
+ const prevToneMapping = renderer.toneMapping;
43
+ const prevAlpha = renderer.getClearAlpha();
44
+ renderer.getClearColor( prevColor );
45
+
46
+ // resize the render target and ensure we don't have an empty texture
47
+ // render target depth must be >= 1 to avoid unbound texture error on android devices
48
+ const depth = textures.length || 1;
49
+ this.setSize( width, height, depth );
50
+ renderer.setClearColor( 0, 0 );
51
+ renderer.toneMapping = NoToneMapping;
52
+
53
+ // render each texture into each layer of the target
54
+ const fsQuad = this.fsQuad;
55
+ for ( let i = 0, l = depth; i < l; i ++ ) {
56
+
57
+ const texture = textures[ i ];
58
+ if ( texture ) {
59
+
60
+ // revert to default texture transform before rendering
61
+ texture.matrixAutoUpdate = false;
62
+ texture.matrix.identity();
63
+
64
+ fsQuad.material.map = texture;
65
+ fsQuad.material.transparent = true;
66
+
67
+ renderer.setRenderTarget( this, i );
68
+ fsQuad.render( renderer );
69
+
70
+ // restore custom texture transform
71
+ texture.updateMatrix();
72
+ texture.matrixAutoUpdate = true;
73
+
74
+ }
75
+
76
+ }
77
+
78
+ // reset the renderer
79
+ fsQuad.material.map = null;
80
+ renderer.setClearColor( prevColor, prevAlpha );
81
+ renderer.setRenderTarget( prevRenderTarget );
82
+ renderer.toneMapping = prevToneMapping;
83
+
84
+ }
85
+
86
+ dispose() {
87
+
88
+ super.dispose();
89
+ this.fsQuad.dispose();
90
+
91
+ }
92
+
93
+ }
@@ -0,0 +1,113 @@
1
+ import { WebGLRenderTarget, RGBAFormat, FloatType, PMREMGenerator, DataTexture, EquirectangularReflectionMapping } from 'three';
2
+ import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
3
+ import { MaterialBase } from '../materials/MaterialBase.js';
4
+ import { shaderUtils } from '../shader/shaderUtils.js';
5
+
6
+ class PMREMCopyMaterial extends MaterialBase {
7
+
8
+ constructor() {
9
+
10
+ super( {
11
+
12
+ uniforms: {
13
+
14
+ envMap: { value: null },
15
+ blur: { value: 0 },
16
+
17
+ },
18
+
19
+ vertexShader: /* glsl */`
20
+
21
+ varying vec2 vUv;
22
+ void main() {
23
+ vUv = uv;
24
+ gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
25
+ }
26
+
27
+ `,
28
+
29
+ fragmentShader: /* glsl */`
30
+
31
+ #include <common>
32
+ #include <cube_uv_reflection_fragment>
33
+
34
+ ${ shaderUtils }
35
+
36
+ uniform sampler2D envMap;
37
+ uniform float blur;
38
+ varying vec2 vUv;
39
+ void main() {
40
+
41
+ vec3 rayDirection = equirectUvToDirection( vUv );
42
+ gl_FragColor = textureCubeUV( envMap, rayDirection, blur );
43
+
44
+ }
45
+
46
+ `,
47
+
48
+ } );
49
+
50
+ }
51
+
52
+ }
53
+
54
+ export class BlurredEnvMapGenerator {
55
+
56
+ constructor( renderer ) {
57
+
58
+ this.renderer = renderer;
59
+ this.pmremGenerator = new PMREMGenerator( renderer );
60
+ this.copyQuad = new FullScreenQuad( new PMREMCopyMaterial() );
61
+ this.renderTarget = new WebGLRenderTarget( 1, 1, { type: FloatType, format: RGBAFormat } );
62
+
63
+ }
64
+
65
+ dispose() {
66
+
67
+ this.pmremGenerator.dispose();
68
+ this.copyQuad.dispose();
69
+ this.renderTarget.dispose();
70
+
71
+ }
72
+
73
+ generate( texture, blur ) {
74
+
75
+ const { pmremGenerator, renderTarget, copyQuad, renderer } = this;
76
+
77
+ // get the pmrem target
78
+ const pmremTarget = pmremGenerator.fromEquirectangular( texture );
79
+
80
+ // set up the material
81
+ const { width, height } = texture.image;
82
+ renderTarget.setSize( width, height );
83
+ copyQuad.material.envMap = pmremTarget.texture;
84
+ copyQuad.material.blur = blur;
85
+
86
+ // render
87
+ const prevRenderTarget = renderer.getRenderTarget();
88
+ const prevClear = renderer.autoClear;
89
+
90
+ renderer.setRenderTarget( renderTarget );
91
+ renderer.autoClear = true;
92
+ copyQuad.render( renderer );
93
+
94
+ renderer.setRenderTarget( prevRenderTarget );
95
+ renderer.autoClear = prevClear;
96
+
97
+ // read the data back
98
+ const buffer = new Float32Array( width * height * 4 );
99
+ renderer.readRenderTargetPixels( renderTarget, 0, 0, width, height, buffer );
100
+
101
+ const result = new DataTexture( buffer, width, height, RGBAFormat, FloatType );
102
+ result.minFilter = texture.minFilter;
103
+ result.magFilter = texture.magFilter;
104
+ result.wrapS = texture.wrapS;
105
+ result.wrapT = texture.wrapT;
106
+ result.mapping = EquirectangularReflectionMapping;
107
+ result.needsUpdate = true;
108
+
109
+ return result;
110
+
111
+ }
112
+
113
+ }
@@ -1,172 +1,194 @@
1
- import { BufferAttribute } from 'three';
2
- import { mergeBufferGeometries, mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
3
- function getGroupMaterialIndicesAttribute( geometry, materials, allMaterials ) {
4
-
5
- if ( ! Array.isArray( materials ) ) {
6
-
7
- materials = [ materials ];
8
-
9
- }
10
-
11
- const vertCount = geometry.attributes.position.count;
12
- const materialArray = new Uint8Array( vertCount );
13
- let groups = geometry.groups;
14
- if ( groups.length === 0 ) {
15
-
16
- groups = [ { count: vertCount, start: 0, materialIndex: 0 } ];
17
-
18
- }
19
-
20
- for ( let i = 0; i < groups.length; i ++ ) {
21
-
22
- const group = groups[ i ];
23
- const { count, start } = group;
24
- const endCount = Math.min( count, vertCount - start );
25
- const mat = materials[ group.materialIndex ];
26
- const materialIndex = allMaterials.indexOf( mat );
27
-
28
- for ( let j = 0; j < endCount; j ++ ) {
29
-
30
- materialArray[ start + j ] = materialIndex;
31
-
32
- }
33
-
34
- }
35
-
36
- return new BufferAttribute( materialArray, 1, false );
37
-
38
- }
39
-
40
- export function mergeMeshes( meshes, options = {} ) {
41
-
42
- options = { attributes: null, cloneGeometry: true, ...options };
43
-
44
- const transformedGeometry = [];
45
- const materialSet = new Set();
46
- for ( let i = 0, l = meshes.length; i < l; i ++ ) {
47
-
48
- // save any materials
49
- const mesh = meshes[ i ];
50
- if ( mesh.visible === false ) continue;
51
-
52
- if ( Array.isArray( mesh.material ) ) {
53
-
54
- mesh.material.forEach( m => materialSet.add( m ) );
55
-
56
- } else {
57
-
58
- materialSet.add( mesh.material );
59
-
60
- }
61
-
62
- }
63
-
64
- const materials = Array.from( materialSet );
65
- for ( let i = 0, l = meshes.length; i < l; i ++ ) {
66
-
67
- // ensure the matrix world is up to date
68
- const mesh = meshes[ i ];
69
- if ( mesh.visible === false ) continue;
70
-
71
- mesh.updateMatrixWorld();
72
-
73
- // apply the matrix world to the geometry
74
- const originalGeometry = meshes[ i ].geometry;
75
- let geometry = options.cloneGeometry ? originalGeometry.clone() : originalGeometry;
76
- geometry.applyMatrix4( mesh.matrixWorld );
77
-
78
- const attrs = options.attributes;
79
- if ( ! geometry.attributes.normal && ( attrs && attrs.includes( 'normal' ) ) ) {
80
-
81
- geometry.computeVertexNormals();
82
-
83
- }
84
-
85
- if ( ! geometry.attributes.uv && ( attrs && attrs.includes( 'uv' ) ) ) {
86
-
87
- const vertCount = geometry.attributes.position.count;
88
- geometry.setAttribute( 'uv', new BufferAttribute( new Float32Array( vertCount * 2 ), 2, false ) );
89
-
90
- }
91
-
92
- if ( ! geometry.attributes.tangent && ( attrs && attrs.includes( 'tangent' ) ) ) {
93
-
94
- if ( mesh.material.normalMap ) {
95
-
96
- // computeTangents requires an index buffer
97
- if ( geometry.index === null ) {
98
-
99
- geometry = mergeVertices( geometry );
100
-
101
- }
102
-
103
- geometry.computeTangents();
104
-
105
- } else {
106
-
107
- const vertCount = geometry.attributes.position.count;
108
- geometry.setAttribute( 'tangent', new BufferAttribute( new Float32Array( vertCount * 4 ), 4, false ) );
109
-
110
- }
111
-
112
- }
113
-
114
- if ( ! geometry.index ) {
115
-
116
- // TODO: compute a typed array
117
- const indexCount = geometry.attributes.position.count;
118
- const array = new Array( indexCount );
119
- for ( let i = 0; i < indexCount; i ++ ) {
120
-
121
- array[ i ] = i;
122
-
123
- }
124
-
125
- geometry.setIndex( array );
126
-
127
- }
128
-
129
- // trim any unneeded attributes
130
- if ( options.attributes ) {
131
-
132
- for ( const key in geometry.attributes ) {
133
-
134
- if ( ! options.attributes.includes( key ) ) {
135
-
136
- geometry.deleteAttribute( key );
137
-
138
- }
139
-
140
- }
141
-
142
- }
143
-
144
- // create the material index attribute
145
- const materialIndexAttribute = getGroupMaterialIndicesAttribute( geometry, mesh.material, materials );
146
- geometry.setAttribute( 'materialIndex', materialIndexAttribute );
147
-
148
- transformedGeometry.push( geometry );
149
-
150
- }
151
-
152
- const textureSet = new Set();
153
- materials.forEach( material => {
154
-
155
- for ( const key in material ) {
156
-
157
- const value = material[ key ];
158
- if ( value && value.isTexture ) {
159
-
160
- textureSet.add( value );
161
-
162
- }
163
-
164
- }
165
-
166
- } );
167
-
168
- const geometry = mergeBufferGeometries( transformedGeometry, false );
169
- const textures = Array.from( textureSet );
170
- return { geometry, materials, textures };
171
-
172
- }
1
+ import { BufferAttribute } from 'three';
2
+ import { mergeBufferGeometries, mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
3
+ export function getGroupMaterialIndicesAttribute( geometry, materials, allMaterials ) {
4
+
5
+ const indexAttr = geometry.index;
6
+ const posAttr = geometry.attributes.position;
7
+ const vertCount = posAttr.count;
8
+ const materialArray = new Uint8Array( vertCount );
9
+ const totalCount = indexAttr ? indexAttr.count : vertCount;
10
+ let groups = geometry.groups;
11
+ if ( groups.length === 0 ) {
12
+
13
+ groups = [ { count: totalCount, start: 0, materialIndex: 0 } ];
14
+
15
+ }
16
+
17
+ for ( let i = 0; i < groups.length; i ++ ) {
18
+
19
+ const group = groups[ i ];
20
+ const start = group.start;
21
+ const count = group.count;
22
+ const endCount = Math.min( count, totalCount - start );
23
+
24
+ const mat = Array.isArray( materials ) ? materials[ group.materialIndex ] : materials;
25
+ const materialIndex = allMaterials.indexOf( mat );
26
+
27
+ for ( let j = 0; j < endCount; j ++ ) {
28
+
29
+ let index = start + j;
30
+ if ( indexAttr ) {
31
+
32
+ index = indexAttr.getX( index );
33
+
34
+ }
35
+
36
+ materialArray[ index ] = materialIndex;
37
+
38
+ }
39
+
40
+ }
41
+
42
+ return new BufferAttribute( materialArray, 1, false );
43
+
44
+ }
45
+
46
+ export function trimToAttributes( geometry, attributes ) {
47
+
48
+ // trim any unneeded attributes
49
+ if ( attributes ) {
50
+
51
+ for ( const key in geometry.attributes ) {
52
+
53
+ if ( ! attributes.includes( key ) ) {
54
+
55
+ geometry.deleteAttribute( key );
56
+
57
+ }
58
+
59
+ }
60
+
61
+ }
62
+
63
+ }
64
+
65
+ export function setCommonAttributes( geometry, options ) {
66
+
67
+ const { attributes = [], normalMapRequired = false } = options;
68
+
69
+ if ( ! geometry.attributes.normal && ( attributes && attributes.includes( 'normal' ) ) ) {
70
+
71
+ geometry.computeVertexNormals();
72
+
73
+ }
74
+
75
+ if ( ! geometry.attributes.uv && ( attributes && attributes.includes( 'uv' ) ) ) {
76
+
77
+ const vertCount = geometry.attributes.position.count;
78
+ geometry.setAttribute( 'uv', new BufferAttribute( new Float32Array( vertCount * 2 ), 2, false ) );
79
+
80
+ }
81
+
82
+ if ( ! geometry.attributes.tangent && ( attributes && attributes.includes( 'tangent' ) ) ) {
83
+
84
+ if ( normalMapRequired ) {
85
+
86
+ // computeTangents requires an index buffer
87
+ if ( geometry.index === null ) {
88
+
89
+ geometry = mergeVertices( geometry );
90
+
91
+ }
92
+
93
+ geometry.computeTangents();
94
+
95
+ } else {
96
+
97
+ const vertCount = geometry.attributes.position.count;
98
+ geometry.setAttribute( 'tangent', new BufferAttribute( new Float32Array( vertCount * 4 ), 4, false ) );
99
+
100
+ }
101
+
102
+ }
103
+
104
+ if ( ! geometry.index ) {
105
+
106
+ // TODO: compute a typed array
107
+ const indexCount = geometry.attributes.position.count;
108
+ const array = new Array( indexCount );
109
+ for ( let i = 0; i < indexCount; i ++ ) {
110
+
111
+ array[ i ] = i;
112
+
113
+ }
114
+
115
+ geometry.setIndex( array );
116
+
117
+ }
118
+
119
+ }
120
+
121
+ export function mergeMeshes( meshes, options = {} ) {
122
+
123
+ options = { attributes: null, cloneGeometry: true, ...options };
124
+
125
+ const transformedGeometry = [];
126
+ const materialSet = new Set();
127
+ for ( let i = 0, l = meshes.length; i < l; i ++ ) {
128
+
129
+ // save any materials
130
+ const mesh = meshes[ i ];
131
+ if ( mesh.visible === false ) continue;
132
+
133
+ if ( Array.isArray( mesh.material ) ) {
134
+
135
+ mesh.material.forEach( m => materialSet.add( m ) );
136
+
137
+ } else {
138
+
139
+ materialSet.add( mesh.material );
140
+
141
+ }
142
+
143
+ }
144
+
145
+ const materials = Array.from( materialSet );
146
+ for ( let i = 0, l = meshes.length; i < l; i ++ ) {
147
+
148
+ // ensure the matrix world is up to date
149
+ const mesh = meshes[ i ];
150
+ if ( mesh.visible === false ) continue;
151
+
152
+ mesh.updateMatrixWorld();
153
+
154
+ // apply the matrix world to the geometry
155
+ const originalGeometry = meshes[ i ].geometry;
156
+ const geometry = options.cloneGeometry ? originalGeometry.clone() : originalGeometry;
157
+ geometry.applyMatrix4( mesh.matrixWorld );
158
+
159
+ // ensure our geometry has common attributes
160
+ setCommonAttributes( geometry, {
161
+ attributes: options.attributes,
162
+ normalMapRequired: ! ! mesh.material.normalMap,
163
+ } );
164
+ trimToAttributes( geometry, options.attributes );
165
+
166
+ // create the material index attribute
167
+ const materialIndexAttribute = getGroupMaterialIndicesAttribute( geometry, mesh.material, materials );
168
+ geometry.setAttribute( 'materialIndex', materialIndexAttribute );
169
+
170
+ transformedGeometry.push( geometry );
171
+
172
+ }
173
+
174
+ const textureSet = new Set();
175
+ materials.forEach( material => {
176
+
177
+ for ( const key in material ) {
178
+
179
+ const value = material[ key ];
180
+ if ( value && value.isTexture ) {
181
+
182
+ textureSet.add( value );
183
+
184
+ }
185
+
186
+ }
187
+
188
+ } );
189
+
190
+ const geometry = mergeBufferGeometries( transformedGeometry, false );
191
+ const textures = Array.from( textureSet );
192
+ return { geometry, materials, textures };
193
+
194
+ }