rayzee 6.2.0 → 6.3.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 +1055 -1037
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +126 -78
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineDefaults.js +27 -16
- package/src/Stages/ASVGF.js +304 -185
- package/src/Stages/AdaptiveSampling.js +6 -6
- package/src/Stages/BilateralFilter.js +85 -69
- package/src/Stages/Compositor.js +1 -0
- package/src/Stages/NormalDepth.js +56 -118
- package/src/TSL/BVHTraversal.js +114 -49
- package/src/TSL/Common.js +3 -4
- package/src/TSL/Debugger.js +6 -6
- package/src/TSL/Displacement.js +13 -13
- package/src/TSL/Environment.js +12 -12
- package/src/TSL/LightsCore.js +14 -15
- package/src/TSL/LightsDirect.js +2 -5
- package/src/TSL/LightsIndirect.js +20 -21
- package/src/TSL/LightsSampling.js +19 -20
- package/src/TSL/MaterialEvaluation.js +2 -2
- package/src/TSL/MaterialProperties.js +5 -5
- package/src/TSL/MaterialTransmission.js +4 -6
- package/src/TSL/PathTracer.js +4 -6
- package/src/TSL/PathTracerCore.js +13 -29
- package/src/TSL/TextureSampling.js +3 -3
- package/src/managers/DenoisingManager.js +12 -2
|
@@ -201,8 +201,8 @@ export class AdaptiveSampling extends RenderStage {
|
|
|
201
201
|
} );
|
|
202
202
|
|
|
203
203
|
// Dispatch dimensions
|
|
204
|
-
this._dispatchX = Math.ceil( w /
|
|
205
|
-
this._dispatchY = Math.ceil( h /
|
|
204
|
+
this._dispatchX = Math.ceil( w / 8 );
|
|
205
|
+
this._dispatchY = Math.ceil( h / 8 );
|
|
206
206
|
|
|
207
207
|
// Input: variance texture from Variance
|
|
208
208
|
// Use regular TextureNode (not StorageTexture) as compile-time placeholder so
|
|
@@ -243,7 +243,7 @@ export class AdaptiveSampling extends RenderStage {
|
|
|
243
243
|
const resH = this.resolutionHeight;
|
|
244
244
|
const outputTex = this._outputStorageTex;
|
|
245
245
|
|
|
246
|
-
const WG_SIZE =
|
|
246
|
+
const WG_SIZE = 8;
|
|
247
247
|
|
|
248
248
|
const computeFn = Fn( () => {
|
|
249
249
|
|
|
@@ -300,7 +300,7 @@ export class AdaptiveSampling extends RenderStage {
|
|
|
300
300
|
const resW = this.resolutionWidth;
|
|
301
301
|
const resH = this.resolutionHeight;
|
|
302
302
|
|
|
303
|
-
const WG_SIZE =
|
|
303
|
+
const WG_SIZE = 8;
|
|
304
304
|
|
|
305
305
|
const computeFn = Fn( () => {
|
|
306
306
|
|
|
@@ -406,8 +406,8 @@ export class AdaptiveSampling extends RenderStage {
|
|
|
406
406
|
this.resolutionHeight.value = height;
|
|
407
407
|
|
|
408
408
|
// Update dispatch dimensions
|
|
409
|
-
this._dispatchX = Math.ceil( width /
|
|
410
|
-
this._dispatchY = Math.ceil( height /
|
|
409
|
+
this._dispatchX = Math.ceil( width / 8 );
|
|
410
|
+
this._dispatchY = Math.ceil( height / 8 );
|
|
411
411
|
this._computeNode.dispatchSize = [ this._dispatchX, this._dispatchY, 1 ];
|
|
412
412
|
this._heatmapComputeNode.dispatchSize = [ this._dispatchX, this._dispatchY, 1 ];
|
|
413
413
|
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
import { Fn, wgslFn, vec3, vec4, float, int, uint, ivec2, uvec2, uniform, If, max,
|
|
1
|
+
import { Fn, wgslFn, vec3, vec4, float, int, uint, ivec2, uvec2, uniform, If, max, sqrt,
|
|
2
2
|
textureLoad, textureStore, localId, workgroupId } from 'three/tsl';
|
|
3
3
|
import { TextureNode, StorageTexture } from 'three/webgpu';
|
|
4
4
|
import { HalfFloatType, RGBAFormat, LinearFilter } from 'three';
|
|
5
5
|
import { RenderStage, StageExecutionMode } from '../Pipeline/RenderStage.js';
|
|
6
6
|
import { luminance } from '../TSL/Common.js';
|
|
7
|
+
import { ALBEDO_EPS } from '../EngineDefaults.js';
|
|
7
8
|
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
* Combines luminance, normal, depth, and color similarity into
|
|
14
|
-
* a single weight multiplied by the kernel weight.
|
|
15
|
-
*/
|
|
9
|
+
// SVGF bilateral edge-stopping weight. All three φ params are relative
|
|
10
|
+
// tolerances (unitless fractions) so the filter is scale-invariant across
|
|
11
|
+
// scenes, HDR ranges, and camera distances. sigmaL is precomputed by the
|
|
12
|
+
// caller as phiLum * √variance / albedoLum + ε, compensating for the
|
|
13
|
+
// 1/albedo noise amplification introduced by demodulation.
|
|
16
14
|
const bilateralWeight = /*@__PURE__*/ wgslFn( `
|
|
17
15
|
fn bilateralWeight(
|
|
18
16
|
centerLum: f32, sLum: f32,
|
|
@@ -20,48 +18,34 @@ const bilateralWeight = /*@__PURE__*/ wgslFn( `
|
|
|
20
18
|
centerDepth: f32, sDepth: f32,
|
|
21
19
|
centerColor: vec3f, sColor: vec3f,
|
|
22
20
|
kernelW: f32,
|
|
23
|
-
|
|
21
|
+
sigmaL: f32, phiNorm: f32, phiDep: f32, phiCol: f32
|
|
24
22
|
) -> f32 {
|
|
25
23
|
|
|
26
|
-
let lumW = exp( -abs( centerLum - sLum )
|
|
27
|
-
// clamp dot to [0,1]
|
|
28
|
-
//
|
|
29
|
-
// and poison output via inf*0 = NaN. See project_tsl_pitfalls memory.
|
|
24
|
+
let lumW = exp( -abs( centerLum - sLum ) / sigmaL );
|
|
25
|
+
// clamp dot to [0,1]: miss-ray normals decode to (-1,-1,-1) with
|
|
26
|
+
// dot=3 → pow saturates to +inf → inf*0 = NaN. See project_tsl_pitfalls.
|
|
30
27
|
let normW = pow( clamp( dot( centerNormal, sNormal ), 0.0, 1.0 ), phiNorm );
|
|
31
|
-
let depW = exp( -abs( centerDepth - sDepth ) / max( phiDep, 0.001 ) );
|
|
28
|
+
let depW = exp( -abs( centerDepth - sDepth ) / max( centerDepth * phiDep, 0.001 ) );
|
|
32
29
|
let maxDiff = max( max( abs( centerColor.x - sColor.x ),
|
|
33
30
|
abs( centerColor.y - sColor.y ) ),
|
|
34
31
|
abs( centerColor.z - sColor.z ) );
|
|
35
|
-
let
|
|
32
|
+
let avgLum = max( ( centerLum + sLum ) * 0.5, 0.0001 );
|
|
33
|
+
let colW = exp( -( maxDiff / avgLum ) / max( phiCol, 0.0001 ) );
|
|
36
34
|
return kernelW * lumW * normW * depW * colW;
|
|
37
35
|
|
|
38
36
|
}
|
|
39
37
|
` );
|
|
40
38
|
|
|
41
39
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* Edge-aware A-trous wavelet filter for spatial denoising.
|
|
45
|
-
* Runs multiple iterations with increasing step size (2^i),
|
|
46
|
-
* ping-ponging between two StorageTextures.
|
|
47
|
-
*
|
|
48
|
-
* Algorithm:
|
|
49
|
-
* 1. textureLoad center pixel (color + normalDepth)
|
|
50
|
-
* 2. Unrolled 5×5 a-trous kernel with edge-stopping weights
|
|
51
|
-
* 3. Normalize accumulated color
|
|
52
|
-
* 4. textureStore filtered result
|
|
53
|
-
* 5. Repeat for 4 iterations (step sizes 1, 2, 4, 8)
|
|
54
|
-
*
|
|
55
|
-
* Edge-stopping functions:
|
|
56
|
-
* - Luminance: exp(-|ΔL| * σ_l)
|
|
57
|
-
* - Normal: dot(n1,n2)^σ_n
|
|
58
|
-
* - Depth: exp(-|Δz| / σ_z)
|
|
59
|
-
* - Color: exp(-maxDiff * σ_c)
|
|
40
|
+
* BilateralFilter — 5×5 à-trous wavelet, edge-preserving, multi-iteration.
|
|
60
41
|
*
|
|
61
|
-
*
|
|
42
|
+
* Reads asvgf:demodulated (lighting), filters in demodulated space across
|
|
43
|
+
* `iterations` ping-pong passes with step size 2^i, multiplies by albedo on
|
|
44
|
+
* the final pass to remodulate. φ params are relative tolerances.
|
|
62
45
|
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
46
|
+
* Publishes: bilateralFiltering:output (modulated)
|
|
47
|
+
* Reads: asvgf:demodulated (or fallback), pathtracer:normalDepth,
|
|
48
|
+
* pathtracer:albedo, variance:output
|
|
65
49
|
*/
|
|
66
50
|
export class BilateralFilter extends RenderStage {
|
|
67
51
|
|
|
@@ -73,29 +57,33 @@ export class BilateralFilter extends RenderStage {
|
|
|
73
57
|
} );
|
|
74
58
|
|
|
75
59
|
this.renderer = renderer;
|
|
76
|
-
this.inputTextureName = options.inputTextureName || 'asvgf:
|
|
60
|
+
this.inputTextureName = options.inputTextureName || 'asvgf:demodulated';
|
|
77
61
|
this.normalDepthTextureName = options.normalDepthTextureName || 'pathtracer:normalDepth';
|
|
62
|
+
this.albedoTextureName = options.albedoTextureName || 'pathtracer:albedo';
|
|
63
|
+
this.varianceTextureName = options.varianceTextureName || 'variance:output';
|
|
78
64
|
this.iterations = options.iterations ?? 4;
|
|
79
65
|
|
|
80
|
-
//
|
|
81
|
-
|
|
66
|
+
// All φ are relative tolerances (fractions of mean/depth). Bigger =
|
|
67
|
+
// more permissive blending across edges.
|
|
68
|
+
this.phiColor = uniform( options.phiColor ?? 0.5 );
|
|
82
69
|
this.phiNormal = uniform( options.phiNormal ?? 128.0 );
|
|
83
|
-
this.phiDepth = uniform( options.phiDepth ??
|
|
70
|
+
this.phiDepth = uniform( options.phiDepth ?? 0.05 );
|
|
84
71
|
this.phiLuminance = uniform( options.phiLuminance ?? 4.0 );
|
|
85
72
|
this.stepSizeU = uniform( 1, 'int' );
|
|
73
|
+
// 1 on the final iteration → multiply by albedo to remodulate.
|
|
74
|
+
this.isLastIterationU = uniform( 0, 'int' );
|
|
86
75
|
this.resW = uniform( options.width || 1 );
|
|
87
76
|
this.resH = uniform( options.height || 1 );
|
|
88
77
|
|
|
89
|
-
// Input texture nodes
|
|
90
78
|
this._readTexNode = new TextureNode();
|
|
91
79
|
this._normalDepthTexNode = new TextureNode();
|
|
80
|
+
this._albedoTexNode = new TextureNode();
|
|
81
|
+
this._varianceTexNode = new TextureNode();
|
|
92
82
|
|
|
93
|
-
// Ping-pong StorageTextures
|
|
94
83
|
const w = options.width || 1;
|
|
95
84
|
const h = options.height || 1;
|
|
96
85
|
|
|
97
|
-
// LinearFilter
|
|
98
|
-
// when _readTexNode.value is later set to a StorageTexture
|
|
86
|
+
// LinearFilter required for textureLoad codegen on StorageTextures.
|
|
99
87
|
this._storageTexA = new StorageTexture( w, h );
|
|
100
88
|
this._storageTexA.type = HalfFloatType;
|
|
101
89
|
this._storageTexA.format = RGBAFormat;
|
|
@@ -110,7 +98,6 @@ export class BilateralFilter extends RenderStage {
|
|
|
110
98
|
|
|
111
99
|
this._compiled = false;
|
|
112
100
|
|
|
113
|
-
// Dispatch dimensions
|
|
114
101
|
this._dispatchX = Math.ceil( w / 8 );
|
|
115
102
|
this._dispatchY = Math.ceil( h / 8 );
|
|
116
103
|
|
|
@@ -118,15 +105,7 @@ export class BilateralFilter extends RenderStage {
|
|
|
118
105
|
|
|
119
106
|
}
|
|
120
107
|
|
|
121
|
-
|
|
122
|
-
* Build two compute nodes — one for each ping-pong write direction.
|
|
123
|
-
*
|
|
124
|
-
* _computeNodeA: writes to StorageTexA, reads from _readTexNode
|
|
125
|
-
* _computeNodeB: writes to StorageTexB, reads from _readTexNode
|
|
126
|
-
*
|
|
127
|
-
* Read-side texture wrapped in TextureNode so compile-time type is
|
|
128
|
-
* regular Texture (avoids Three.js WGSL textureLoad codegen bug).
|
|
129
|
-
*/
|
|
108
|
+
// One compute node per ping-pong write direction.
|
|
130
109
|
_buildCompute() {
|
|
131
110
|
|
|
132
111
|
this._computeNodeA = this._buildComputeForDirection( this._storageTexA );
|
|
@@ -138,11 +117,14 @@ export class BilateralFilter extends RenderStage {
|
|
|
138
117
|
|
|
139
118
|
const readTexNode = this._readTexNode;
|
|
140
119
|
const ndTexNode = this._normalDepthTexNode;
|
|
120
|
+
const albedoTexNode = this._albedoTexNode;
|
|
121
|
+
const varTexNode = this._varianceTexNode;
|
|
141
122
|
const phiColor = this.phiColor;
|
|
142
123
|
const phiNormal = this.phiNormal;
|
|
143
124
|
const phiDepth = this.phiDepth;
|
|
144
125
|
const phiLuminance = this.phiLuminance;
|
|
145
126
|
const stepSize = this.stepSizeU;
|
|
127
|
+
const isLastIterationU = this.isLastIterationU;
|
|
146
128
|
const resW = this.resW;
|
|
147
129
|
const resH = this.resH;
|
|
148
130
|
|
|
@@ -165,18 +147,29 @@ export class BilateralFilter extends RenderStage {
|
|
|
165
147
|
If( gx.lessThan( int( resW ) ).and( gy.lessThan( int( resH ) ) ), () => {
|
|
166
148
|
|
|
167
149
|
const coord = ivec2( gx, gy );
|
|
168
|
-
|
|
169
|
-
// Centre sample
|
|
170
150
|
const centerColor = textureLoad( readTexNode, coord ).xyz;
|
|
171
151
|
const centerND = textureLoad( ndTexNode, coord );
|
|
172
152
|
const centerNormal = centerND.xyz.mul( 2.0 ).sub( 1.0 );
|
|
173
153
|
const centerDepth = centerND.w;
|
|
174
154
|
const centerLum = luminance( centerColor );
|
|
155
|
+
const centerSafeAlbedo = max( textureLoad( albedoTexNode, coord ).xyz, vec3( ALBEDO_EPS ) );
|
|
156
|
+
const centerAlbedoLum = max( luminance( centerSafeAlbedo ), float( ALBEDO_EPS ) ).toVar();
|
|
157
|
+
|
|
158
|
+
// sigma_l = phiLum * √variance / albedoLum + ε. Dividing by
|
|
159
|
+
// albedoLum compensates for the 1/albedo noise amplification
|
|
160
|
+
// from demodulation — otherwise dark materials get an
|
|
161
|
+
// under-estimated sigma → over-strict luminance gate → no
|
|
162
|
+
// blending → silhouette dark-outline artifact.
|
|
163
|
+
const variance = textureLoad( varTexNode, coord ).z;
|
|
164
|
+
const sigmaL = phiLuminance
|
|
165
|
+
.mul( sqrt( max( variance, float( 0.0 ) ) ) )
|
|
166
|
+
.div( centerAlbedoLum )
|
|
167
|
+
.add( float( 0.0001 ) );
|
|
175
168
|
|
|
176
169
|
const colorSum = vec3( 0.0 ).toVar();
|
|
177
170
|
const weightSum = float( 0.0 ).toVar();
|
|
178
171
|
|
|
179
|
-
//
|
|
172
|
+
// 5×5 à-trous kernel (Gaussian-approx, Σ=1)
|
|
180
173
|
for ( let iy = 0; iy < 5; iy ++ ) {
|
|
181
174
|
|
|
182
175
|
for ( let ix = 0; ix < 5; ix ++ ) {
|
|
@@ -202,7 +195,7 @@ export class BilateralFilter extends RenderStage {
|
|
|
202
195
|
centerDepth, sDepth,
|
|
203
196
|
centerColor, sColor,
|
|
204
197
|
float( kw ),
|
|
205
|
-
|
|
198
|
+
sigmaL, phiNormal, phiDepth, phiColor
|
|
206
199
|
);
|
|
207
200
|
|
|
208
201
|
colorSum.addAssign( sColor.mul( w ) );
|
|
@@ -214,10 +207,15 @@ export class BilateralFilter extends RenderStage {
|
|
|
214
207
|
|
|
215
208
|
const filtered = colorSum.div( max( weightSum, float( 0.0001 ) ) );
|
|
216
209
|
|
|
210
|
+
// Remodulate by albedo only on the final iteration so the
|
|
211
|
+
// inner ping-pong stays in demodulated space.
|
|
212
|
+
const isLast = isLastIterationU.equal( int( 1 ) );
|
|
213
|
+
const output = isLast.select( filtered.mul( centerSafeAlbedo ), filtered );
|
|
214
|
+
|
|
217
215
|
textureStore(
|
|
218
216
|
writeStorageTex,
|
|
219
217
|
uvec2( uint( gx ), uint( gy ) ),
|
|
220
|
-
vec4(
|
|
218
|
+
vec4( output, 1.0 )
|
|
221
219
|
).toWriteOnly();
|
|
222
220
|
|
|
223
221
|
} );
|
|
@@ -236,12 +234,14 @@ export class BilateralFilter extends RenderStage {
|
|
|
236
234
|
if ( ! this.enabled ) return;
|
|
237
235
|
|
|
238
236
|
const inputTex = context.getTexture( this.inputTextureName )
|
|
237
|
+
|| context.getTexture( 'asvgf:output' )
|
|
239
238
|
|| context.getTexture( 'pathtracer:color' );
|
|
240
239
|
const ndTex = context.getTexture( this.normalDepthTextureName );
|
|
240
|
+
const albedoTex = context.getTexture( this.albedoTextureName );
|
|
241
|
+
const varTex = context.getTexture( this.varianceTextureName );
|
|
241
242
|
|
|
242
243
|
if ( ! inputTex ) return;
|
|
243
244
|
|
|
244
|
-
// Auto-size
|
|
245
245
|
const img = inputTex.image;
|
|
246
246
|
if ( img && img.width > 0 && img.height > 0 ) {
|
|
247
247
|
|
|
@@ -254,12 +254,13 @@ export class BilateralFilter extends RenderStage {
|
|
|
254
254
|
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
//
|
|
257
|
+
// RenderTarget textures — safe to bind before first-compile.
|
|
258
258
|
if ( ndTex ) this._normalDepthTexNode.value = ndTex;
|
|
259
|
+
if ( albedoTex ) this._albedoTexNode.value = albedoTex;
|
|
259
260
|
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
//
|
|
261
|
+
// First-frame compile while StorageTexture-typed nodes still hold
|
|
262
|
+
// EmptyTexture — codegen then emits textureLoad with the level
|
|
263
|
+
// parameter, which the runtime requires for non-zero reads.
|
|
263
264
|
if ( ! this._compiled ) {
|
|
264
265
|
|
|
265
266
|
this.renderer.compute( this._computeNodeA );
|
|
@@ -268,7 +269,10 @@ export class BilateralFilter extends RenderStage {
|
|
|
268
269
|
|
|
269
270
|
}
|
|
270
271
|
|
|
271
|
-
|
|
272
|
+
if ( varTex ) this._varianceTexNode.value = varTex;
|
|
273
|
+
|
|
274
|
+
// À-trous iterations: step size 2^i, ping-pong write direction.
|
|
275
|
+
// Last iteration multiplies by albedo to remodulate.
|
|
272
276
|
let readTex = inputTex;
|
|
273
277
|
let writeNode = this._computeNodeA;
|
|
274
278
|
let nextWriteNode = this._computeNodeB;
|
|
@@ -277,26 +281,36 @@ export class BilateralFilter extends RenderStage {
|
|
|
277
281
|
|
|
278
282
|
this.stepSizeU.value = 1 << i;
|
|
279
283
|
this._readTexNode.value = readTex;
|
|
284
|
+
this.isLastIterationU.value = ( i === this.iterations - 1 ) ? 1 : 0;
|
|
280
285
|
|
|
281
286
|
this.renderer.compute( writeNode );
|
|
282
287
|
|
|
283
|
-
// Next iteration reads from what we just wrote
|
|
284
288
|
readTex = ( writeNode === this._computeNodeA )
|
|
285
289
|
? this._storageTexA
|
|
286
290
|
: this._storageTexB;
|
|
287
291
|
|
|
288
|
-
// Swap write direction
|
|
289
292
|
const tmp = writeNode;
|
|
290
293
|
writeNode = nextWriteNode;
|
|
291
294
|
nextWriteNode = tmp;
|
|
292
295
|
|
|
293
296
|
}
|
|
294
297
|
|
|
295
|
-
// Publish final output (last written StorageTexture)
|
|
296
298
|
context.setTexture( 'bilateralFiltering:output', readTex );
|
|
297
299
|
|
|
298
300
|
}
|
|
299
301
|
|
|
302
|
+
// Accepts the same keys as ASVGF presets; unknown keys ignored.
|
|
303
|
+
updateParameters( params ) {
|
|
304
|
+
|
|
305
|
+
if ( ! params ) return;
|
|
306
|
+
if ( params.phiColor !== undefined ) this.phiColor.value = params.phiColor;
|
|
307
|
+
if ( params.phiNormal !== undefined ) this.phiNormal.value = params.phiNormal;
|
|
308
|
+
if ( params.phiDepth !== undefined ) this.phiDepth.value = params.phiDepth;
|
|
309
|
+
if ( params.phiLuminance !== undefined ) this.phiLuminance.value = params.phiLuminance;
|
|
310
|
+
if ( params.atrousIterations !== undefined ) this.iterations = params.atrousIterations;
|
|
311
|
+
|
|
312
|
+
}
|
|
313
|
+
|
|
300
314
|
setSize( width, height ) {
|
|
301
315
|
|
|
302
316
|
this._storageTexA.setSize( width, height );
|
|
@@ -326,6 +340,8 @@ export class BilateralFilter extends RenderStage {
|
|
|
326
340
|
this._storageTexB?.dispose();
|
|
327
341
|
this._readTexNode?.dispose();
|
|
328
342
|
this._normalDepthTexNode?.dispose();
|
|
343
|
+
this._albedoTexNode?.dispose();
|
|
344
|
+
this._varianceTexNode?.dispose();
|
|
329
345
|
|
|
330
346
|
}
|
|
331
347
|
|
package/src/Stages/Compositor.js
CHANGED
|
@@ -57,6 +57,7 @@ export class Compositor extends RenderStage {
|
|
|
57
57
|
|
|
58
58
|
return context.getTexture( 'bloom:output' )
|
|
59
59
|
|| context.getTexture( 'edgeFiltering:output' )
|
|
60
|
+
|| context.getTexture( 'bilateralFiltering:output' )
|
|
60
61
|
|| context.getTexture( 'asvgf:output' )
|
|
61
62
|
|| context.getTexture( 'ssrc:output' )
|
|
62
63
|
|| context.getTexture( 'pathtracer:color' );
|