three-gpu-pathtracer 0.0.22 → 0.0.24

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-gpu-pathtracer",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
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",
@@ -30,35 +30,35 @@
30
30
  "tracer"
31
31
  ],
32
32
  "devDependencies": {
33
- "@lookingglass/webxr": "^0.3.1",
33
+ "@lookingglass/webxr": "^0.6.0",
34
34
  "@monogrid/gainmap-js": "^3.0.5",
35
- "@types/node": "^20.12.7",
36
- "@types/three": "^0.163.0",
37
- "@typescript-eslint/parser": "^6.21.0",
35
+ "@types/node": "^24.3.1",
36
+ "@types/three": "^0.181.0",
37
+ "@typescript-eslint/eslint-plugin": "^8.40.0",
38
38
  "canvas-capture": "^2.0.5",
39
- "eslint": "^7.32.0",
39
+ "eslint": "^8.56.0",
40
40
  "eslint-config-mdcs": "^5.0.0",
41
41
  "node-fetch": "^3.2.9",
42
- "parcel": "^2.12.0",
43
42
  "pixelmatch": "^5.3.0",
44
43
  "pngjs": "^6.0.0",
45
44
  "process": "^0.11.10",
46
45
  "puppeteer": "^15.4.0",
47
46
  "rollup": "^2.70.0",
48
47
  "simple-git": "^3.10.0",
49
- "three": "^0.163.0",
50
- "three-mesh-bvh": "^0.7.4",
51
- "typescript": "5.3.3",
48
+ "three": "^0.181.1",
49
+ "three-mesh-bvh": "^0.9.5",
50
+ "typescript": "^5.9.2",
51
+ "vite": "^6.2.2",
52
52
  "yargs": "^17.5.1"
53
53
  },
54
54
  "peerDependencies": {
55
- "three": ">=0.151.0",
55
+ "three": ">=0.180.0",
56
56
  "three-mesh-bvh": ">=0.7.4",
57
57
  "xatlas-web": "^0.1.0"
58
58
  },
59
59
  "scripts": {
60
- "start": "cd example && parcel serve ./*.html --dist-dir ./dev-bundle/ --no-cache --no-hmr",
61
- "build-examples": "cd example && parcel build ./*.html --dist-dir ./bundle/ --public-url . --no-cache --no-content-hash",
60
+ "start": "vite --config ./vite.config.js",
61
+ "build-examples": "vite build --config ./vite.config.js",
62
62
  "update-screenshots": "node ./scripts/push-screenshots.js",
63
63
  "screenshot-diff": "node ./scripts/regression-test.js",
64
64
  "build": "rollup -c",
@@ -159,6 +159,9 @@ export class PathTracingRenderer {
159
159
 
160
160
  set material( v ) {
161
161
 
162
+ this._fsQuad.material.removeEventListener( 'recompilation', this._compileFunction );
163
+ v.addEventListener( 'recompilation', this._compileFunction );
164
+
162
165
  this._fsQuad.material = v;
163
166
 
164
167
  }
@@ -195,6 +198,12 @@ export class PathTracingRenderer {
195
198
 
196
199
  }
197
200
 
201
+ get isCompiling() {
202
+
203
+ return Boolean( this._compilePromise );
204
+
205
+ }
206
+
198
207
  constructor( renderer ) {
199
208
 
200
209
  this.camera = null;
@@ -212,6 +221,7 @@ export class PathTracingRenderer {
212
221
  this._blendQuad = new FullScreenQuad( new BlendMaterial() );
213
222
  this._task = null;
214
223
  this._currentTile = 0;
224
+ this._compilePromise = null;
215
225
 
216
226
  this._sobolTarget = new SobolNumberMapGenerator().generate( renderer );
217
227
 
@@ -236,6 +246,33 @@ export class PathTracingRenderer {
236
246
  } ),
237
247
  ];
238
248
 
249
+ // function for listening to for triggered compilation so we can wait for compilation to finish
250
+ // before starting to render
251
+ this._compileFunction = () => {
252
+
253
+ const promise = this.compileMaterial( this._fsQuad._mesh );
254
+ promise.then( () => {
255
+
256
+ if ( this._compilePromise === promise ) {
257
+
258
+ this._compilePromise = null;
259
+
260
+ }
261
+
262
+ } );
263
+
264
+ this._compilePromise = promise;
265
+
266
+ };
267
+
268
+ this.material.addEventListener( 'recompilation', this._compileFunction );
269
+
270
+ }
271
+
272
+ compileMaterial() {
273
+
274
+ return this._renderer.compileAsync( this._fsQuad._mesh );
275
+
239
276
  }
240
277
 
241
278
  setCamera( camera ) {
@@ -267,7 +304,6 @@ export class PathTracingRenderer {
267
304
  material.setDefine( 'CAMERA_TYPE', cameraType );
268
305
 
269
306
  this.camera = camera;
270
- // this.reset();
271
307
 
272
308
  }
273
309
 
@@ -334,9 +370,11 @@ export class PathTracingRenderer {
334
370
  this.samples = 0;
335
371
  this._task = null;
336
372
 
373
+ this.material.stratifiedTexture.stableNoise = this.stableNoise;
337
374
  if ( this.stableNoise ) {
338
375
 
339
376
  this.material.seed = 0;
377
+ this.material.stratifiedTexture.reset();
340
378
 
341
379
  }
342
380
 
@@ -344,6 +382,15 @@ export class PathTracingRenderer {
344
382
 
345
383
  update() {
346
384
 
385
+ // ensure we've updated our defines before rendering so we can ensure we
386
+ // can wait for compilation to finish
387
+ this.material.onBeforeRender();
388
+ if ( this.isCompiling ) {
389
+
390
+ return;
391
+
392
+ }
393
+
347
394
  if ( ! this._task ) {
348
395
 
349
396
  this._task = renderTask.call( this );
@@ -95,6 +95,7 @@ export class PathTracingSceneGenerator {
95
95
  this._bvhWorker = null;
96
96
  this._pendingGenerate = null;
97
97
  this._buildAsync = false;
98
+ this._materialUuids = null;
98
99
 
99
100
  }
100
101
 
@@ -176,12 +177,29 @@ export class PathTracingSceneGenerator {
176
177
  // generate the geometry
177
178
  const result = staticGeometryGenerator.generate( geometry );
178
179
  const materials = result.materials;
180
+ let needsMaterialIndexUpdate = result.changeType !== NO_CHANGE || this._materialUuids === null || this._materialUuids.length !== length;
181
+ if ( ! needsMaterialIndexUpdate ) {
182
+
183
+ for ( let i = 0, length = materials.length; i < length; i ++ ) {
184
+
185
+ const material = materials[ i ];
186
+ if ( material.uuid !== this._materialUuids[ i ] ) {
187
+
188
+ needsMaterialIndexUpdate = true;
189
+ break;
190
+
191
+ }
192
+
193
+ }
194
+
195
+ }
196
+
179
197
  const textures = getTextures( materials );
180
198
  const { lights, iesTextures } = getLights( objects );
181
-
182
- if ( result.changeType !== NO_CHANGE ) {
199
+ if ( needsMaterialIndexUpdate ) {
183
200
 
184
201
  updateMaterialIndexAttribute( geometry, materials, materials );
202
+ this._materialUuids = materials.map( material => material.uuid );
185
203
 
186
204
  }
187
205
 
@@ -225,6 +243,7 @@ export class PathTracingSceneGenerator {
225
243
  return {
226
244
  bvhChanged: result.changeType !== NO_CHANGE,
227
245
  bvh: this.bvh,
246
+ needsMaterialIndexUpdate,
228
247
  lights,
229
248
  iesTextures,
230
249
  geometry,
@@ -5,6 +5,7 @@ import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
5
5
  import { GradientEquirectTexture } from '../textures/GradientEquirectTexture.js';
6
6
  import { getIesTextures, getLights, getTextures } from './utils/sceneUpdateUtils.js';
7
7
  import { ClampedInterpolationMaterial } from '../materials/fullscreen/ClampedInterpolationMaterial.js';
8
+ import { CubeToEquirectGenerator } from '../utils/CubeToEquirectGenerator.js';
8
9
 
9
10
  function supportsFloatBlending( renderer ) {
10
11
 
@@ -81,6 +82,24 @@ export class WebGLPathTracer {
81
82
 
82
83
  }
83
84
 
85
+ get stableNoise() {
86
+
87
+ return this._pathTracer.stableNoise;
88
+
89
+ }
90
+
91
+ set stableNoise( v ) {
92
+
93
+ this._pathTracer.stableNoise = v;
94
+
95
+ }
96
+
97
+ get isCompiling() {
98
+
99
+ return Boolean( this._pathTracer.isCompiling );
100
+
101
+ }
102
+
84
103
  constructor( renderer ) {
85
104
 
86
105
  // members
@@ -89,6 +108,7 @@ export class WebGLPathTracer {
89
108
  this._pathTracer = new PathTracingRenderer( renderer );
90
109
  this._queueReset = false;
91
110
  this._clock = new Clock();
111
+ this._compilePromise = null;
92
112
 
93
113
  this._lowResPathTracer = new PathTracingRenderer( renderer );
94
114
  this._lowResPathTracer.tiles.set( 1, 1 );
@@ -101,6 +121,10 @@ export class WebGLPathTracer {
101
121
  } ) );
102
122
  this._materials = null;
103
123
 
124
+ this._previousEnvironment = null;
125
+ this._previousBackground = null;
126
+ this._internalBackground = null;
127
+
104
128
  // options
105
129
  this.renderDelay = 100;
106
130
  this.minSamples = 5;
@@ -229,6 +253,13 @@ export class WebGLPathTracer {
229
253
  const scene = this.scene;
230
254
  const material = this._pathTracer.material;
231
255
 
256
+ if ( this._internalBackground ) {
257
+
258
+ this._internalBackground.dispose();
259
+ this._internalBackground = null;
260
+
261
+ }
262
+
232
263
  // update scene background
233
264
  material.backgroundBlur = scene.backgroundBlurriness;
234
265
  material.backgroundIntensity = scene.backgroundIntensity ?? 1;
@@ -256,6 +287,17 @@ export class WebGLPathTracer {
256
287
  material.backgroundMap = colorBackground;
257
288
  material.backgroundAlpha = 1;
258
289
 
290
+ } else if ( scene.background.isCubeTexture ) {
291
+
292
+ if ( scene.background !== this._previousBackground ) {
293
+
294
+ const background = new CubeToEquirectGenerator( this._renderer ).generate( scene.background );
295
+ this._internalBackground = background;
296
+ material.backgroundMap = background;
297
+ material.backgroundAlpha = 1;
298
+
299
+ }
300
+
259
301
  } else {
260
302
 
261
303
  material.backgroundMap = scene.background;
@@ -264,26 +306,32 @@ export class WebGLPathTracer {
264
306
  }
265
307
 
266
308
  // update scene environment
267
- material.environmentIntensity = scene.environmentIntensity ?? 1;
309
+ material.environmentIntensity = scene.environment !== null ? ( scene.environmentIntensity ?? 1 ) : 0;
268
310
  material.environmentRotation.makeRotationFromEuler( scene.environmentRotation ).invert();
269
311
  if ( this._previousEnvironment !== scene.environment ) {
270
312
 
271
- if ( scene.environment ) {
313
+ if ( scene.environment !== null ) {
272
314
 
273
- // TODO: Consider setting this to the highest supported bit depth by checking for
274
- // OES_texture_float_linear or OES_texture_half_float_linear. Requires changes to
275
- // the equirect uniform
276
- material.envMapInfo.updateFrom( scene.environment );
315
+ if ( scene.environment.isCubeTexture ) {
277
316
 
278
- } else {
317
+ const environment = new CubeToEquirectGenerator( this._renderer ).generate( scene.environment );
318
+ material.envMapInfo.updateFrom( environment );
319
+
320
+ } else {
279
321
 
280
- material.environmentIntensity = 0;
322
+ // TODO: Consider setting this to the highest supported bit depth by checking for
323
+ // OES_texture_float_linear or OES_texture_half_float_linear. Requires changes to
324
+ // the equirect uniform
325
+ material.envMapInfo.updateFrom( scene.environment );
326
+
327
+ }
281
328
 
282
329
  }
283
330
 
284
331
  }
285
332
 
286
333
  this._previousEnvironment = scene.environment;
334
+ this._previousBackground = scene.background;
287
335
  this.reset();
288
336
 
289
337
  }
@@ -295,6 +343,7 @@ export class WebGLPathTracer {
295
343
  geometry,
296
344
  bvh,
297
345
  bvhChanged,
346
+ needsMaterialIndexUpdate,
298
347
  } = results;
299
348
 
300
349
  this._materials = materials;
@@ -312,6 +361,10 @@ export class WebGLPathTracer {
312
361
  geometry.attributes.color,
313
362
  );
314
363
 
364
+ }
365
+
366
+ if ( needsMaterialIndexUpdate ) {
367
+
315
368
  material.materialIndexAttribute.updateFrom( geometry.attributes.materialIndex );
316
369
 
317
370
  }
@@ -354,7 +407,7 @@ export class WebGLPathTracer {
354
407
  // render the path tracing sample after enough time has passed
355
408
  const delta = clock.getDelta() * 1e3;
356
409
  const elapsedTime = clock.getElapsedTime() * 1e3;
357
- if ( ! this.pausePathTracing && this.enablePathTracing && this.renderDelay <= elapsedTime ) {
410
+ if ( ! this.pausePathTracing && this.enablePathTracing && this.renderDelay <= elapsedTime && ! this.isCompiling ) {
358
411
 
359
412
  pathTracer.update();
360
413
 
@@ -387,7 +440,7 @@ export class WebGLPathTracer {
387
440
  // render the fallback if we haven't rendered enough samples, are paused, or are occluded
388
441
  if ( ! this.enablePathTracing || this.samples < minSamples || quad.material.opacity < 1 ) {
389
442
 
390
- if ( this.dynamicLowRes ) {
443
+ if ( this.dynamicLowRes && ! this.isCompiling ) {
391
444
 
392
445
  if ( lowResPathTracer.samples < 1 ) {
393
446
 
@@ -402,7 +455,9 @@ export class WebGLPathTracer {
402
455
  quad.render( renderer );
403
456
  quad.material.opacity = currentOpacity;
404
457
 
405
- } else if ( this.rasterizeScene ) {
458
+ }
459
+
460
+ if ( ! this.dynamicLowRes && this.rasterizeScene || this.dynamicLowRes && this.isCompiling ) {
406
461
 
407
462
  this.rasterizeSceneCallback( this.scene, this.camera );
408
463
 
@@ -440,8 +495,8 @@ export class WebGLPathTracer {
440
495
 
441
496
  dispose() {
442
497
 
443
- this._renderQuad.dispose();
444
- this._renderQuad.material.dispose();
498
+ this._quad.dispose();
499
+ this._quad.material.dispose();
445
500
  this._pathTracer.dispose();
446
501
 
447
502
  }
@@ -1,6 +1,7 @@
1
1
  import { BufferGeometry } from 'three';
2
2
  import { MeshDiff } from './MeshDiff.js';
3
3
  import { convertToStaticGeometry } from './convertToStaticGeometry.js';
4
+ import { validateAttributes } from './BufferAttributeUtils.js';
4
5
 
5
6
  export class BakedGeometry extends BufferGeometry {
6
7
 
@@ -13,6 +14,28 @@ export class BakedGeometry extends BufferGeometry {
13
14
 
14
15
  }
15
16
 
17
+ // returns whether the passed mesh is compatible with this baked geometry
18
+ // such that it can be updated without resizing attributes
19
+ isCompatible( mesh, attributes ) {
20
+
21
+ const geometry = mesh.geometry;
22
+ for ( let i = 0; i < attributes.length; i ++ ) {
23
+
24
+ const key = attributes[ i ];
25
+ const attr1 = geometry.attributes[ key ];
26
+ const attr2 = this.attributes[ key ];
27
+ if ( attr1 && ! validateAttributes( attr1, attr2 ) ) {
28
+
29
+ return false;
30
+
31
+ }
32
+
33
+ }
34
+
35
+ return true;
36
+
37
+ }
38
+
16
39
  updateFrom( mesh, options ) {
17
40
 
18
41
  const diff = this._diff;
@@ -41,12 +41,18 @@ export function createAttributeClone( attr, countOverride = null ) {
41
41
 
42
42
  }
43
43
 
44
- // Confirms that the two provided attributes are compatible
44
+ // Confirms that the two provided attributes are compatible. Returns false if they are not.
45
45
  export function validateAttributes( attr1, attr2 ) {
46
46
 
47
47
  if ( ! attr1 && ! attr2 ) {
48
48
 
49
- return;
49
+ return true;
50
+
51
+ }
52
+
53
+ if ( Boolean( attr1 ) !== Boolean( attr2 ) ) {
54
+
55
+ return false;
50
56
 
51
57
  }
52
58
 
@@ -57,8 +63,10 @@ export function validateAttributes( attr1, attr2 ) {
57
63
 
58
64
  if ( ! sameCount || ! sameNormalized || ! sameType || ! sameItemSize ) {
59
65
 
60
- throw new Error();
66
+ return false;
61
67
 
62
68
  }
63
69
 
70
+ return true;
71
+
64
72
  }
@@ -1,29 +1,22 @@
1
1
  import { Matrix4 } from 'three';
2
2
  import { bufferToHash } from '../../utils/bufferToHash.js';
3
3
 
4
- function attributeSort( a, b ) {
5
-
6
- if ( a.uuid > b.uuid ) return 1;
7
- if ( a.uuid < b.uuid ) return - 1;
8
- return 0;
9
-
10
- }
11
-
12
4
  function getGeometryHash( geometry ) {
13
5
 
14
- let hash = '';
6
+ let hash = geometry.uuid;
15
7
  const attributes = Object.values( geometry.attributes );
16
8
  if ( geometry.index ) {
17
9
 
18
10
  attributes.push( geometry.index );
11
+ hash += `index|${ geometry.index.version }`;
19
12
 
20
13
  }
21
14
 
22
- attributes.sort( attributeSort );
23
-
24
- for ( const attr of attributes ) {
15
+ const keys = Object.keys( attributes ).sort();
16
+ for ( const key of keys ) {
25
17
 
26
- hash += `${ attr.uuid }_${ attr.version }|`;
18
+ const attr = attributes[ key ];
19
+ hash += `${ key }_${ attr.version }|`;
27
20
 
28
21
  }
29
22
 
@@ -171,15 +171,24 @@ export class StaticGeometryGenerator {
171
171
  unusedMeshKeys.delete( meshKey );
172
172
 
173
173
  // initialize the intermediate geometry
174
- if ( ! _intermediateGeometry.has( meshKey ) ) {
174
+ // if the mesh and source geometry have changed in such a way that they are no longer
175
+ // compatible then regenerate the baked geometry from scratch
176
+ let geom = _intermediateGeometry.get( meshKey );
177
+ if ( ! geom || ! geom.isCompatible( mesh, this.attributes ) ) {
175
178
 
176
- _intermediateGeometry.set( meshKey, new BakedGeometry() );
179
+ if ( geom ) {
180
+
181
+ geom.dispose();
182
+
183
+ }
184
+
185
+ geom = new BakedGeometry();
186
+ _intermediateGeometry.set( meshKey, geom );
177
187
 
178
188
  }
179
189
 
180
190
  // transform the geometry into the intermediate buffer geometry, saving whether
181
191
  // or not it changed.
182
- const geom = _intermediateGeometry.get( meshKey );
183
192
  if ( geom.updateFrom( mesh, convertOptions ) ) {
184
193
 
185
194
  // TODO: provide option for only generating the set of attributes that are present
@@ -231,6 +240,12 @@ export class StaticGeometryGenerator {
231
240
 
232
241
  // get the list of geometries to merge
233
242
  let forceUpdate = false;
243
+ if ( meshes.length !== previousMergeInfo.length ) {
244
+
245
+ forceUpdate = true;
246
+
247
+ }
248
+
234
249
  for ( let i = 0, l = meshes.length; i < l; i ++ ) {
235
250
 
236
251
  const mesh = meshes[ i ];
@@ -205,7 +205,20 @@ export function mergeGeometries( geometries, options = {}, targetGeometry = new
205
205
  const attr = geometry.getAttribute( key );
206
206
  if ( ! skip ) {
207
207
 
208
- copyAttributeContents( attr, targetAttribute, offset );
208
+ if ( key === 'color' && targetAttribute.itemSize !== attr.itemSize ) {
209
+
210
+ // make sure the color attribute is aligned with itemSize 3 to 4
211
+ for ( let index = offset, l = attr.count; index < l; index ++ ) {
212
+
213
+ attr.setXYZW( index, targetAttribute.getX( index ), targetAttribute.getY( index ), targetAttribute.getZ( index ), 1.0 );
214
+
215
+ }
216
+
217
+ } else {
218
+
219
+ copyAttributeContents( attr, targetAttribute, offset );
220
+
221
+ }
209
222
 
210
223
  }
211
224
 
package/src/index.d.ts CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  Scene,
22
22
  PMREMGenerator
23
23
  } from 'three';
24
- import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass';
24
+ import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
25
25
  import { MeshBVH, MeshBVHOptions } from 'three-mesh-bvh';
26
26
 
27
27
  // three.js type augmentation
@@ -57,6 +57,7 @@ export interface PathTracingSceneGeneratorResult {
57
57
  lights: Array<Light>;
58
58
  iesTextures: Array<DataTexture>;
59
59
  geometry: BufferGeometry;
60
+ needsMaterialIndexUpdate: boolean;
60
61
  materials: Array<Material>;
61
62
  textures: Array<Texture>;
62
63
  objects: Array<Object3D>;
@@ -2,6 +2,17 @@ import { ShaderMaterial } from 'three';
2
2
 
3
3
  export class MaterialBase extends ShaderMaterial {
4
4
 
5
+ set needsUpdate( v ) {
6
+
7
+ super.needsUpdate = true;
8
+ this.dispatchEvent( {
9
+
10
+ type: 'recompilation',
11
+
12
+ } );
13
+
14
+ }
15
+
5
16
  constructor( shader ) {
6
17
 
7
18
  super( shader );
@@ -10,7 +10,7 @@ import { PhysicalCameraUniform } from '../../uniforms/PhysicalCameraUniform.js';
10
10
  import { EquirectHdrInfoUniform } from '../../uniforms/EquirectHdrInfoUniform.js';
11
11
  import { LightsInfoUniformStruct } from '../../uniforms/LightsInfoUniformStruct.js';
12
12
  import { AttributesTextureArray } from '../../uniforms/AttributesTextureArray.js';
13
- import { MaterialsTexture } from '../../uniforms/MaterialsTexture.js';
13
+ import { MaterialsTexture, MATERIAL_PIXELS } from '../../uniforms/MaterialsTexture.js';
14
14
  import { RenderTarget2DArray } from '../../uniforms/RenderTarget2DArray.js';
15
15
  import { StratifiedSamplesTexture } from '../../uniforms/StratifiedSamplesTexture.js';
16
16
  import { BlueNoiseTexture } from '../../textures/BlueNoiseTexture.js';
@@ -66,6 +66,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
66
66
  ATTR_TANGENT: 1,
67
67
  ATTR_UV: 2,
68
68
  ATTR_COLOR: 3,
69
+ MATERIAL_PIXELS: MATERIAL_PIXELS,
69
70
  },
70
71
 
71
72
  uniforms: {
@@ -97,7 +97,8 @@ export const attenuate_hit_function = /* glsl */`
97
97
  // alphaMap
98
98
  if ( material.alphaMap != - 1 ) {
99
99
 
100
- albedo.a *= texture2D( textures, vec3( uv, material.alphaMap ) ).x;
100
+ vec3 uvPrime = material.alphaMapTransform * vec3( uv, 1 );
101
+ albedo.a *= texture2D( textures, vec3( uvPrime.xy, material.alphaMap ) ).x;
101
102
 
102
103
  }
103
104
 
@@ -60,7 +60,7 @@ export const camera_util_functions = /* glsl */`
60
60
  vec3 shapeUVW= rand3( 1 );
61
61
  int blades = physicalCamera.apertureBlades;
62
62
  float anamorphicRatio = physicalCamera.anamorphicRatio;
63
- vec2 apertureSample = blades == 0 ? sampleCircle( shapeUVW.xy ) : sampleRegularPolygon( blades, shapeUVW );
63
+ vec2 apertureSample = sampleAperture( blades, shapeUVW );
64
64
  apertureSample *= physicalCamera.bokehSize * 0.5 * 1e-3;
65
65
 
66
66
  // rotate the aperture shape
@@ -48,7 +48,8 @@ export const get_surface_record_function = /* glsl */`
48
48
  // alphaMap
49
49
  if ( material.alphaMap != - 1 ) {
50
50
 
51
- albedo.a *= texture2D( textures, vec3( uv, material.alphaMap ) ).x;
51
+ vec3 uvPrime = material.alphaMapTransform * vec3( uv, 1 );
52
+ albedo.a *= texture2D( textures, vec3( uvPrime.xy, material.alphaMap ) ).x;
52
53
 
53
54
  }
54
55
 
@@ -1,6 +1,7 @@
1
1
  import { TangentSpaceNormalMap, Vector2 } from 'three';
2
2
  import { MaterialBase } from '../MaterialBase.js';
3
3
  import { MeshBVHUniformStruct, BVHShaderGLSL } from 'three-mesh-bvh';
4
+ import { MATERIAL_PIXELS } from '../../uniforms/MaterialsTexture.js';
4
5
 
5
6
  import * as StructsGLSL from '../../shader/structs/index.js';
6
7
  import * as SamplingGLSL from '../../shader/sampling/index.js';
@@ -43,6 +44,7 @@ export class AmbientOcclusionMaterial extends MaterialBase {
43
44
 
44
45
  defines: {
45
46
  SAMPLES: 10,
47
+ MATERIAL_PIXELS: MATERIAL_PIXELS,
46
48
  },
47
49
 
48
50
  uniforms: {
@@ -7,7 +7,7 @@ export const inside_fog_volume_function = /* glsl */`
7
7
  // returns whether the given material is a fog material or not
8
8
  bool isMaterialFogVolume( sampler2D materials, uint materialIndex ) {
9
9
 
10
- uint i = materialIndex * 45u;
10
+ uint i = materialIndex * uint( MATERIAL_PIXELS );
11
11
  vec4 s14 = texelFetch1D( materials, i + 14u );
12
12
  return bool( int( s14.b ) & 4 );
13
13