rayzee 7.10.1 → 7.10.2

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/src/TSL/SSRC.js DELETED
@@ -1,266 +0,0 @@
1
- // Screen-Space Radiance Cache (SSRC) — TSL compute shader builders
2
- //
3
- // Two passes per frame:
4
- // Pass 1 (Temporal): Reproject previous cache via motion vectors, EMA blend
5
- // Pass 2 (Spatial): 8-tap neighbor reuse weighted by normal/depth similarity
6
-
7
- import {
8
- Fn,
9
- vec3,
10
- vec4,
11
- float,
12
- int,
13
- ivec2,
14
- uvec2,
15
- If,
16
- max,
17
- min,
18
- mix,
19
- textureLoad,
20
- textureStore,
21
- localId,
22
- workgroupId,
23
- } from 'three/tsl';
24
- import { normalDepthWeight } from './Common.js';
25
-
26
- // ── Pass 1: Temporal Accumulation ──────────────────────────────────────────
27
-
28
- /**
29
- * Build the temporal reprojection + EMA accumulation compute shader.
30
- *
31
- * @param {object} params
32
- * @param {TextureNode} params.colorTexNode — pathtracer:color (current frame)
33
- * @param {TextureNode} params.ndTexNode — pathtracer:normalDepth (current frame)
34
- * @param {TextureNode} params.motionTexNode — motionVector:screenSpace
35
- * @param {TextureNode} params.readCacheTexNode — previous frame's cache (ping-pong read)
36
- * @param {TextureNode} params.readPrevNDTexNode — previous frame's normalDepth (ping-pong read)
37
- * @param {StorageTexture} params.writeCacheTex — this frame's cache output (ping-pong write)
38
- * @param {StorageTexture} params.writePrevNDTex — current normalDepth saved for next frame
39
- * @param {uniform} params.resW / params.resH — render dimensions
40
- * @param {uniform} params.temporalAlpha — EMA blend factor (0.05–0.2)
41
- * @param {uniform} params.phiNormal — normal edge-stopping exponent
42
- * @param {uniform} params.phiDepth — depth edge-stopping scale
43
- * @param {uniform} params.maxHistory — history frame cap
44
- * @param {uniform} params.framesSinceReset — frames since last reset; 0 = skip cache lookup
45
- * @returns {Fn} — call `.compute([dX, dY, 1], [8, 8, 1])` on the result
46
- */
47
- export function buildTemporalPass( {
48
- colorTexNode,
49
- ndTexNode,
50
- motionTexNode,
51
- readCacheTexNode,
52
- readPrevNDTexNode,
53
- writeCacheTex,
54
- writePrevNDTex,
55
- resW,
56
- resH,
57
- temporalAlpha,
58
- phiNormal,
59
- phiDepth,
60
- maxHistory,
61
- framesSinceReset,
62
- } ) {
63
-
64
- const WG_SIZE = 8;
65
-
66
- return Fn( () => {
67
-
68
- const gx = int( workgroupId.x ).mul( WG_SIZE ).add( int( localId.x ) );
69
- const gy = int( workgroupId.y ).mul( WG_SIZE ).add( int( localId.y ) );
70
-
71
- If( gx.lessThan( int( resW ) ).and( gy.lessThan( int( resH ) ) ), () => {
72
-
73
- const coord = ivec2( gx, gy );
74
-
75
- // Current frame data
76
- const currentColor = textureLoad( colorTexNode, coord ).xyz;
77
- const currentND = textureLoad( ndTexNode, coord );
78
-
79
- // Default: pass-through, history = 1 (used on reset frames or cache misses)
80
- const result = vec4( currentColor, 1.0 ).toVar();
81
-
82
- // Skip cache lookup on the first frame after a reset — prevents stale data bleeding in
83
- If( framesSinceReset.greaterThan( int( 0 ) ), () => {
84
-
85
- // Read motion vector (.xy = UV-space offset, .w = validity)
86
- const motion = textureLoad( motionTexNode, coord );
87
- const motionValid = motion.w.greaterThan( 0.5 );
88
-
89
- // Reprojected pixel coordinates (ASVGF convention: current - motion * res)
90
- const prevXf = float( gx ).sub( motion.x.mul( resW ) );
91
- const prevYf = float( gy ).sub( motion.y.mul( resH ) );
92
- const prevOnScreen = prevXf.greaterThanEqual( 0.0 )
93
- .and( prevXf.lessThan( float( resW ) ) )
94
- .and( prevYf.greaterThanEqual( 0.0 ) )
95
- .and( prevYf.lessThan( float( resH ) ) );
96
-
97
- If( motionValid.and( prevOnScreen ), () => {
98
-
99
- const prevX = int( prevXf ).clamp( int( 0 ), int( resW ).sub( 1 ) );
100
- const prevY = int( prevYf ).clamp( int( 0 ), int( resH ).sub( 1 ) );
101
- const prevCoord = ivec2( prevX, prevY );
102
-
103
- // Edge-stopping: compare normals and depths
104
- // Normals packed in [0,1] in normalDepth texture — decode to [-1,1]
105
- const currentNormal = currentND.xyz.mul( 2.0 ).sub( 1.0 );
106
- const prevND = textureLoad( readPrevNDTexNode, prevCoord );
107
- const prevNormal = prevND.xyz.mul( 2.0 ).sub( 1.0 );
108
-
109
- const similarity = normalDepthWeight(
110
- currentNormal, prevNormal,
111
- currentND.w, prevND.w,
112
- phiNormal, phiDepth
113
- );
114
-
115
- If( similarity.greaterThan( 0.01 ), () => {
116
-
117
- // Read previous cache: .rgb = radiance, .w = history count
118
- const prevCache = textureLoad( readCacheTexNode, prevCoord );
119
- const prevColor = prevCache.xyz;
120
- const prevHistory = prevCache.w;
121
-
122
- // Effective alpha: at least temporalAlpha, but use 1/(history+1) when
123
- // history is low so the first few frames converge faster
124
- const historyAlpha = float( 1.0 ).div( prevHistory.add( 1.0 ) );
125
- const effectiveAlpha = max( temporalAlpha, historyAlpha );
126
-
127
- // Weigh alpha by similarity to suppress bleeding across edges
128
- const blendAlpha = min( effectiveAlpha.div( max( similarity, float( 0.1 ) ) ), 1.0 );
129
-
130
- const blended = mix( prevColor, currentColor, blendAlpha );
131
- const newHistory = min( prevHistory.add( 1.0 ), maxHistory );
132
-
133
- result.assign( vec4( blended, newHistory ) );
134
-
135
- } ).Else( () => {
136
-
137
- // Edge detected or similarity too low — start fresh
138
- result.assign( vec4( currentColor, 1.0 ) );
139
-
140
- } );
141
-
142
- } ).Else( () => {
143
-
144
- // Off-screen or invalid motion — start fresh
145
- result.assign( vec4( currentColor, 1.0 ) );
146
-
147
- } );
148
-
149
- } ); // end framesSinceReset guard
150
-
151
- // Always write — even on reset frames (seeds cache with current color,
152
- // and saves normalDepth so the next frame has valid prevND for edge-stopping)
153
- textureStore( writeCacheTex, uvec2( gx, gy ), result ).toWriteOnly();
154
- textureStore( writePrevNDTex, uvec2( gx, gy ), currentND ).toWriteOnly();
155
-
156
- } );
157
-
158
- } );
159
-
160
- }
161
-
162
- // ── Pass 2: Spatial Neighbor Reuse ─────────────────────────────────────────
163
-
164
- /**
165
- * Build the spatial 8-tap neighbor reuse compute shader.
166
- *
167
- * Samples 8 neighbors at ±spatialRadius in a cross + diagonal pattern.
168
- * Spatial influence fades as temporal history accumulates (trust temporal over spatial).
169
- *
170
- * @param {object} params
171
- * @param {TextureNode} params.ndTexNode — pathtracer:normalDepth (current frame)
172
- * @param {TextureNode} params.readCacheTexNode — temporal cache from pass 1 (just-written)
173
- * @param {StorageTexture} params.outputTex — final SSRC output
174
- * @param {uniform} params.resW / params.resH
175
- * @param {uniform} params.spatialRadius — neighbor offset in pixels (int)
176
- * @param {uniform} params.spatialWeight — max spatial contribution weight
177
- * @param {uniform} params.phiNormal — normal edge-stopping exponent
178
- * @param {uniform} params.phiDepth — depth edge-stopping scale
179
- * @returns {Fn}
180
- */
181
- export function buildSpatialPass( {
182
- colorTexNode,
183
- ndTexNode,
184
- readCacheTexNode,
185
- outputTex,
186
- resW,
187
- resH,
188
- spatialRadius,
189
- spatialWeight,
190
- phiNormal,
191
- phiDepth,
192
- } ) {
193
-
194
- const WG_SIZE = 8;
195
-
196
- // 8 neighbor offsets: axis-aligned ×4 + diagonal ×4
197
- const OFFSETS = [
198
- [ 1, 0 ], [ - 1, 0 ], [ 0, 1 ], [ 0, - 1 ],
199
- [ 1, 1 ], [ - 1, 1 ], [ 1, - 1 ], [ - 1, - 1 ],
200
- ];
201
-
202
- return Fn( () => {
203
-
204
- const gx = int( workgroupId.x ).mul( WG_SIZE ).add( int( localId.x ) );
205
- const gy = int( workgroupId.y ).mul( WG_SIZE ).add( int( localId.y ) );
206
-
207
- If( gx.lessThan( int( resW ) ).and( gy.lessThan( int( resH ) ) ), () => {
208
-
209
- const coord = ivec2( gx, gy );
210
-
211
- // Center pixel
212
- const centerCache = textureLoad( readCacheTexNode, coord );
213
- const centerColor = centerCache.xyz;
214
- const centerHistory = centerCache.w;
215
- const centerND = textureLoad( ndTexNode, coord );
216
- const centerNormal = centerND.xyz.mul( 2.0 ).sub( 1.0 );
217
-
218
- // Adaptive spatial weight: fades to 0 once history >= 16
219
- // (temporal data is reliable by then)
220
- const historyFactor = float( 1.0 ).sub(
221
- centerHistory.div( 16.0 ).clamp( 0.0, 1.0 )
222
- );
223
- const effectiveSpatialWeight = spatialWeight.mul( historyFactor );
224
-
225
- // Weighted sum over 8 neighbors
226
- const weightedSum = vec3( centerColor ).toVar();
227
- const weightSum = float( 1.0 ).toVar();
228
-
229
- for ( let i = 0; i < OFFSETS.length; i ++ ) {
230
-
231
- const [ ox, oy ] = OFFSETS[ i ];
232
-
233
- const nx = gx.add( int( spatialRadius ).mul( ox ) ).clamp( int( 0 ), int( resW ).sub( 1 ) );
234
- const ny = gy.add( int( spatialRadius ).mul( oy ) ).clamp( int( 0 ), int( resH ).sub( 1 ) );
235
-
236
- const neighborCoord = ivec2( nx, ny );
237
- const neighborCache = textureLoad( readCacheTexNode, neighborCoord );
238
- const neighborColor = neighborCache.xyz;
239
-
240
- const neighborND = textureLoad( ndTexNode, neighborCoord );
241
- const neighborNormal = neighborND.xyz.mul( 2.0 ).sub( 1.0 );
242
-
243
- const w = normalDepthWeight(
244
- centerNormal, neighborNormal,
245
- centerND.w, neighborND.w,
246
- phiNormal, phiDepth
247
- );
248
-
249
- weightedSum.addAssign( neighborColor.mul( w ) );
250
- weightSum.addAssign( w );
251
-
252
- }
253
-
254
- // Blend: (1 - effective spatial) * temporal + effective spatial * spatial average
255
- const spatialAverage = weightedSum.div( max( weightSum, 0.0001 ) );
256
- const finalColor = mix( centerColor, spatialAverage, effectiveSpatialWeight );
257
-
258
- // Pass through path tracer alpha to support transparentBackground mode
259
- const ptAlpha = textureLoad( colorTexNode, coord ).w;
260
- textureStore( outputTex, uvec2( gx, gy ), vec4( finalColor, ptAlpha ) ).toWriteOnly();
261
-
262
- } );
263
-
264
- } );
265
-
266
- }