rayzee 7.6.0 → 7.8.0
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/README.md +1 -1
- package/dist/rayzee.es.js +1971 -1971
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +55 -55
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineDefaults.js +45 -1
- package/src/Processor/SceneProcessor.js +165 -112
- package/src/Processor/ShaderBuilder.js +6 -27
- package/src/Stages/NormalDepth.js +16 -27
- package/src/Stages/PathTracer.js +16 -40
- package/src/TSL/DebugKernel.js +0 -2
- package/src/TSL/Debugger.js +1 -8
- package/src/TSL/Displacement.js +10 -10
- package/src/TSL/FinalWriteKernel.js +5 -2
- package/src/TSL/LightsDirect.js +8 -9
- package/src/TSL/ShadeKernel.js +1 -6
- package/src/TSL/TextureSampling.js +155 -34
- package/src/managers/MaterialDataManager.js +71 -39
package/package.json
CHANGED
package/src/EngineDefaults.js
CHANGED
|
@@ -492,9 +492,53 @@ export const TEXTURE_CONSTANTS = {
|
|
|
492
492
|
// guaranteed minimum). The configurable maxTextureSize setting is clamped to this.
|
|
493
493
|
MAX_TEXTURE_SIZE: 8192,
|
|
494
494
|
// Default cap applied when no maxTextureSize is supplied (engine standalone use).
|
|
495
|
-
DEFAULT_MAX_TEXTURE_SIZE: 4096
|
|
495
|
+
DEFAULT_MAX_TEXTURE_SIZE: 4096,
|
|
496
|
+
// Max layers (textures) per bucket array. Also the packing stride for (bucket, layer).
|
|
497
|
+
MAX_TEXTURES_LIMIT: 128,
|
|
498
|
+
// Size buckets per colorSpace pool. Material maps are grouped into this many
|
|
499
|
+
// longest-edge size classes so a small map no longer pays a large neighbour's
|
|
500
|
+
// footprint. 4 → ~8 bound material arrays (4 sRGB + 4 linear).
|
|
501
|
+
MATERIAL_BUCKET_COUNT: 4,
|
|
502
|
+
// Packing stride: a map's stored index encodes bucketId * BUCKET_LAYER_STRIDE + layer.
|
|
503
|
+
// Also the per-bucket layer cap. Kept at the WebGPU portable maxTextureArrayLayers floor (256)
|
|
504
|
+
// so a consolidated bucket (which merges several map types of one size) stays portable.
|
|
505
|
+
BUCKET_LAYER_STRIDE: 256,
|
|
496
506
|
};
|
|
497
507
|
|
|
508
|
+
// Longest-edge size ladder for a given cap, ascending.
|
|
509
|
+
// cap=4096, count=4 → [512, 1024, 2048, 4096].
|
|
510
|
+
export function getTextureBucketSizes( maxTextureSize, count = TEXTURE_CONSTANTS.MATERIAL_BUCKET_COUNT ) {
|
|
511
|
+
|
|
512
|
+
const sizes = [];
|
|
513
|
+
for ( let i = count - 1; i >= 0; i -- ) {
|
|
514
|
+
|
|
515
|
+
sizes.push( Math.max( TEXTURE_CONSTANTS.MIN_TEXTURE_WIDTH, Math.round( maxTextureSize / Math.pow( 2, i ) ) ) );
|
|
516
|
+
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return sizes;
|
|
520
|
+
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Bucket index for a texture, by its power-of-2 longest edge vs the cap's ladder.
|
|
524
|
+
// Larger-than-cap maps land in the top bucket (downscaled to cap, as before).
|
|
525
|
+
export function getTextureBucketId( width, height, maxTextureSize, count = TEXTURE_CONSTANTS.MATERIAL_BUCKET_COUNT ) {
|
|
526
|
+
|
|
527
|
+
const longest = Math.max( width || 1, height || 1 );
|
|
528
|
+
const pot = Math.pow( 2, Math.ceil( Math.log2( longest ) ) );
|
|
529
|
+
const sizes = getTextureBucketSizes( maxTextureSize, count );
|
|
530
|
+
for ( let i = 0; i < sizes.length; i ++ ) if ( pot <= sizes[ i ] ) return i;
|
|
531
|
+
return sizes.length - 1;
|
|
532
|
+
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Pack (bucketId, layer) into the single int slot a material map index occupies.
|
|
536
|
+
export function packTextureIndex( bucketId, layer ) {
|
|
537
|
+
|
|
538
|
+
return bucketId * TEXTURE_CONSTANTS.BUCKET_LAYER_STRIDE + layer;
|
|
539
|
+
|
|
540
|
+
}
|
|
541
|
+
|
|
498
542
|
// Default texture matrix for materials
|
|
499
543
|
export const DEFAULT_TEXTURE_MATRIX = [ 0, 0, 1, 1, 0, 0, 0, 1 ];
|
|
500
544
|
|
|
@@ -9,7 +9,8 @@ import { GeometryExtractor } from './GeometryExtractor.js';
|
|
|
9
9
|
import { EmissiveTriangleBuilder } from './EmissiveTriangleBuilder.js';
|
|
10
10
|
import { updateLoading } from '../Processor/utils.js';
|
|
11
11
|
import { BuildTimer } from './BuildTimer.js';
|
|
12
|
-
import {
|
|
12
|
+
import { SRGBColorSpace } from 'three';
|
|
13
|
+
import { TRIANGLE_DATA_LAYOUT, TEXTURE_CONSTANTS, getTextureBucketId, packTextureIndex } from '../EngineDefaults.js';
|
|
13
14
|
import { fetchAsWorker } from './Workers/fetchAsWorker.js';
|
|
14
15
|
import BVH_WORKER_URL from './Workers/BVHWorker.js?worker&url';
|
|
15
16
|
import BVH_REFIT_WORKER_URL from './Workers/BVHRefitWorker.js?worker&url';
|
|
@@ -79,14 +80,13 @@ export class SceneProcessor {
|
|
|
79
80
|
this._rebuildGeneration = 0; // Monotonic counter to discard stale background rebuilds
|
|
80
81
|
this._pendingRebuilds = new Map(); // meshIndex → worker
|
|
81
82
|
|
|
82
|
-
// Initialize texture references
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
this.
|
|
88
|
-
this.
|
|
89
|
-
this.displacementTextures = null;
|
|
83
|
+
// Initialize texture references.
|
|
84
|
+
// Material maps are packed into consolidated size-bucketed arrays (see _bucketTextures):
|
|
85
|
+
// srgbBucketTextures[K] — albedo + emissive (SRGBColorSpace)
|
|
86
|
+
// linearBucketTextures[K] — normal/bump/roughness/metalness/displacement
|
|
87
|
+
// A material's per-map index encodes (bucket, layer) via packTextureIndex.
|
|
88
|
+
this.srgbBucketTextures = null;
|
|
89
|
+
this.linearBucketTextures = null;
|
|
90
90
|
this.emissiveTriangleData = null;
|
|
91
91
|
this.emissiveTriangleCount = 0;
|
|
92
92
|
this.lightBVHNodeData = null;
|
|
@@ -762,40 +762,45 @@ export class SceneProcessor {
|
|
|
762
762
|
|
|
763
763
|
try {
|
|
764
764
|
|
|
765
|
-
//
|
|
765
|
+
// Group the extractor's per-type arrays into consolidated colorSpace×size-bucket
|
|
766
|
+
// pools, and rewrite each material's per-map index to the packed (bucket, layer)
|
|
767
|
+
// form. Must run BEFORE createMaterialRawData (which reads mat.map etc.).
|
|
768
|
+
const { srgbLists, linearLists, remap } = this._bucketTextures();
|
|
769
|
+
this._remapMaterialTextureIndices( remap );
|
|
770
|
+
|
|
771
|
+
// Material raw data for storage buffers (sync, ~1-5ms) — now holds packed indices.
|
|
766
772
|
if ( this.materials?.length ) {
|
|
767
773
|
|
|
768
774
|
this.materialData = this.textureCreator.createMaterialRawData( this.materials );
|
|
769
775
|
|
|
770
776
|
}
|
|
771
777
|
|
|
772
|
-
//
|
|
773
|
-
//
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
} )
|
|
794
|
-
)
|
|
795
|
-
);
|
|
778
|
+
// One DataArrayTexture per non-empty bucket. The sRGB pool (albedo + emissive — both
|
|
779
|
+
// authored in sRGB per glTF) carries SRGBColorSpace so the GPU decodes sRGB→linear
|
|
780
|
+
// before lighting; the linear pool (normal/roughness/metalness/bump/displacement —
|
|
781
|
+
// data textures) stays linear. Applied consistently across load AND rebuildMaterials
|
|
782
|
+
// (the prior model-load path omitted this, leaving albedo un-decoded / too bright).
|
|
783
|
+
const buildBucket = ( list, srgb ) => list.length === 0
|
|
784
|
+
? Promise.resolve( null )
|
|
785
|
+
: this.textureCreator.createTexturesToDataTexture( list ).then( tex => {
|
|
786
|
+
|
|
787
|
+
if ( tex && srgb ) tex.colorSpace = SRGBColorSpace;
|
|
788
|
+
return tex;
|
|
789
|
+
|
|
790
|
+
} );
|
|
791
|
+
|
|
792
|
+
const [ srgbTextures, linearTextures ] = await Promise.all( [
|
|
793
|
+
Promise.all( srgbLists.map( list => buildBucket( list, true ) ) ),
|
|
794
|
+
Promise.all( linearLists.map( list => buildBucket( list, false ) ) ),
|
|
795
|
+
] );
|
|
796
|
+
|
|
797
|
+
this.srgbBucketTextures = srgbTextures;
|
|
798
|
+
this.linearBucketTextures = linearTextures;
|
|
796
799
|
|
|
797
800
|
this._log( 'Material textures complete', {
|
|
798
801
|
materialData: !! this.materialData,
|
|
802
|
+
srgbBuckets: srgbTextures.map( t => ( t ? `${t.image.width}x${t.image.height}x${t.image.depth}` : '-' ) ).join( ',' ),
|
|
803
|
+
linearBuckets: linearTextures.map( t => ( t ? `${t.image.width}x${t.image.height}x${t.image.depth}` : '-' ) ).join( ',' ),
|
|
799
804
|
} );
|
|
800
805
|
|
|
801
806
|
} catch ( error ) {
|
|
@@ -807,6 +812,96 @@ export class SceneProcessor {
|
|
|
807
812
|
|
|
808
813
|
}
|
|
809
814
|
|
|
815
|
+
/**
|
|
816
|
+
* Group the extractor's seven per-type texture arrays into two consolidated colorSpace
|
|
817
|
+
* pools (sRGB: albedo+emissive; linear: normal/bump/roughness/metalness/displacement),
|
|
818
|
+
* each split into MATERIAL_BUCKET_COUNT longest-edge size buckets. Textures are deduped
|
|
819
|
+
* across types within a (pool, bucket) so a shared image (e.g. ORM) costs one layer.
|
|
820
|
+
* @returns {{ srgbLists: Array<Array>, linearLists: Array<Array>, remap: Object }}
|
|
821
|
+
* bucket lists + per-type remap arrays (old per-type layer → packed bucket index).
|
|
822
|
+
* @private
|
|
823
|
+
*/
|
|
824
|
+
_bucketTextures() {
|
|
825
|
+
|
|
826
|
+
const cap = this.config.maxTextureSize;
|
|
827
|
+
const K = TEXTURE_CONSTANTS.MATERIAL_BUCKET_COUNT;
|
|
828
|
+
const STRIDE = TEXTURE_CONSTANTS.BUCKET_LAYER_STRIDE;
|
|
829
|
+
|
|
830
|
+
const srgbLists = Array.from( { length: K }, () => [] );
|
|
831
|
+
const linearLists = Array.from( { length: K }, () => [] );
|
|
832
|
+
const srgbDedup = Array.from( { length: K }, () => new Map() );
|
|
833
|
+
const linearDedup = Array.from( { length: K }, () => new Map() );
|
|
834
|
+
|
|
835
|
+
// Persistent uuid → packed maps so runtime material edits (updateMaterial) can re-pack
|
|
836
|
+
// a texture's index against the CURRENT bucket layout instead of the stale per-type index.
|
|
837
|
+
this._srgbTexPacked = new Map();
|
|
838
|
+
this._linearTexPacked = new Map();
|
|
839
|
+
|
|
840
|
+
// Assign one texture to its (bucket, layer) within a pool; dedup by source uuid.
|
|
841
|
+
const assign = ( tex, lists, dedup, flat ) => {
|
|
842
|
+
|
|
843
|
+
if ( ! tex || ! tex.image ) return - 1;
|
|
844
|
+
const bucket = getTextureBucketId( tex.image.width, tex.image.height, cap, K );
|
|
845
|
+
const uuid = tex.source?.uuid ?? tex.uuid;
|
|
846
|
+
const seen = dedup[ bucket ].get( uuid );
|
|
847
|
+
if ( seen !== undefined ) return packTextureIndex( bucket, seen );
|
|
848
|
+
if ( lists[ bucket ].length >= STRIDE ) {
|
|
849
|
+
|
|
850
|
+
console.warn( `[SceneProcessor] Texture bucket ${bucket} full (${STRIDE}); dropping a map.` );
|
|
851
|
+
return - 1;
|
|
852
|
+
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
lists[ bucket ].push( tex );
|
|
856
|
+
const layer = lists[ bucket ].length - 1;
|
|
857
|
+
dedup[ bucket ].set( uuid, layer );
|
|
858
|
+
const packed = packTextureIndex( bucket, layer );
|
|
859
|
+
flat.set( uuid, packed );
|
|
860
|
+
return packed;
|
|
861
|
+
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
// Per-type arrays hold unique textures indexed by the layer the extractor assigned
|
|
865
|
+
// (= array position), so remap[type][oldLayer] = packed index.
|
|
866
|
+
const remapType = ( arr, lists, dedup, flat ) => ( arr || [] ).map( tex => assign( tex, lists, dedup, flat ) );
|
|
867
|
+
|
|
868
|
+
const remap = {
|
|
869
|
+
albedo: remapType( this.maps, srgbLists, srgbDedup, this._srgbTexPacked ),
|
|
870
|
+
emissive: remapType( this.emissiveMaps, srgbLists, srgbDedup, this._srgbTexPacked ),
|
|
871
|
+
normal: remapType( this.normalMaps, linearLists, linearDedup, this._linearTexPacked ),
|
|
872
|
+
bump: remapType( this.bumpMaps, linearLists, linearDedup, this._linearTexPacked ),
|
|
873
|
+
roughness: remapType( this.roughnessMaps, linearLists, linearDedup, this._linearTexPacked ),
|
|
874
|
+
metalness: remapType( this.metalnessMaps, linearLists, linearDedup, this._linearTexPacked ),
|
|
875
|
+
displacement: remapType( this.displacementMaps, linearLists, linearDedup, this._linearTexPacked ),
|
|
876
|
+
};
|
|
877
|
+
|
|
878
|
+
return { srgbLists, linearLists, remap };
|
|
879
|
+
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Rewrite each material's per-map index from the extractor's per-type layer to the
|
|
884
|
+
* packed (bucket, layer) index. Idempotency is NOT guaranteed — call exactly once per
|
|
885
|
+
* extraction (materials are freshly extracted on each process/rebuild).
|
|
886
|
+
* @private
|
|
887
|
+
*/
|
|
888
|
+
_remapMaterialTextureIndices( remap ) {
|
|
889
|
+
|
|
890
|
+
const fix = ( v, table ) => ( v >= 0 && v < table.length ? table[ v ] : - 1 );
|
|
891
|
+
for ( const mat of this.materials ) {
|
|
892
|
+
|
|
893
|
+
mat.map = fix( mat.map, remap.albedo );
|
|
894
|
+
mat.emissiveMap = fix( mat.emissiveMap, remap.emissive );
|
|
895
|
+
mat.normalMap = fix( mat.normalMap, remap.normal );
|
|
896
|
+
mat.bumpMap = fix( mat.bumpMap, remap.bump );
|
|
897
|
+
mat.roughnessMap = fix( mat.roughnessMap, remap.roughness );
|
|
898
|
+
mat.metalnessMap = fix( mat.metalnessMap, remap.metalness );
|
|
899
|
+
mat.displacementMap = fix( mat.displacementMap, remap.displacement );
|
|
900
|
+
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
}
|
|
904
|
+
|
|
810
905
|
/**
|
|
811
906
|
* Extract emissive triangles and build Light BVH.
|
|
812
907
|
* MUST run after BVH reordering — emissive data stores triangle indices
|
|
@@ -905,27 +1000,42 @@ export class SceneProcessor {
|
|
|
905
1000
|
*/
|
|
906
1001
|
_disposeTextures() {
|
|
907
1002
|
|
|
908
|
-
|
|
909
|
-
'albedoTextures', 'normalTextures', 'bumpTextures', 'roughnessTextures',
|
|
910
|
-
'metalnessTextures', 'emissiveTextures', 'displacementTextures'
|
|
911
|
-
];
|
|
1003
|
+
this._disposeBucketTextures();
|
|
912
1004
|
|
|
913
|
-
|
|
914
|
-
textureProps.forEach( prop => {
|
|
1005
|
+
}
|
|
915
1006
|
|
|
916
|
-
|
|
1007
|
+
/**
|
|
1008
|
+
* Dispose the consolidated bucket arrays (srgb/linear), each an Array<K> of
|
|
1009
|
+
* DataArrayTexture | null.
|
|
1010
|
+
* @private
|
|
1011
|
+
*/
|
|
1012
|
+
_disposeBucketTextures() {
|
|
917
1013
|
|
|
918
|
-
|
|
1014
|
+
for ( const prop of [ 'srgbBucketTextures', 'linearBucketTextures' ] ) {
|
|
919
1015
|
|
|
920
|
-
|
|
1016
|
+
const arr = this[ prop ];
|
|
1017
|
+
if ( ! arr ) continue;
|
|
1018
|
+
for ( const tex of arr ) {
|
|
921
1019
|
|
|
922
|
-
|
|
1020
|
+
if ( tex && typeof tex.dispose === 'function' ) {
|
|
923
1021
|
|
|
924
|
-
|
|
1022
|
+
try {
|
|
1023
|
+
|
|
1024
|
+
tex.dispose();
|
|
1025
|
+
|
|
1026
|
+
} catch ( error ) {
|
|
1027
|
+
|
|
1028
|
+
console.warn( `[SceneProcessor] Error disposing ${prop}:`, error );
|
|
1029
|
+
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
}
|
|
925
1033
|
|
|
926
1034
|
}
|
|
927
1035
|
|
|
928
|
-
|
|
1036
|
+
this[ prop ] = null;
|
|
1037
|
+
|
|
1038
|
+
}
|
|
929
1039
|
|
|
930
1040
|
}
|
|
931
1041
|
|
|
@@ -969,34 +1079,9 @@ export class SceneProcessor {
|
|
|
969
1079
|
this.displacementMaps = extractedData.displacementMaps;
|
|
970
1080
|
this.sceneFeatures = extractedData.sceneFeatures; // Update material feature flags
|
|
971
1081
|
|
|
972
|
-
//
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
triangles: this.triangleData, // Reuse existing triangle data
|
|
976
|
-
maps: this.maps,
|
|
977
|
-
normalMaps: this.normalMaps,
|
|
978
|
-
bumpMaps: this.bumpMaps,
|
|
979
|
-
roughnessMaps: this.roughnessMaps,
|
|
980
|
-
metalnessMaps: this.metalnessMaps,
|
|
981
|
-
emissiveMaps: this.emissiveMaps,
|
|
982
|
-
displacementMaps: this.displacementMaps,
|
|
983
|
-
bvhRoot: this.bvhRoot // Reuse existing BVH
|
|
984
|
-
};
|
|
985
|
-
|
|
986
|
-
// Create only material and texture-related textures
|
|
987
|
-
const textures = await this.textureCreator.createMaterialTextures( params );
|
|
988
|
-
|
|
989
|
-
// Regenerate raw material data for storage buffers
|
|
990
|
-
this.materialData = this.textureCreator.createMaterialRawData( this.materials );
|
|
991
|
-
|
|
992
|
-
// Update texture references (keep triangle and BVH data unchanged)
|
|
993
|
-
this.albedoTextures = textures.albedoTexture;
|
|
994
|
-
this.normalTextures = textures.normalTexture;
|
|
995
|
-
this.bumpTextures = textures.bumpTexture;
|
|
996
|
-
this.roughnessTextures = textures.roughnessTexture;
|
|
997
|
-
this.metalnessTextures = textures.metalnessTexture;
|
|
998
|
-
this.emissiveTextures = textures.emissiveTexture;
|
|
999
|
-
this.displacementTextures = textures.displacementTexture;
|
|
1082
|
+
// Bucket textures, remap material indices, regenerate raw material data, and
|
|
1083
|
+
// build the consolidated bucket arrays — same path as the initial build.
|
|
1084
|
+
await this._createMaterialTextures();
|
|
1000
1085
|
|
|
1001
1086
|
const duration = performance.now() - startTime;
|
|
1002
1087
|
this._log( `Material rebuild complete (${duration.toFixed( 2 )}ms)`, {
|
|
@@ -1026,37 +1111,7 @@ export class SceneProcessor {
|
|
|
1026
1111
|
*/
|
|
1027
1112
|
_disposeMaterialTextures() {
|
|
1028
1113
|
|
|
1029
|
-
|
|
1030
|
-
'albedoTextures', 'normalTextures',
|
|
1031
|
-
'bumpTextures', 'roughnessTextures', 'metalnessTextures', 'emissiveTextures',
|
|
1032
|
-
'displacementTextures'
|
|
1033
|
-
];
|
|
1034
|
-
|
|
1035
|
-
materialTextureProps.forEach( prop => {
|
|
1036
|
-
|
|
1037
|
-
if ( this[ prop ] ) {
|
|
1038
|
-
|
|
1039
|
-
try {
|
|
1040
|
-
|
|
1041
|
-
if ( typeof this[ prop ].dispose === 'function' ) {
|
|
1042
|
-
|
|
1043
|
-
this[ prop ].dispose();
|
|
1044
|
-
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
} catch ( error ) {
|
|
1048
|
-
|
|
1049
|
-
console.warn( `[SceneProcessor] Error disposing ${prop}:`, error );
|
|
1050
|
-
|
|
1051
|
-
} finally {
|
|
1052
|
-
|
|
1053
|
-
this[ prop ] = null;
|
|
1054
|
-
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
} );
|
|
1114
|
+
this._disposeBucketTextures();
|
|
1060
1115
|
|
|
1061
1116
|
// Clear texture creator cache to prevent stale references
|
|
1062
1117
|
if ( this.textureCreator && this.textureCreator.textureCache ) {
|
|
@@ -1407,14 +1462,12 @@ export class SceneProcessor {
|
|
|
1407
1462
|
}
|
|
1408
1463
|
|
|
1409
1464
|
pathTracer.materialData.setMaterialTextures( {
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
bumpMaps: this.bumpTextures,
|
|
1413
|
-
roughnessMaps: this.roughnessTextures,
|
|
1414
|
-
metalnessMaps: this.metalnessTextures,
|
|
1415
|
-
emissiveMaps: this.emissiveTextures,
|
|
1416
|
-
displacementMaps: this.displacementTextures,
|
|
1465
|
+
srgbBuckets: this.srgbBucketTextures,
|
|
1466
|
+
linearBuckets: this.linearBucketTextures,
|
|
1417
1467
|
} );
|
|
1468
|
+
// Hand the uuid→packed maps to materialData so runtime edits (updateMaterial) can
|
|
1469
|
+
// re-pack a texture's index against this scene's bucket layout.
|
|
1470
|
+
pathTracer.materialData.setTexturePackMaps?.( this._srgbTexPacked, this._linearTexPacked );
|
|
1418
1471
|
|
|
1419
1472
|
if ( this.emissiveTriangleData ) {
|
|
1420
1473
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { texture } from 'three/tsl';
|
|
11
11
|
import { LinearFilter, DataArrayTexture } from 'three';
|
|
12
|
-
import {
|
|
12
|
+
import { setAlphaShadowsUniform } from '../TSL/LightsDirect.js';
|
|
13
13
|
import { setGoboMapsTexture, setIESProfilesTexture } from '../TSL/LightsCore.js';
|
|
14
14
|
|
|
15
15
|
export class ShaderBuilder {
|
|
@@ -18,7 +18,6 @@ export class ShaderBuilder {
|
|
|
18
18
|
|
|
19
19
|
// Previous-frame texture nodes (sample from MRT RenderTarget)
|
|
20
20
|
this.prevColorTexNode = null;
|
|
21
|
-
this.prevNormalDepthTexNode = null;
|
|
22
21
|
this.prevAlbedoTexNode = null;
|
|
23
22
|
|
|
24
23
|
// Scene texture nodes cache (for in-place updates on model change)
|
|
@@ -31,7 +30,6 @@ export class ShaderBuilder {
|
|
|
31
30
|
const nodes = this._sceneTextureNodes;
|
|
32
31
|
|
|
33
32
|
const env = stage.environment;
|
|
34
|
-
const mat = stage.materialData;
|
|
35
33
|
|
|
36
34
|
if ( env.environmentTexture && nodes.envTex ) {
|
|
37
35
|
|
|
@@ -39,13 +37,8 @@ export class ShaderBuilder {
|
|
|
39
37
|
|
|
40
38
|
}
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if ( mat.bumpMaps && nodes.bumpMapsTex ) nodes.bumpMapsTex.value = mat.bumpMaps;
|
|
45
|
-
if ( mat.metalnessMaps && nodes.metalnessMapsTex ) nodes.metalnessMapsTex.value = mat.metalnessMaps;
|
|
46
|
-
if ( mat.roughnessMaps && nodes.roughnessMapsTex ) nodes.roughnessMapsTex.value = mat.roughnessMaps;
|
|
47
|
-
if ( mat.emissiveMaps && nodes.emissiveMapsTex ) nodes.emissiveMapsTex.value = mat.emissiveMaps;
|
|
48
|
-
if ( mat.displacementMaps && nodes.displacementMapsTex ) nodes.displacementMapsTex.value = mat.displacementMaps;
|
|
40
|
+
// Material bucket arrays are owned by PathTracer's independent wavefront nodes
|
|
41
|
+
// (_refreshWfTextureNodes); nothing to swap here.
|
|
49
42
|
if ( stage.goboMaps && nodes.goboMapsTex ) nodes.goboMapsTex.value = stage.goboMaps;
|
|
50
43
|
if ( stage.iesProfiles && nodes.iesProfilesTex ) nodes.iesProfilesTex.value = stage.iesProfiles;
|
|
51
44
|
|
|
@@ -104,7 +97,6 @@ export class ShaderBuilder {
|
|
|
104
97
|
// Previous-frame texture nodes — initialized from readTarget textures
|
|
105
98
|
const readTextures = storageTextures.getReadTextures();
|
|
106
99
|
this.prevColorTexNode = texture( readTextures.color );
|
|
107
|
-
this.prevNormalDepthTexNode = texture( readTextures.normalDepth );
|
|
108
100
|
this.prevAlbedoTexNode = texture( readTextures.albedo );
|
|
109
101
|
|
|
110
102
|
const createArrayPlaceholder = () => {
|
|
@@ -118,14 +110,9 @@ export class ShaderBuilder {
|
|
|
118
110
|
|
|
119
111
|
};
|
|
120
112
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const bumpMapsTex = mat.bumpMaps ? texture( mat.bumpMaps ) : createArrayPlaceholder();
|
|
125
|
-
const metalnessMapsTex = mat.metalnessMaps ? texture( mat.metalnessMaps ) : createArrayPlaceholder();
|
|
126
|
-
const roughnessMapsTex = mat.roughnessMaps ? texture( mat.roughnessMaps ) : createArrayPlaceholder();
|
|
127
|
-
const emissiveMapsTex = mat.emissiveMaps ? texture( mat.emissiveMaps ) : createArrayPlaceholder();
|
|
128
|
-
const displacementMapsTex = mat.displacementMaps ? texture( mat.displacementMaps ) : createArrayPlaceholder();
|
|
113
|
+
// Material map arrays (consolidated size buckets) are owned by PathTracer's
|
|
114
|
+
// independent wavefront nodes + setMaterialBucketTextures/setShadowAlbedoMaps —
|
|
115
|
+
// see PathTracer._buildWavefrontKernels. Nothing material-map related is bound here.
|
|
129
116
|
|
|
130
117
|
// Spot light gobo array — placeholder until GoboManager populates it.
|
|
131
118
|
const goboMapsTex = stage.goboMaps ? texture( stage.goboMaps ) : createArrayPlaceholder();
|
|
@@ -135,16 +122,9 @@ export class ShaderBuilder {
|
|
|
135
122
|
const iesProfilesTex = stage.iesProfiles ? texture( stage.iesProfiles ) : createArrayPlaceholder();
|
|
136
123
|
setIESProfilesTexture( iesProfilesTex );
|
|
137
124
|
|
|
138
|
-
// Set albedo texture array for alpha-aware shadow rays (module-level in LightsDirect.js).
|
|
139
|
-
// Always pass the texture node (real or placeholder) so alpha-cutout code is emitted
|
|
140
|
-
// into the shader at graph construction time. Runtime albedoMapIndex >= 0 guards sampling.
|
|
141
|
-
setShadowAlbedoMaps( albedoMapsTex );
|
|
142
|
-
|
|
143
125
|
const result = {
|
|
144
126
|
triStorage, bvhStorage, matStorage, lightBufferStorage,
|
|
145
127
|
envTex,
|
|
146
|
-
albedoMapsTex, normalMapsTex, bumpMapsTex,
|
|
147
|
-
metalnessMapsTex, roughnessMapsTex, emissiveMapsTex, displacementMapsTex,
|
|
148
128
|
goboMapsTex, iesProfilesTex,
|
|
149
129
|
};
|
|
150
130
|
|
|
@@ -156,7 +136,6 @@ export class ShaderBuilder {
|
|
|
156
136
|
dispose() {
|
|
157
137
|
|
|
158
138
|
this.prevColorTexNode = null;
|
|
159
|
-
this.prevNormalDepthTexNode = null;
|
|
160
139
|
this.prevAlbedoTexNode = null;
|
|
161
140
|
this._sceneTextureNodes = null;
|
|
162
141
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Fn, vec3, vec4, float, int, uint, uvec2, uniform, normalize, mat3, storage, If,
|
|
2
|
-
|
|
2
|
+
textureStore, workgroupId, localId } from 'three/tsl';
|
|
3
3
|
import { RenderTarget, StorageTexture } from 'three/webgpu';
|
|
4
|
-
import { HalfFloatType, RGBAFormat, NearestFilter,
|
|
4
|
+
import { HalfFloatType, RGBAFormat, NearestFilter, Matrix4, Box2, Vector2 } from 'three';
|
|
5
5
|
import { RenderStage, StageExecutionMode } from '../Pipeline/RenderStage.js';
|
|
6
6
|
import { MAX_STORAGE_TEXTURE_SIZE } from '../EngineDefaults.js';
|
|
7
7
|
import { Ray, HitInfo, RayTracingMaterial, UVCache } from '../TSL/Struct.js';
|
|
8
8
|
import { traverseBVH } from '../TSL/BVHTraversal.js';
|
|
9
9
|
import { getMaterial } from '../TSL/Common.js';
|
|
10
|
-
import { computeUVCache, processNormal, processBump } from '../TSL/TextureSampling.js';
|
|
10
|
+
import { computeUVCache, processNormal, processBump, buildBucketTextureNodes, refreshBucketTextureNodes, setMaterialBucketTextures } from '../TSL/TextureSampling.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* NormalDepth — primary-ray G-buffer for SVGF gates.
|
|
@@ -104,22 +104,10 @@ export class NormalDepth extends RenderStage {
|
|
|
104
104
|
this._computeNode = null;
|
|
105
105
|
this._computeBuilt = false;
|
|
106
106
|
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
this.
|
|
111
|
-
this._bumpMapsTex = texture( this._makePlaceholderArray() );
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
_makePlaceholderArray() {
|
|
116
|
-
|
|
117
|
-
const t = new DataArrayTexture( new Uint8Array( [ 128, 128, 255, 255 ] ), 1, 1, 1 );
|
|
118
|
-
t.minFilter = LinearFilter;
|
|
119
|
-
t.magFilter = LinearFilter;
|
|
120
|
-
t.generateMipmaps = false;
|
|
121
|
-
t.needsUpdate = true;
|
|
122
|
-
return t;
|
|
107
|
+
// Independent linear-pool bucket nodes for this pipeline (normal + bump live in the
|
|
108
|
+
// linear pool). Built lazily in _buildCompute from the path tracer's materialData;
|
|
109
|
+
// value-swapped on model load. processNormal/processBump runtime-guard on map indices.
|
|
110
|
+
this._linearBuckets = null;
|
|
123
111
|
|
|
124
112
|
}
|
|
125
113
|
|
|
@@ -185,10 +173,8 @@ export class NormalDepth extends RenderStage {
|
|
|
185
173
|
|
|
186
174
|
}
|
|
187
175
|
|
|
188
|
-
// In-place
|
|
189
|
-
|
|
190
|
-
if ( md?.normalMaps ) this._normalMapsTex.value = md.normalMaps;
|
|
191
|
-
if ( md?.bumpMaps ) this._bumpMapsTex.value = md.bumpMaps;
|
|
176
|
+
// In-place bucket swaps (model change) — graph closes over the nodes, only .value changes.
|
|
177
|
+
if ( this._linearBuckets ) refreshBucketTextureNodes( this._linearBuckets, pt.materialData?.linearBuckets );
|
|
192
178
|
|
|
193
179
|
this._lastTriAttr = pt.triangleStorageAttr || this._lastTriAttr;
|
|
194
180
|
this._lastBvhAttr = pt.bvhStorageAttr || this._lastBvhAttr;
|
|
@@ -203,8 +189,11 @@ export class NormalDepth extends RenderStage {
|
|
|
203
189
|
const triStorage = this._triStorageNode;
|
|
204
190
|
const bvhStorage = this._bvhStorageNode;
|
|
205
191
|
const matStorage = this._matStorageNode;
|
|
206
|
-
|
|
207
|
-
|
|
192
|
+
// Independent linear-pool bucket nodes for this pipeline (normal + bump). The sRGB pool
|
|
193
|
+
// is never sampled here, so it gets placeholders. Publish to the sampling module before
|
|
194
|
+
// the graph is built so processNormal/processBump bake in THESE (per-pipeline) nodes.
|
|
195
|
+
this._linearBuckets = buildBucketTextureNodes( this.pathTracer?.materialData?.linearBuckets );
|
|
196
|
+
setMaterialBucketTextures( buildBucketTextureNodes( null ), this._linearBuckets );
|
|
208
197
|
const camWorld = this.cameraWorldMatrix;
|
|
209
198
|
const camProjInv = this.cameraProjectionMatrixInverse;
|
|
210
199
|
const resW = this.resolutionWidth;
|
|
@@ -266,8 +255,8 @@ export class NormalDepth extends RenderStage {
|
|
|
266
255
|
getMaterial( hit.materialIndex, matStorage )
|
|
267
256
|
).toVar();
|
|
268
257
|
const uvCache = UVCache.wrap( computeUVCache( hit.uv, material ) ).toVar();
|
|
269
|
-
const mapped = processNormal(
|
|
270
|
-
shadingNormal.assign( processBump(
|
|
258
|
+
const mapped = processNormal( hit.normal, material, uvCache ).toVar();
|
|
259
|
+
shadingNormal.assign( processBump( mapped, material, uvCache ) );
|
|
271
260
|
|
|
272
261
|
} );
|
|
273
262
|
|