rayzee 7.8.0 → 7.9.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/assets/{BVHSubtreeWorker-D9GImjGj.js → BVHSubtreeWorker-sNzvxn66.js} +2 -2
- package/dist/assets/{BVHSubtreeWorker-D9GImjGj.js.map → BVHSubtreeWorker-sNzvxn66.js.map} +1 -1
- package/dist/assets/{BVHWorker-CNJ0UBQz.js → BVHWorker-CiVdFrwe.js} +2 -2
- package/dist/assets/{BVHWorker-CNJ0UBQz.js.map → BVHWorker-CiVdFrwe.js.map} +1 -1
- package/dist/rayzee.es.js +714 -693
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +21 -21
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineEvents.js +1 -0
- package/src/Passes/OIDNDenoiser.js +25 -1
- package/src/PathTracerApp.js +36 -0
- package/src/Processor/AssetLoader.js +3 -0
- package/src/Processor/GeometryExtractor.js +35 -9
- package/src/Processor/PackedRayBuffer.js +15 -1
- package/src/Processor/QueueManager.js +13 -1
- package/src/Processor/SceneProcessor.js +11 -1
- package/src/Processor/TextureCreator.js +16 -32
- package/src/Processor/Workers/BVHWorker.js +6 -1
- package/src/Stages/EdgeFilter.js +4 -3
- package/src/Stages/MotionVector.js +4 -4
- package/src/Stages/NormalDepth.js +4 -3
- package/src/Stages/PathTracer.js +4 -3
- package/src/TSL/GenerateKernel.js +5 -1
- package/src/TSL/LightsIndirect.js +15 -9
- package/src/TSL/LightsSampling.js +9 -19
- package/src/TSL/Random.js +11 -1
- package/src/managers/EnvironmentManager.js +11 -0
- package/src/managers/RenderTargetManager.js +0 -522
|
@@ -360,7 +360,6 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
360
360
|
If( totalLights.greaterThan( int( 0 ) ), () => {
|
|
361
361
|
|
|
362
362
|
const totalWeight = float( 0.0 ).toVar();
|
|
363
|
-
const lightIndex = int( 0 ).toVar();
|
|
364
363
|
|
|
365
364
|
// Reservoir state: winning light's type/index/importance.
|
|
366
365
|
const selectedType = int( - 1 ).toVar(); // 0=dir, 1=area, 2=point, 3=spot
|
|
@@ -375,7 +374,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
375
374
|
|
|
376
375
|
Loop( { start: int( 0 ), end: numDirectionalLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
377
376
|
|
|
378
|
-
If(
|
|
377
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
379
378
|
|
|
380
379
|
const light = DirectionalLight.wrap( getDirectionalLight( directionalLightsBuffer, i ) );
|
|
381
380
|
const importance = calculateDirectionalLightImportance( light, normal, material, bounceIndex ).toVar();
|
|
@@ -389,7 +388,6 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
389
388
|
selectedImportance.assign( importance );
|
|
390
389
|
|
|
391
390
|
} );
|
|
392
|
-
lightIndex.addAssign( 1 );
|
|
393
391
|
|
|
394
392
|
} );
|
|
395
393
|
|
|
@@ -401,7 +399,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
401
399
|
|
|
402
400
|
Loop( { start: int( 0 ), end: numAreaLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
403
401
|
|
|
404
|
-
If(
|
|
402
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
405
403
|
|
|
406
404
|
const light = AreaLight.wrap( getAreaLight( areaLightsBuffer, i ) );
|
|
407
405
|
const importance = select( light.intensity.greaterThan( 0.0 ), estimateLightImportance( light, rayOrigin, normal, material ), float( 0.0 ) ).toVar();
|
|
@@ -415,7 +413,6 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
415
413
|
selectedImportance.assign( importance );
|
|
416
414
|
|
|
417
415
|
} );
|
|
418
|
-
lightIndex.addAssign( 1 );
|
|
419
416
|
|
|
420
417
|
} );
|
|
421
418
|
|
|
@@ -427,7 +424,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
427
424
|
|
|
428
425
|
Loop( { start: int( 0 ), end: numPointLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
429
426
|
|
|
430
|
-
If(
|
|
427
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
431
428
|
|
|
432
429
|
const light = PointLight.wrap( getPointLight( pointLightsBuffer, i ) );
|
|
433
430
|
const importance = calculatePointLightImportance( light, rayOrigin, normal, material ).toVar();
|
|
@@ -441,7 +438,6 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
441
438
|
selectedImportance.assign( importance );
|
|
442
439
|
|
|
443
440
|
} );
|
|
444
|
-
lightIndex.addAssign( 1 );
|
|
445
441
|
|
|
446
442
|
} );
|
|
447
443
|
|
|
@@ -453,7 +449,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
453
449
|
|
|
454
450
|
Loop( { start: int( 0 ), end: numSpotLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
455
451
|
|
|
456
|
-
If(
|
|
452
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
457
453
|
|
|
458
454
|
const light = SpotLight.wrap( getSpotLight( spotLightsBuffer, i ) );
|
|
459
455
|
const importance = calculateSpotLightImportance( light, rayOrigin, normal, material ).toVar();
|
|
@@ -467,7 +463,6 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
467
463
|
selectedImportance.assign( importance );
|
|
468
464
|
|
|
469
465
|
} );
|
|
470
|
-
lightIndex.addAssign( 1 );
|
|
471
466
|
|
|
472
467
|
} );
|
|
473
468
|
|
|
@@ -762,7 +757,7 @@ export const calculateMaterialPDF = Fn( ( [ viewDir, lightDir, normal, material
|
|
|
762
757
|
} );
|
|
763
758
|
|
|
764
759
|
// Total light-selection weight at a shading point, replicating the NEE reservoir's
|
|
765
|
-
// importance accumulation (same per-type importance fns, same 16-light cap + order).
|
|
760
|
+
// importance accumulation (same per-type importance fns, same per-type 16-light cap + order).
|
|
766
761
|
// Used to reconstruct the exact NEE selection pdf for MIS on the BSDF-hit path —
|
|
767
762
|
// the analogue of Cycles' light_tree_pdf re-walk. Without it, the BSDF-hit MIS
|
|
768
763
|
// partner assumes uniform 1/N selection, which disagrees with the importance-
|
|
@@ -776,17 +771,15 @@ export const computeTotalLightImportance = Fn( ( [
|
|
|
776
771
|
] ) => {
|
|
777
772
|
|
|
778
773
|
const totalWeight = float( 0.0 ).toVar();
|
|
779
|
-
const lightIndex = int( 0 ).toVar();
|
|
780
774
|
|
|
781
775
|
If( numDirectionalLights.greaterThan( int( 0 ) ), () => {
|
|
782
776
|
|
|
783
777
|
Loop( { start: int( 0 ), end: numDirectionalLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
784
778
|
|
|
785
|
-
If(
|
|
779
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
786
780
|
|
|
787
781
|
const light = DirectionalLight.wrap( getDirectionalLight( directionalLightsBuffer, i ) );
|
|
788
782
|
totalWeight.addAssign( calculateDirectionalLightImportance( light, normal, material, bounceIndex ) );
|
|
789
|
-
lightIndex.addAssign( 1 );
|
|
790
783
|
|
|
791
784
|
} );
|
|
792
785
|
|
|
@@ -798,11 +791,10 @@ export const computeTotalLightImportance = Fn( ( [
|
|
|
798
791
|
|
|
799
792
|
Loop( { start: int( 0 ), end: numAreaLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
800
793
|
|
|
801
|
-
If(
|
|
794
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
802
795
|
|
|
803
796
|
const light = AreaLight.wrap( getAreaLight( areaLightsBuffer, i ) );
|
|
804
797
|
totalWeight.addAssign( select( light.intensity.greaterThan( 0.0 ), estimateLightImportance( light, rayOrigin, normal, material ), float( 0.0 ) ) );
|
|
805
|
-
lightIndex.addAssign( 1 );
|
|
806
798
|
|
|
807
799
|
} );
|
|
808
800
|
|
|
@@ -814,11 +806,10 @@ export const computeTotalLightImportance = Fn( ( [
|
|
|
814
806
|
|
|
815
807
|
Loop( { start: int( 0 ), end: numPointLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
816
808
|
|
|
817
|
-
If(
|
|
809
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
818
810
|
|
|
819
811
|
const light = PointLight.wrap( getPointLight( pointLightsBuffer, i ) );
|
|
820
812
|
totalWeight.addAssign( calculatePointLightImportance( light, rayOrigin, normal, material ) );
|
|
821
|
-
lightIndex.addAssign( 1 );
|
|
822
813
|
|
|
823
814
|
} );
|
|
824
815
|
|
|
@@ -830,11 +821,10 @@ export const computeTotalLightImportance = Fn( ( [
|
|
|
830
821
|
|
|
831
822
|
Loop( { start: int( 0 ), end: numSpotLights, type: 'int', condition: '<' }, ( { i } ) => {
|
|
832
823
|
|
|
833
|
-
If(
|
|
824
|
+
If( i.lessThan( int( 16 ) ), () => {
|
|
834
825
|
|
|
835
826
|
const light = SpotLight.wrap( getSpotLight( spotLightsBuffer, i ) );
|
|
836
827
|
totalWeight.addAssign( calculateSpotLightImportance( light, rayOrigin, normal, material ) );
|
|
837
|
-
lightIndex.addAssign( 1 );
|
|
838
828
|
|
|
839
829
|
} );
|
|
840
830
|
|
package/src/TSL/Random.js
CHANGED
|
@@ -187,7 +187,17 @@ const computeSTBNAtlasCoord = ( pixelCoords, sampleIndex, dimensionIndex, frame
|
|
|
187
187
|
export const sampleSTBN2D = ( pixelCoords, sampleIndex, dimensionPairIndex, frame ) => {
|
|
188
188
|
|
|
189
189
|
const coord = computeSTBNAtlasCoord( pixelCoords, sampleIndex, dimensionPairIndex, frame );
|
|
190
|
-
|
|
190
|
+
const raw = stbnVec2TextureNode.load( coord ).xy;
|
|
191
|
+
|
|
192
|
+
// The atlas has only 64 temporal slices, so frame N and N+64 read the same slice: the
|
|
193
|
+
// sample repeats and accumulation stops improving past 64 frames. Decorrelate across
|
|
194
|
+
// 64-frame cycles with a Cranley-Patterson rotation (toroidal shift) by an R2 offset
|
|
195
|
+
// keyed on the cycle index (frame >> 6). The offset is uniform per cycle, preserving
|
|
196
|
+
// spatial and within-window temporal blue noise; cycle 0's offset is 0, so frames
|
|
197
|
+
// 0-63 stay bit-identical. A toroidal shift of uniform samples stays uniform (unbiased).
|
|
198
|
+
const cycle = float( uint( frame ).shiftRight( uint( 6 ) ) );
|
|
199
|
+
const rotation = fract( vec2( R2_A1, R2_A2 ).mul( cycle ) );
|
|
200
|
+
return fract( raw.add( rotation ) );
|
|
191
201
|
|
|
192
202
|
};
|
|
193
203
|
|
|
@@ -360,6 +360,17 @@ export class EnvironmentManager {
|
|
|
360
360
|
*/
|
|
361
361
|
async setEnvironmentMap( envMap ) {
|
|
362
362
|
|
|
363
|
+
// Free the outgoing env texture's GPU memory before it is orphaned. Skip: the
|
|
364
|
+
// reusable placeholder, an idempotent re-set of the same texture, and the HDRI
|
|
365
|
+
// stashed by setMode() for a later sky→hdri restore. Only when actually
|
|
366
|
+
// installing a new non-null texture (the null-env branch keeps the old ref).
|
|
367
|
+
const oldTex = this.environmentTexture;
|
|
368
|
+
if ( envMap && oldTex && oldTex !== envMap && oldTex !== this._envPlaceholder && oldTex !== this._previousHDRI ) {
|
|
369
|
+
|
|
370
|
+
oldTex.dispose?.();
|
|
371
|
+
|
|
372
|
+
}
|
|
373
|
+
|
|
363
374
|
this.scene.environment = envMap;
|
|
364
375
|
this.setEnvironmentTexture( envMap );
|
|
365
376
|
|
|
@@ -1,522 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RenderTargetManager.js
|
|
3
|
-
* Manages render targets, MRT textures, and efficient copying operations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
WebGLRenderTarget,
|
|
8
|
-
FloatType,
|
|
9
|
-
NearestFilter,
|
|
10
|
-
LinearSRGBColorSpace,
|
|
11
|
-
ShaderMaterial,
|
|
12
|
-
GLSL3
|
|
13
|
-
} from 'three';
|
|
14
|
-
import { FullScreenQuad } from 'three/addons/postprocessing/Pass.js';
|
|
15
|
-
|
|
16
|
-
export class RenderTargetManager {
|
|
17
|
-
|
|
18
|
-
constructor( width, height, renderer ) {
|
|
19
|
-
|
|
20
|
-
this.width = width;
|
|
21
|
-
this.height = height;
|
|
22
|
-
this.renderer = renderer;
|
|
23
|
-
|
|
24
|
-
// Initialize targets to null first
|
|
25
|
-
this.currentTarget = null;
|
|
26
|
-
this.previousTarget = null;
|
|
27
|
-
|
|
28
|
-
// Copy material for efficient final output
|
|
29
|
-
this.copyMaterial = null;
|
|
30
|
-
this.copyQuad = null;
|
|
31
|
-
|
|
32
|
-
// Performance cache for MRT textures
|
|
33
|
-
this.mrtTexturesCache = { color: null, normalDepth: null, albedo: null };
|
|
34
|
-
|
|
35
|
-
// Create ping-pong MRT targets immediately
|
|
36
|
-
this.createTargets( width, height );
|
|
37
|
-
|
|
38
|
-
// Verify targets were created successfully
|
|
39
|
-
if ( ! this.currentTarget || ! this.previousTarget ) {
|
|
40
|
-
|
|
41
|
-
console.error( 'RenderTargetManager: Failed to create render targets!' );
|
|
42
|
-
// Create simple fallback targets without MRT
|
|
43
|
-
this.createFallbackTargets( width, height );
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Create unified render targets with MRT support
|
|
51
|
-
* @param {number} width - Target width
|
|
52
|
-
* @param {number} height - Target height
|
|
53
|
-
*/
|
|
54
|
-
createTargets( width, height ) {
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
|
|
58
|
-
const targetOptions = {
|
|
59
|
-
minFilter: NearestFilter,
|
|
60
|
-
magFilter: NearestFilter,
|
|
61
|
-
type: FloatType,
|
|
62
|
-
colorSpace: LinearSRGBColorSpace,
|
|
63
|
-
depthBuffer: false,
|
|
64
|
-
count: 3, // MRT: Color + NormalDepth + Albedo (for OIDN)
|
|
65
|
-
samples: 0 // IMPORTANT: No multisampling to avoid blitFramebuffer issues
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// Dispose existing targets if they exist
|
|
69
|
-
if ( this.currentTarget ) {
|
|
70
|
-
|
|
71
|
-
this.currentTarget.dispose();
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if ( this.previousTarget ) {
|
|
76
|
-
|
|
77
|
-
this.previousTarget.dispose();
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Create new targets
|
|
82
|
-
this.currentTarget = new WebGLRenderTarget( width, height, targetOptions );
|
|
83
|
-
this.previousTarget = new WebGLRenderTarget( width, height, targetOptions );
|
|
84
|
-
|
|
85
|
-
// Verify targets have textures
|
|
86
|
-
if ( ! this.currentTarget.textures || this.currentTarget.textures.length !== 3 ) {
|
|
87
|
-
|
|
88
|
-
throw new Error( 'Current target missing MRT textures' );
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if ( ! this.previousTarget.textures || this.previousTarget.textures.length !== 3 ) {
|
|
93
|
-
|
|
94
|
-
throw new Error( 'Previous target missing MRT textures' );
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Set texture names for debugging
|
|
99
|
-
this.currentTarget.textures[ 0 ].name = 'CurrentColor';
|
|
100
|
-
this.currentTarget.textures[ 1 ].name = 'CurrentNormalDepth';
|
|
101
|
-
this.currentTarget.textures[ 2 ].name = 'CurrentAlbedo';
|
|
102
|
-
this.previousTarget.textures[ 0 ].name = 'PreviousColor';
|
|
103
|
-
this.previousTarget.textures[ 1 ].name = 'PreviousNormalDepth';
|
|
104
|
-
this.previousTarget.textures[ 2 ].name = 'PreviousAlbedo';
|
|
105
|
-
|
|
106
|
-
} catch ( error ) {
|
|
107
|
-
|
|
108
|
-
console.error( 'RenderTargetManager: Error creating MRT targets:', error );
|
|
109
|
-
this.currentTarget = null;
|
|
110
|
-
this.previousTarget = null;
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Create fallback render targets without MRT if main creation fails
|
|
118
|
-
* @param {number} width - Target width
|
|
119
|
-
* @param {number} height - Target height
|
|
120
|
-
*/
|
|
121
|
-
createFallbackTargets( width, height ) {
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
|
|
125
|
-
console.warn( 'RenderTargetManager: Creating fallback targets without MRT' );
|
|
126
|
-
|
|
127
|
-
const fallbackOptions = {
|
|
128
|
-
minFilter: NearestFilter,
|
|
129
|
-
magFilter: NearestFilter,
|
|
130
|
-
type: FloatType,
|
|
131
|
-
colorSpace: LinearSRGBColorSpace,
|
|
132
|
-
depthBuffer: false,
|
|
133
|
-
samples: 0
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
this.currentTarget = new WebGLRenderTarget( width, height, fallbackOptions );
|
|
137
|
-
this.previousTarget = new WebGLRenderTarget( width, height, fallbackOptions );
|
|
138
|
-
|
|
139
|
-
// Create dummy textures for MRT compatibility
|
|
140
|
-
this.currentTarget.textures = [ this.currentTarget.texture, this.currentTarget.texture, this.currentTarget.texture ];
|
|
141
|
-
this.previousTarget.textures = [ this.previousTarget.texture, this.previousTarget.texture, this.previousTarget.texture ];
|
|
142
|
-
|
|
143
|
-
this.currentTarget.textures[ 0 ].name = 'CurrentColor';
|
|
144
|
-
this.currentTarget.textures[ 1 ].name = 'CurrentNormalDepth';
|
|
145
|
-
this.currentTarget.textures[ 2 ].name = 'CurrentAlbedo';
|
|
146
|
-
this.previousTarget.textures[ 0 ].name = 'PreviousColor';
|
|
147
|
-
this.previousTarget.textures[ 1 ].name = 'PreviousNormalDepth';
|
|
148
|
-
this.previousTarget.textures[ 2 ].name = 'PreviousAlbedo';
|
|
149
|
-
|
|
150
|
-
console.log( 'RenderTargetManager: Fallback targets created successfully' );
|
|
151
|
-
|
|
152
|
-
} catch ( error ) {
|
|
153
|
-
|
|
154
|
-
console.error( 'RenderTargetManager: Failed to create even fallback targets:', error );
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Get current accumulated render target
|
|
162
|
-
* @returns {WebGLRenderTarget} - Current accumulation target
|
|
163
|
-
*/
|
|
164
|
-
getCurrentAccumulation() {
|
|
165
|
-
|
|
166
|
-
this.ensureTargetsReady();
|
|
167
|
-
return this.currentTarget;
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Get current raw sample render target
|
|
173
|
-
* @returns {WebGLRenderTarget} - Current raw sample target
|
|
174
|
-
*/
|
|
175
|
-
getCurrentRawSample() {
|
|
176
|
-
|
|
177
|
-
this.ensureTargetsReady();
|
|
178
|
-
return this.currentTarget;
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Get MRT textures (color, normal/depth, and albedo)
|
|
184
|
-
* @returns {Object} - Object containing color, normalDepth, and albedo textures
|
|
185
|
-
*/
|
|
186
|
-
getMRTTextures() {
|
|
187
|
-
|
|
188
|
-
if ( ! this.ensureTargetsReady() ) {
|
|
189
|
-
|
|
190
|
-
return {
|
|
191
|
-
color: null,
|
|
192
|
-
normalDepth: null,
|
|
193
|
-
albedo: null
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Reuse cached object to avoid allocation
|
|
199
|
-
this.mrtTexturesCache.color = this.currentTarget.textures[ 0 ];
|
|
200
|
-
this.mrtTexturesCache.normalDepth = this.currentTarget.textures[ 1 ];
|
|
201
|
-
this.mrtTexturesCache.albedo = this.currentTarget.textures[ 2 ];
|
|
202
|
-
return this.mrtTexturesCache;
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Get previous frame textures
|
|
208
|
-
* @returns {Object} - Object containing previous frame textures
|
|
209
|
-
*/
|
|
210
|
-
getPreviousTextures() {
|
|
211
|
-
|
|
212
|
-
if ( ! this.ensureTargetsReady() ) {
|
|
213
|
-
|
|
214
|
-
return {
|
|
215
|
-
color: null,
|
|
216
|
-
normalDepth: null,
|
|
217
|
-
albedo: null
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return {
|
|
223
|
-
color: this.previousTarget.textures[ 0 ],
|
|
224
|
-
normalDepth: this.previousTarget.textures[ 1 ],
|
|
225
|
-
albedo: this.previousTarget.textures[ 2 ]
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Swap current and previous targets
|
|
232
|
-
*/
|
|
233
|
-
swapTargets() {
|
|
234
|
-
|
|
235
|
-
if ( ! this.ensureTargetsReady() ) {
|
|
236
|
-
|
|
237
|
-
return;
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
[ this.currentTarget, this.previousTarget ] = [ this.previousTarget, this.currentTarget ];
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Clear both render targets
|
|
247
|
-
*/
|
|
248
|
-
clearTargets() {
|
|
249
|
-
|
|
250
|
-
if ( ! this.ensureTargetsReady() ) {
|
|
251
|
-
|
|
252
|
-
return;
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const currentRenderTarget = this.renderer.getRenderTarget();
|
|
257
|
-
|
|
258
|
-
this.renderer.setRenderTarget( this.currentTarget );
|
|
259
|
-
this.renderer.clear();
|
|
260
|
-
this.renderer.setRenderTarget( this.previousTarget );
|
|
261
|
-
this.renderer.clear();
|
|
262
|
-
|
|
263
|
-
this.renderer.setRenderTarget( currentRenderTarget );
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Efficiently copy color output to destination target
|
|
269
|
-
* @param {WebGLRenderer} renderer - Three.js renderer
|
|
270
|
-
* @param {WebGLRenderTarget|null} writeBuffer - Destination target (null for screen)
|
|
271
|
-
* @param {boolean} renderToScreen - Whether to render to screen
|
|
272
|
-
*/
|
|
273
|
-
efficientCopyColorOutput( renderer, writeBuffer, renderToScreen = false ) {
|
|
274
|
-
|
|
275
|
-
if ( ! this.ensureTargetsReady() ) {
|
|
276
|
-
|
|
277
|
-
return;
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Lazy create copy material and quad
|
|
282
|
-
if ( ! this.copyMaterial ) {
|
|
283
|
-
|
|
284
|
-
this.copyMaterial = new ShaderMaterial( {
|
|
285
|
-
glslVersion: GLSL3,
|
|
286
|
-
uniforms: {
|
|
287
|
-
tDiffuse: { value: null }
|
|
288
|
-
},
|
|
289
|
-
|
|
290
|
-
vertexShader: `
|
|
291
|
-
// in vec3 position;
|
|
292
|
-
// in vec2 uv;
|
|
293
|
-
out vec2 vUv;
|
|
294
|
-
// uniform mat4 modelViewMatrix;
|
|
295
|
-
// uniform mat4 projectionMatrix;
|
|
296
|
-
|
|
297
|
-
void main() {
|
|
298
|
-
vUv = uv;
|
|
299
|
-
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
|
|
300
|
-
}
|
|
301
|
-
`,
|
|
302
|
-
|
|
303
|
-
fragmentShader: `
|
|
304
|
-
precision highp float;
|
|
305
|
-
uniform sampler2D tDiffuse;
|
|
306
|
-
in vec2 vUv;
|
|
307
|
-
out vec4 pc_fragColor;
|
|
308
|
-
|
|
309
|
-
void main() {
|
|
310
|
-
pc_fragColor = textureLod( tDiffuse, vUv, 0.0 );
|
|
311
|
-
}
|
|
312
|
-
`,
|
|
313
|
-
|
|
314
|
-
depthTest: false,
|
|
315
|
-
depthWrite: false,
|
|
316
|
-
transparent: false,
|
|
317
|
-
} );
|
|
318
|
-
|
|
319
|
-
this.copyQuad = new FullScreenQuad( this.copyMaterial );
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Set source texture (color output from our MRT)
|
|
324
|
-
this.copyMaterial.uniforms.tDiffuse.value = this.currentTarget.textures[ 0 ];
|
|
325
|
-
|
|
326
|
-
// Render to destination
|
|
327
|
-
renderer.setRenderTarget( renderToScreen ? null : writeBuffer );
|
|
328
|
-
this.copyQuad.render( renderer );
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Copy specific MRT texture to destination
|
|
334
|
-
* @param {WebGLRenderer} renderer - Three.js renderer
|
|
335
|
-
* @param {number} textureIndex - Index of texture to copy (0 = color, 1 = normal/depth)
|
|
336
|
-
* @param {WebGLRenderTarget|null} writeBuffer - Destination target
|
|
337
|
-
*/
|
|
338
|
-
copyMRTTexture( renderer, textureIndex, writeBuffer ) {
|
|
339
|
-
|
|
340
|
-
if ( ! this.ensureTargetsReady() ) {
|
|
341
|
-
|
|
342
|
-
return;
|
|
343
|
-
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
if ( ! this.copyMaterial ) {
|
|
347
|
-
|
|
348
|
-
this.efficientCopyColorOutput( renderer, null, false ); // Initialize copy material
|
|
349
|
-
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
this.copyMaterial.uniforms.tDiffuse.value = this.currentTarget.textures[ textureIndex ];
|
|
353
|
-
renderer.setRenderTarget( writeBuffer );
|
|
354
|
-
this.copyQuad.render( renderer );
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Resize render targets
|
|
360
|
-
* @param {number} width - New width
|
|
361
|
-
* @param {number} height - New height
|
|
362
|
-
*/
|
|
363
|
-
setSize( width, height ) {
|
|
364
|
-
|
|
365
|
-
this.width = width;
|
|
366
|
-
this.height = height;
|
|
367
|
-
|
|
368
|
-
// Recreate targets with new size
|
|
369
|
-
this.createTargets( width, height );
|
|
370
|
-
|
|
371
|
-
// Verify creation was successful
|
|
372
|
-
if ( ! this.isValid() ) {
|
|
373
|
-
|
|
374
|
-
console.warn( 'RenderTargetManager: Failed to resize targets, creating fallback...' );
|
|
375
|
-
this.createFallbackTargets( width, height );
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Get target dimensions
|
|
383
|
-
* @returns {Object} - Object containing width and height
|
|
384
|
-
*/
|
|
385
|
-
getSize() {
|
|
386
|
-
|
|
387
|
-
return {
|
|
388
|
-
width: this.width,
|
|
389
|
-
height: this.height
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Check if targets are valid and properly sized
|
|
396
|
-
* @returns {boolean} - True if targets are valid
|
|
397
|
-
*/
|
|
398
|
-
isValid() {
|
|
399
|
-
|
|
400
|
-
return this.currentTarget &&
|
|
401
|
-
this.previousTarget &&
|
|
402
|
-
this.currentTarget.textures &&
|
|
403
|
-
this.previousTarget.textures &&
|
|
404
|
-
this.currentTarget.width === this.width &&
|
|
405
|
-
this.currentTarget.height === this.height &&
|
|
406
|
-
this.previousTarget.width === this.width &&
|
|
407
|
-
this.previousTarget.height === this.height;
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Ensure targets are ready before operations
|
|
413
|
-
* @returns {boolean} - True if targets are ready
|
|
414
|
-
*/
|
|
415
|
-
ensureTargetsReady() {
|
|
416
|
-
|
|
417
|
-
if ( ! this.isValid() ) {
|
|
418
|
-
|
|
419
|
-
console.warn( 'RenderTargetManager: Targets not ready, attempting to recreate...' );
|
|
420
|
-
this.createTargets( this.width, this.height );
|
|
421
|
-
|
|
422
|
-
if ( ! this.isValid() ) {
|
|
423
|
-
|
|
424
|
-
this.createFallbackTargets( this.width, this.height );
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
return this.isValid();
|
|
431
|
-
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
/**
|
|
435
|
-
* Get memory usage information
|
|
436
|
-
* @returns {Object} - Memory usage statistics
|
|
437
|
-
*/
|
|
438
|
-
getMemoryUsage() {
|
|
439
|
-
|
|
440
|
-
const bytesPerPixel = 16; // 4 channels * 4 bytes (Float32) per channel
|
|
441
|
-
const pixelsPerTarget = this.width * this.height;
|
|
442
|
-
const texturesPerTarget = 3; // MRT: color + normal/depth + albedo
|
|
443
|
-
const targetCount = 2; // current + previous
|
|
444
|
-
|
|
445
|
-
const totalBytes = pixelsPerTarget * bytesPerPixel * texturesPerTarget * targetCount;
|
|
446
|
-
|
|
447
|
-
return {
|
|
448
|
-
totalBytes,
|
|
449
|
-
totalMB: totalBytes / ( 1024 * 1024 ),
|
|
450
|
-
perTargetMB: ( totalBytes / targetCount ) / ( 1024 * 1024 ),
|
|
451
|
-
width: this.width,
|
|
452
|
-
height: this.height,
|
|
453
|
-
textureCount: texturesPerTarget * targetCount
|
|
454
|
-
};
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* Create debug info for render targets
|
|
460
|
-
* @returns {Object} - Debug information
|
|
461
|
-
*/
|
|
462
|
-
getDebugInfo() {
|
|
463
|
-
|
|
464
|
-
return {
|
|
465
|
-
currentTarget: {
|
|
466
|
-
width: this.currentTarget.width,
|
|
467
|
-
height: this.currentTarget.height,
|
|
468
|
-
textureCount: this.currentTarget.textures.length,
|
|
469
|
-
textureNames: this.currentTarget.textures.map( tex => tex.name )
|
|
470
|
-
},
|
|
471
|
-
previousTarget: {
|
|
472
|
-
width: this.previousTarget.width,
|
|
473
|
-
height: this.previousTarget.height,
|
|
474
|
-
textureCount: this.previousTarget.textures.length,
|
|
475
|
-
textureNames: this.previousTarget.textures.map( tex => tex.name )
|
|
476
|
-
},
|
|
477
|
-
memoryUsage: this.getMemoryUsage()
|
|
478
|
-
};
|
|
479
|
-
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
/**
|
|
483
|
-
* Dispose of all resources
|
|
484
|
-
*/
|
|
485
|
-
dispose() {
|
|
486
|
-
|
|
487
|
-
// Dispose render targets
|
|
488
|
-
if ( this.currentTarget ) {
|
|
489
|
-
|
|
490
|
-
this.currentTarget.dispose();
|
|
491
|
-
this.currentTarget = null;
|
|
492
|
-
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
if ( this.previousTarget ) {
|
|
496
|
-
|
|
497
|
-
this.previousTarget.dispose();
|
|
498
|
-
this.previousTarget = null;
|
|
499
|
-
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// Dispose copy materials
|
|
503
|
-
if ( this.copyMaterial ) {
|
|
504
|
-
|
|
505
|
-
this.copyMaterial.dispose();
|
|
506
|
-
this.copyMaterial = null;
|
|
507
|
-
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
if ( this.copyQuad ) {
|
|
511
|
-
|
|
512
|
-
this.copyQuad.dispose();
|
|
513
|
-
this.copyQuad = null;
|
|
514
|
-
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
// Clear caches
|
|
518
|
-
this.mrtTexturesCache = { color: null, normalDepth: null, albedo: null };
|
|
519
|
-
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
}
|