rayzee 6.5.0 → 7.0.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 +24 -5
- package/dist/rayzee.es.js +7554 -7014
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +157 -236
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/EngineDefaults.js +12 -9
- package/src/PathTracerApp.js +118 -26
- package/src/Pipeline/PipelineContext.js +1 -2
- package/src/Pipeline/RenderPipeline.js +1 -1
- package/src/Pipeline/RenderStage.js +1 -1
- package/src/Processor/CameraOptimizer.js +0 -5
- package/src/Processor/GeometryExtractor.js +6 -0
- package/src/Processor/KernelManager.js +277 -0
- package/src/Processor/PackedRayBuffer.js +265 -0
- package/src/Processor/QueueManager.js +173 -0
- package/src/Processor/SceneProcessor.js +1 -0
- package/src/Processor/ShaderBuilder.js +11 -317
- package/src/Processor/StorageTexturePool.js +29 -15
- package/src/Processor/VRAMTracker.js +169 -0
- package/src/Processor/utils.js +11 -110
- package/src/RenderSettings.js +0 -3
- package/src/Stages/ASVGF.js +76 -20
- package/src/Stages/BilateralFilter.js +34 -10
- package/src/Stages/EdgeFilter.js +2 -3
- package/src/Stages/MotionVector.js +16 -9
- package/src/Stages/NormalDepth.js +17 -5
- package/src/Stages/PathTracer.js +671 -1456
- package/src/Stages/PathTracerStage.js +1451 -0
- package/src/Stages/SSRC.js +32 -15
- package/src/Stages/Variance.js +35 -12
- package/src/TSL/CompactKernel.js +110 -0
- package/src/TSL/DebugKernel.js +98 -0
- package/src/TSL/Environment.js +13 -11
- package/src/TSL/ExtendKernel.js +75 -0
- package/src/TSL/FinalWriteKernel.js +121 -0
- package/src/TSL/GenerateKernel.js +109 -0
- package/src/TSL/LightsSampling.js +2 -2
- package/src/TSL/PathTracerCore.js +43 -1039
- package/src/TSL/ShadeKernel.js +873 -0
- package/src/TSL/patches.js +81 -4
- package/src/index.js +3 -0
- package/src/managers/CameraManager.js +1 -1
- package/src/managers/DenoisingManager.js +40 -75
- package/src/managers/EnvironmentManager.js +30 -39
- package/src/managers/OverlayManager.js +7 -22
- package/src/managers/UniformManager.js +0 -3
- package/src/managers/helpers/TileHelper.js +2 -2
- package/src/Stages/AdaptiveSampling.js +0 -483
- package/src/TSL/PathTracer.js +0 -384
- package/src/managers/TileManager.js +0 -298
package/src/TSL/PathTracer.js
DELETED
|
@@ -1,384 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PathTracer.js - Main Path Tracer Entry Point (Compute Shader)
|
|
3
|
-
*
|
|
4
|
-
* Contains:
|
|
5
|
-
* - dithering — anti-banding dither pattern
|
|
6
|
-
* - computeNDCDepth — world position to NDC depth [0,1]
|
|
7
|
-
* - getRequiredSamples — adaptive sampling sample count
|
|
8
|
-
* - pathTracerMain — main entry (sample loop, accumulation, textureStore output)
|
|
9
|
-
*
|
|
10
|
-
* Compute Outputs (via textureStore):
|
|
11
|
-
* - writeColorTex: RGB + alpha (transparent bg: per-sample hit/miss alpha, opaque: 1.0)
|
|
12
|
-
* - writeNDTex: Normal(RGB) + depth(A)
|
|
13
|
-
* - writeAlbedoTex: Albedo(RGB) for OIDN denoiser
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import {
|
|
17
|
-
Fn,
|
|
18
|
-
wgslFn,
|
|
19
|
-
float,
|
|
20
|
-
vec2,
|
|
21
|
-
vec3,
|
|
22
|
-
vec4,
|
|
23
|
-
int,
|
|
24
|
-
uint,
|
|
25
|
-
uvec2,
|
|
26
|
-
clamp,
|
|
27
|
-
mix,
|
|
28
|
-
normalize,
|
|
29
|
-
floor,
|
|
30
|
-
If,
|
|
31
|
-
Loop,
|
|
32
|
-
Break,
|
|
33
|
-
texture,
|
|
34
|
-
textureStore,
|
|
35
|
-
select,
|
|
36
|
-
} from 'three/tsl';
|
|
37
|
-
|
|
38
|
-
import {
|
|
39
|
-
RandomValue,
|
|
40
|
-
getDecorrelatedSeed,
|
|
41
|
-
getStratifiedSample,
|
|
42
|
-
pcgHash,
|
|
43
|
-
} from './Random.js';
|
|
44
|
-
|
|
45
|
-
import { generateRayFromCamera } from './BVHTraversal.js';
|
|
46
|
-
import { Trace, TraceResult } from './PathTracerCore.js';
|
|
47
|
-
import { TraceDebugMode } from './Debugger.js';
|
|
48
|
-
import { Ray } from './Struct.js';
|
|
49
|
-
|
|
50
|
-
// =============================================================================
|
|
51
|
-
// Helper Functions
|
|
52
|
-
// =============================================================================
|
|
53
|
-
|
|
54
|
-
// Dithering to prevent banding in 8-bit output
|
|
55
|
-
export const dithering = Fn( ( [ color, seed ] ) => {
|
|
56
|
-
|
|
57
|
-
const gridPosition = RandomValue( seed );
|
|
58
|
-
const ditherShiftRGB = vec3( 0.25 / 255.0, - 0.25 / 255.0, 0.25 / 255.0 ).toVar();
|
|
59
|
-
|
|
60
|
-
ditherShiftRGB.assign(
|
|
61
|
-
mix( ditherShiftRGB.mul( 2.0 ), ditherShiftRGB.mul( - 2.0 ), gridPosition ),
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
return color.add( ditherShiftRGB );
|
|
65
|
-
|
|
66
|
-
} );
|
|
67
|
-
|
|
68
|
-
// Compute NDC depth from world position for motion vector reprojection
|
|
69
|
-
export const computeNDCDepth = /*@__PURE__*/ wgslFn( `
|
|
70
|
-
fn computeNDCDepth( worldPos: vec3f, cameraProjectionMatrix: mat4x4f, cameraViewMatrix: mat4x4f ) -> f32 {
|
|
71
|
-
let clipPos = cameraProjectionMatrix * cameraViewMatrix * vec4f( worldPos, 1.0f );
|
|
72
|
-
let ndcDepth = clipPos.z / clipPos.w * 0.5f + 0.5f;
|
|
73
|
-
return clamp( ndcDepth, 0.0f, 1.0f );
|
|
74
|
-
}
|
|
75
|
-
` );
|
|
76
|
-
|
|
77
|
-
// NaN/Inf detector for debug mode 11. x != x catches NaN; the abs threshold catches Inf.
|
|
78
|
-
const nanInfToRed = /*@__PURE__*/ wgslFn( `
|
|
79
|
-
fn nanInfToRed( c: vec3f ) -> vec3f {
|
|
80
|
-
let isNan = c.x != c.x || c.y != c.y || c.z != c.z;
|
|
81
|
-
let isInf = abs( c.x ) > 1e30f || abs( c.y ) > 1e30f || abs( c.z ) > 1e30f;
|
|
82
|
-
if ( isNan || isInf ) { return vec3f( 1.0f, 0.0f, 0.0f ); }
|
|
83
|
-
return vec3f( 0.0f );
|
|
84
|
-
}
|
|
85
|
-
` );
|
|
86
|
-
|
|
87
|
-
// Get required samples from adaptive sampling texture
|
|
88
|
-
export const getRequiredSamples = Fn( ( [
|
|
89
|
-
pixelCoord, resolution,
|
|
90
|
-
adaptiveSamplingTexture, adaptiveSamplingMin, adaptiveSamplingMax,
|
|
91
|
-
] ) => {
|
|
92
|
-
|
|
93
|
-
const texCoord = pixelCoord.div( resolution );
|
|
94
|
-
const samplingData = texture( adaptiveSamplingTexture, texCoord, 0 );
|
|
95
|
-
|
|
96
|
-
const result = int( 0 ).toVar();
|
|
97
|
-
|
|
98
|
-
// Early exit for converged pixels
|
|
99
|
-
If( samplingData.b.greaterThan( 0.5 ), () => {
|
|
100
|
-
|
|
101
|
-
result.assign( 0 );
|
|
102
|
-
|
|
103
|
-
} ).Else( () => {
|
|
104
|
-
|
|
105
|
-
const normalizedSamples = samplingData.r;
|
|
106
|
-
const targetSamples = normalizedSamples.mul( float( adaptiveSamplingMax ) );
|
|
107
|
-
const samples = int( floor( targetSamples.add( 0.5 ) ) );
|
|
108
|
-
result.assign( clamp( samples, adaptiveSamplingMin, adaptiveSamplingMax ) );
|
|
109
|
-
|
|
110
|
-
} );
|
|
111
|
-
|
|
112
|
-
return result;
|
|
113
|
-
|
|
114
|
-
} );
|
|
115
|
-
|
|
116
|
-
// =============================================================================
|
|
117
|
-
// Main Path Tracer Implementation (Compute Shader)
|
|
118
|
-
// =============================================================================
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Generates TSL nodes for the path tracer compute kernel body.
|
|
122
|
-
* Must be called from within a compute Fn() scope (e.g., in ShaderBuilder).
|
|
123
|
-
*
|
|
124
|
-
* Previous-frame reads use texture() sampling from MRT RenderTarget textures
|
|
125
|
-
* (populated by copyTextureToTexture after each dispatch).
|
|
126
|
-
*
|
|
127
|
-
* @param {Object} params - All uniforms, textures, and output StorageTextures
|
|
128
|
-
*/
|
|
129
|
-
export const pathTracerMain = ( params ) => {
|
|
130
|
-
|
|
131
|
-
const {
|
|
132
|
-
// Compute pixel coordinate (vec2 with 0.5 offset for pixel center)
|
|
133
|
-
pixelCoord,
|
|
134
|
-
// Output StorageTextures
|
|
135
|
-
writeColorTex, writeNDTex, writeAlbedoTex,
|
|
136
|
-
// Frame / resolution
|
|
137
|
-
resolution, frame,
|
|
138
|
-
samplesPerPixel: numRaysPerPixel,
|
|
139
|
-
visMode,
|
|
140
|
-
cameraWorldMatrix, cameraProjectionMatrixInverse, cameraViewMatrix, cameraProjectionMatrix,
|
|
141
|
-
bvhBuffer, triangleBuffer, materialBuffer,
|
|
142
|
-
albedoMaps, normalMaps, bumpMaps,
|
|
143
|
-
metalnessMaps, roughnessMaps, emissiveMaps,
|
|
144
|
-
displacementMaps,
|
|
145
|
-
directionalLightsBuffer, numDirectionalLights,
|
|
146
|
-
areaLightsBuffer, numAreaLights,
|
|
147
|
-
pointLightsBuffer, numPointLights,
|
|
148
|
-
spotLightsBuffer, numSpotLights,
|
|
149
|
-
envTexture, environmentIntensity, envMatrix,
|
|
150
|
-
envCDFBuffer,
|
|
151
|
-
envTotalSum, envCompensationDelta, envResolution,
|
|
152
|
-
enableEnvironmentLight, useEnvMapIS,
|
|
153
|
-
groundProjectionEnabled, groundProjectionRadius, groundProjectionHeight,
|
|
154
|
-
maxBounceCount, transmissiveBounces, maxSubsurfaceSteps,
|
|
155
|
-
showBackground, transparentBackground, backgroundIntensity,
|
|
156
|
-
fireflyThreshold, globalIlluminationIntensity,
|
|
157
|
-
enableEmissiveTriangleSampling,
|
|
158
|
-
emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower, emissiveBoost,
|
|
159
|
-
lightBVHBuffer, lightBVHNodeCount,
|
|
160
|
-
debugVisScale,
|
|
161
|
-
enableAccumulation, hasPreviousAccumulated,
|
|
162
|
-
prevAccumTexture, prevNormalDepthTexture, prevAlbedoTexture,
|
|
163
|
-
accumulationAlpha, cameraIsMoving,
|
|
164
|
-
useAdaptiveSampling, adaptiveSamplingTexture, adaptiveSamplingMin, adaptiveSamplingMax,
|
|
165
|
-
enableDOF, focalLength, aperture, focusDistance, sceneScale, apertureScale, anamorphicRatio,
|
|
166
|
-
} = params;
|
|
167
|
-
|
|
168
|
-
// Integer coordinates for textureStore writes
|
|
169
|
-
const uintCoord = uvec2( uint( int( pixelCoord.x ) ), uint( int( pixelCoord.y ) ) );
|
|
170
|
-
|
|
171
|
-
// UV for sampling previous-frame textures from MRT RenderTarget
|
|
172
|
-
const prevUV = pixelCoord.div( resolution );
|
|
173
|
-
|
|
174
|
-
// Screen position in NDC [-1, 1]
|
|
175
|
-
// Negate Y for NDC convention (bottom-up)
|
|
176
|
-
const screenPosition = pixelCoord.div( resolution ).mul( 2.0 ).sub( 1.0 ).toVar();
|
|
177
|
-
screenPosition.y.assign( screenPosition.y.negate() );
|
|
178
|
-
|
|
179
|
-
// Initialize pixel accumulator
|
|
180
|
-
const pixelColor = vec4( 0.0 ).toVar();
|
|
181
|
-
const pixelSamples = int( 0 ).toVar();
|
|
182
|
-
|
|
183
|
-
const baseSeed = getDecorrelatedSeed( { pixelCoord, rayIndex: int( 0 ), frame } ).toVar();
|
|
184
|
-
|
|
185
|
-
// MRT — linearDepth is ray distance (sky/miss = 1e6), the convention
|
|
186
|
-
// MotionVector + ASVGF expect downstream.
|
|
187
|
-
const worldNormal = vec3( 0.0, 0.0, 1.0 ).toVar();
|
|
188
|
-
const linearDepth = float( 1e6 ).toVar();
|
|
189
|
-
|
|
190
|
-
// Accumulate per-sample alpha for transparent background (0.0 = env, 1.0 = geometry)
|
|
191
|
-
const pixelAlpha = float( 0.0 ).toVar();
|
|
192
|
-
|
|
193
|
-
const samplesCount = int( numRaysPerPixel ).toVar();
|
|
194
|
-
|
|
195
|
-
// Adaptive sampling
|
|
196
|
-
If( frame.greaterThan( uint( 2 ) ).and( useAdaptiveSampling ), () => {
|
|
197
|
-
|
|
198
|
-
const adaptiveSamples = getRequiredSamples(
|
|
199
|
-
pixelCoord, resolution,
|
|
200
|
-
adaptiveSamplingTexture, adaptiveSamplingMin, adaptiveSamplingMax,
|
|
201
|
-
);
|
|
202
|
-
samplesCount.assign( adaptiveSamples );
|
|
203
|
-
|
|
204
|
-
// Handle converged pixels — carry forward all data from previous frame
|
|
205
|
-
If( samplesCount.equal( int( 0 ) ), () => {
|
|
206
|
-
|
|
207
|
-
If( enableAccumulation.and( hasPreviousAccumulated ), () => {
|
|
208
|
-
|
|
209
|
-
const prevSample = texture( prevAccumTexture, prevUV, 0 );
|
|
210
|
-
pixelColor.assign( prevSample );
|
|
211
|
-
pixelAlpha.assign( prevSample.w );
|
|
212
|
-
|
|
213
|
-
// Carry forward MRT data so accumulation blend preserves them
|
|
214
|
-
const prevND = texture( prevNormalDepthTexture, prevUV, 0 );
|
|
215
|
-
worldNormal.assign( prevND.xyz.mul( 2.0 ).sub( 1.0 ) );
|
|
216
|
-
linearDepth.assign( prevND.w );
|
|
217
|
-
|
|
218
|
-
} ).Else( () => {
|
|
219
|
-
|
|
220
|
-
samplesCount.assign( 1 );
|
|
221
|
-
|
|
222
|
-
} );
|
|
223
|
-
|
|
224
|
-
} );
|
|
225
|
-
|
|
226
|
-
} );
|
|
227
|
-
|
|
228
|
-
// First-hit data for MRT
|
|
229
|
-
const objectNormal = vec3( 0.0 ).toVar();
|
|
230
|
-
const objectColor = vec3( 0.0 ).toVar();
|
|
231
|
-
const objectID = float( - 1000.0 ).toVar();
|
|
232
|
-
|
|
233
|
-
// Pre-compute loop-invariant jitter scale
|
|
234
|
-
const jitterScale = vec2( 2.0 ).div( resolution ).toVar();
|
|
235
|
-
|
|
236
|
-
// Main sample loop
|
|
237
|
-
Loop( { start: int( 0 ), end: samplesCount, type: 'int', condition: '<' }, ( { i: rayIndex } ) => {
|
|
238
|
-
|
|
239
|
-
const seed = pcgHash( { state: baseSeed.add( uint( rayIndex ) ) } ).toVar();
|
|
240
|
-
|
|
241
|
-
const stratifiedJitter = getStratifiedSample(
|
|
242
|
-
pixelCoord, rayIndex, samplesCount, seed, resolution, frame,
|
|
243
|
-
).toVar();
|
|
244
|
-
|
|
245
|
-
// Debug mode 9: Visualize stratified samples
|
|
246
|
-
If( visMode.equal( int( 9 ) ), () => {
|
|
247
|
-
|
|
248
|
-
pixelColor.assign( vec4( stratifiedJitter, 1.0, 1.0 ) );
|
|
249
|
-
pixelSamples.assign( 1 );
|
|
250
|
-
Break();
|
|
251
|
-
|
|
252
|
-
} );
|
|
253
|
-
|
|
254
|
-
const jitter = stratifiedJitter.sub( 0.5 ).mul( jitterScale );
|
|
255
|
-
const jitteredScreenPosition = screenPosition.add( jitter );
|
|
256
|
-
|
|
257
|
-
const ray = Ray.wrap( generateRayFromCamera(
|
|
258
|
-
jitteredScreenPosition, seed,
|
|
259
|
-
cameraWorldMatrix, cameraProjectionMatrixInverse,
|
|
260
|
-
enableDOF, focalLength, aperture, focusDistance, sceneScale, apertureScale, anamorphicRatio,
|
|
261
|
-
) );
|
|
262
|
-
|
|
263
|
-
const sampleColor = vec4( 0.0 ).toVar();
|
|
264
|
-
|
|
265
|
-
// Debug or normal trace (mode 11 runs the full path tracer so we can sniff NaN at the end)
|
|
266
|
-
If( visMode.greaterThan( int( 0 ) ).and( visMode.notEqual( int( 11 ) ) ), () => {
|
|
267
|
-
|
|
268
|
-
sampleColor.assign( TraceDebugMode(
|
|
269
|
-
ray.origin, ray.direction,
|
|
270
|
-
bvhBuffer,
|
|
271
|
-
triangleBuffer,
|
|
272
|
-
materialBuffer,
|
|
273
|
-
envTexture, envMatrix, environmentIntensity, enableEnvironmentLight,
|
|
274
|
-
visMode, debugVisScale,
|
|
275
|
-
pixelCoord, resolution,
|
|
276
|
-
albedoMaps, normalMaps, bumpMaps,
|
|
277
|
-
metalnessMaps, roughnessMaps, emissiveMaps,
|
|
278
|
-
cameraProjectionMatrix, cameraViewMatrix,
|
|
279
|
-
frame,
|
|
280
|
-
) );
|
|
281
|
-
|
|
282
|
-
} ).Else( () => {
|
|
283
|
-
|
|
284
|
-
// Normal path tracing
|
|
285
|
-
const traceResult = TraceResult.wrap( Trace(
|
|
286
|
-
ray, seed, rayIndex,
|
|
287
|
-
bvhBuffer,
|
|
288
|
-
triangleBuffer,
|
|
289
|
-
materialBuffer,
|
|
290
|
-
albedoMaps, normalMaps, bumpMaps,
|
|
291
|
-
metalnessMaps, roughnessMaps, emissiveMaps,
|
|
292
|
-
displacementMaps,
|
|
293
|
-
directionalLightsBuffer, numDirectionalLights,
|
|
294
|
-
areaLightsBuffer, numAreaLights,
|
|
295
|
-
pointLightsBuffer, numPointLights,
|
|
296
|
-
spotLightsBuffer, numSpotLights,
|
|
297
|
-
envTexture, environmentIntensity, envMatrix,
|
|
298
|
-
envCDFBuffer,
|
|
299
|
-
envTotalSum, envCompensationDelta, envResolution,
|
|
300
|
-
enableEnvironmentLight, useEnvMapIS,
|
|
301
|
-
groundProjectionEnabled, groundProjectionRadius, groundProjectionHeight,
|
|
302
|
-
maxBounceCount, transmissiveBounces, maxSubsurfaceSteps,
|
|
303
|
-
backgroundIntensity, showBackground, transparentBackground,
|
|
304
|
-
fireflyThreshold, globalIlluminationIntensity,
|
|
305
|
-
enableEmissiveTriangleSampling,
|
|
306
|
-
emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower, emissiveBoost,
|
|
307
|
-
lightBVHBuffer, lightBVHNodeCount,
|
|
308
|
-
pixelCoord, resolution, frame,
|
|
309
|
-
) );
|
|
310
|
-
|
|
311
|
-
sampleColor.assign( traceResult.radiance );
|
|
312
|
-
|
|
313
|
-
// Accumulate first-hit data from primary rays
|
|
314
|
-
If( rayIndex.equal( int( 0 ) ), () => {
|
|
315
|
-
|
|
316
|
-
objectNormal.assign( traceResult.objectNormal );
|
|
317
|
-
objectColor.assign( traceResult.objectColor );
|
|
318
|
-
objectID.assign( traceResult.objectID );
|
|
319
|
-
|
|
320
|
-
// Set MRT data from first hit (only for geometry hits — miss rays have zero normal,
|
|
321
|
-
// and normalize(vec3(0)) = NaN which would corrupt the OIDN denoiser input)
|
|
322
|
-
If( traceResult.firstHitDistance.lessThan( 1e9 ), () => {
|
|
323
|
-
|
|
324
|
-
worldNormal.assign( normalize( traceResult.objectNormal ) );
|
|
325
|
-
linearDepth.assign( traceResult.firstHitDistance );
|
|
326
|
-
|
|
327
|
-
} );
|
|
328
|
-
|
|
329
|
-
} );
|
|
330
|
-
|
|
331
|
-
} );
|
|
332
|
-
|
|
333
|
-
pixelColor.addAssign( sampleColor );
|
|
334
|
-
pixelAlpha.addAssign( sampleColor.w );
|
|
335
|
-
pixelSamples.addAssign( 1 );
|
|
336
|
-
|
|
337
|
-
} );
|
|
338
|
-
|
|
339
|
-
// Average samples
|
|
340
|
-
If( pixelSamples.greaterThan( int( 0 ) ), () => {
|
|
341
|
-
|
|
342
|
-
pixelColor.divAssign( float( pixelSamples ) );
|
|
343
|
-
pixelAlpha.divAssign( float( pixelSamples ) );
|
|
344
|
-
|
|
345
|
-
} );
|
|
346
|
-
|
|
347
|
-
// Temporal accumulation
|
|
348
|
-
const finalColor = pixelColor.xyz.toVar();
|
|
349
|
-
const finalNormalDepth = vec4( worldNormal.mul( 0.5 ).add( 0.5 ), linearDepth ).toVar();
|
|
350
|
-
const finalAlbedo = vec3( objectColor ).toVar();
|
|
351
|
-
|
|
352
|
-
// Output alpha: accumulated per-sample alpha when transparent, otherwise 1.0
|
|
353
|
-
const outputAlpha = select( transparentBackground, pixelAlpha, float( 1.0 ) ).toVar();
|
|
354
|
-
|
|
355
|
-
If( enableAccumulation.and( cameraIsMoving.not() ).and( frame.greaterThan( uint( 0 ) ) ).and( hasPreviousAccumulated ).and( visMode.notEqual( int( 11 ) ) ), () => {
|
|
356
|
-
|
|
357
|
-
const prevAccumSample = texture( prevAccumTexture, prevUV, 0 ).toVar();
|
|
358
|
-
|
|
359
|
-
finalColor.assign( mix( prevAccumSample.xyz, pixelColor.xyz, accumulationAlpha ) );
|
|
360
|
-
finalNormalDepth.assign( mix( texture( prevNormalDepthTexture, prevUV, 0 ), finalNormalDepth, accumulationAlpha ) );
|
|
361
|
-
finalAlbedo.assign( mix( texture( prevAlbedoTexture, prevUV, 0 ).xyz, finalAlbedo, accumulationAlpha ) );
|
|
362
|
-
|
|
363
|
-
// Temporally accumulate alpha from previous frame's gColor.w
|
|
364
|
-
If( transparentBackground, () => {
|
|
365
|
-
|
|
366
|
-
outputAlpha.assign( mix( prevAccumSample.w, pixelAlpha, accumulationAlpha ) );
|
|
367
|
-
|
|
368
|
-
} );
|
|
369
|
-
|
|
370
|
-
} );
|
|
371
|
-
|
|
372
|
-
// NaN/Inf debug: red where the path tracer output isn't finite, black otherwise.
|
|
373
|
-
If( visMode.equal( int( 11 ) ), () => {
|
|
374
|
-
|
|
375
|
-
finalColor.assign( nanInfToRed( finalColor ) );
|
|
376
|
-
|
|
377
|
-
} );
|
|
378
|
-
|
|
379
|
-
// Write outputs to StorageTextures
|
|
380
|
-
textureStore( writeColorTex, uintCoord, vec4( finalColor.xyz, outputAlpha ) ).toWriteOnly();
|
|
381
|
-
textureStore( writeNDTex, uintCoord, finalNormalDepth ).toWriteOnly();
|
|
382
|
-
textureStore( writeAlbedoTex, uintCoord, vec4( finalAlbedo, 1.0 ) ).toWriteOnly();
|
|
383
|
-
|
|
384
|
-
};
|
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TileManager.js
|
|
3
|
-
* Handles all tile-based rendering logic including tile bounds calculation,
|
|
4
|
-
* spiral order generation, and tile bounds calculation.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export class TileManager {
|
|
8
|
-
|
|
9
|
-
constructor( width, height, tiles ) {
|
|
10
|
-
|
|
11
|
-
this.width = width;
|
|
12
|
-
this.height = height;
|
|
13
|
-
this.tiles = tiles;
|
|
14
|
-
this.tileIndex = 0;
|
|
15
|
-
|
|
16
|
-
// Performance caches
|
|
17
|
-
this.tileBoundsCache = new Map();
|
|
18
|
-
this.totalTilesCache = tiles * tiles;
|
|
19
|
-
|
|
20
|
-
// Tile rendering state
|
|
21
|
-
this.currentTileBounds = null;
|
|
22
|
-
|
|
23
|
-
// Generate initial spiral order
|
|
24
|
-
this.spiralOrder = this.generateSpiralOrder( tiles );
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Calculate the scissor bounds for a given tile
|
|
30
|
-
* @param {number} tileIndex - The index of the tile
|
|
31
|
-
* @param {number} totalTiles - Total number of tiles per row/column
|
|
32
|
-
* @param {number} width - Render target width
|
|
33
|
-
* @param {number} height - Render target height
|
|
34
|
-
* @returns {Object} - Scissor bounds {x, y, width, height}
|
|
35
|
-
*/
|
|
36
|
-
calculateTileBounds( tileIndex, totalTiles, width, height ) {
|
|
37
|
-
|
|
38
|
-
// Use cache to avoid recalculation
|
|
39
|
-
const cacheKey = `${tileIndex}-${totalTiles}-${width}-${height}`;
|
|
40
|
-
if ( this.tileBoundsCache.has( cacheKey ) ) {
|
|
41
|
-
|
|
42
|
-
return this.tileBoundsCache.get( cacheKey );
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Calculate tile size using ceiling division to ensure all pixels are covered
|
|
47
|
-
const tileWidth = Math.ceil( width / totalTiles );
|
|
48
|
-
const tileHeight = Math.ceil( height / totalTiles );
|
|
49
|
-
|
|
50
|
-
// Calculate tile coordinates
|
|
51
|
-
const tileX = tileIndex % totalTiles;
|
|
52
|
-
const tileY = Math.floor( tileIndex / totalTiles );
|
|
53
|
-
|
|
54
|
-
// Calculate pixel bounds
|
|
55
|
-
const x = tileX * tileWidth;
|
|
56
|
-
const y = tileY * tileHeight;
|
|
57
|
-
|
|
58
|
-
// Clamp to actual render target bounds
|
|
59
|
-
const clampedWidth = Math.min( tileWidth, width - x );
|
|
60
|
-
const clampedHeight = Math.min( tileHeight, height - y );
|
|
61
|
-
|
|
62
|
-
const bounds = {
|
|
63
|
-
x: x,
|
|
64
|
-
y: y,
|
|
65
|
-
width: clampedWidth,
|
|
66
|
-
height: clampedHeight
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// Cache the result
|
|
70
|
-
this.tileBoundsCache.set( cacheKey, bounds );
|
|
71
|
-
return bounds;
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Set up scissor testing for tile rendering
|
|
77
|
-
* @param {Object} renderer - The Three.js renderer
|
|
78
|
-
* @param {Object} bounds - Scissor bounds {x, y, width, height}
|
|
79
|
-
*/
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Generate spiral order for tile rendering (center-out pattern)
|
|
83
|
-
* @param {number} tiles - Number of tiles per row/column
|
|
84
|
-
* @returns {Array<number>} - Array of tile indices in spiral order
|
|
85
|
-
*/
|
|
86
|
-
generateSpiralOrder( tiles ) {
|
|
87
|
-
|
|
88
|
-
const totalTiles = tiles * tiles;
|
|
89
|
-
const tilePositions = [];
|
|
90
|
-
|
|
91
|
-
// Create array of tile positions with their distances from center
|
|
92
|
-
for ( let i = 0; i < totalTiles; i ++ ) {
|
|
93
|
-
|
|
94
|
-
const x = i % tiles;
|
|
95
|
-
const y = Math.floor( i / tiles );
|
|
96
|
-
|
|
97
|
-
// Improved distance calculation for better ordering with any tile count
|
|
98
|
-
const center = ( tiles - 1 ) / 2;
|
|
99
|
-
const dx = x - center;
|
|
100
|
-
const dy = y - center;
|
|
101
|
-
|
|
102
|
-
// Use Manhattan distance as primary sort key for more predictable ordering
|
|
103
|
-
const manhattanDistance = Math.abs( dx ) + Math.abs( dy );
|
|
104
|
-
|
|
105
|
-
// Use Euclidean distance as secondary sort key for smooth transitions
|
|
106
|
-
const euclideanDistance = Math.sqrt( dx * dx + dy * dy );
|
|
107
|
-
|
|
108
|
-
// Calculate angle with better precision for spiral ordering
|
|
109
|
-
let angle = Math.atan( dy, dx );
|
|
110
|
-
// Normalize angle to 0-2π range
|
|
111
|
-
if ( angle < 0 ) angle += 2 * Math.PI;
|
|
112
|
-
|
|
113
|
-
// Add small offset based on position to ensure deterministic ordering
|
|
114
|
-
const positionOffset = ( x + y * tiles ) * 0.001;
|
|
115
|
-
|
|
116
|
-
tilePositions.push( {
|
|
117
|
-
index: i,
|
|
118
|
-
x,
|
|
119
|
-
y,
|
|
120
|
-
manhattanDistance,
|
|
121
|
-
euclideanDistance,
|
|
122
|
-
angle,
|
|
123
|
-
positionOffset
|
|
124
|
-
} );
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Improved sorting: Manhattan distance first, then Euclidean, then angle
|
|
129
|
-
tilePositions.sort( ( a, b ) => {
|
|
130
|
-
|
|
131
|
-
// Primary: Manhattan distance (creates more predictable rings)
|
|
132
|
-
const manhattanDiff = a.manhattanDistance - b.manhattanDistance;
|
|
133
|
-
if ( Math.abs( manhattanDiff ) > 0.01 ) {
|
|
134
|
-
|
|
135
|
-
return manhattanDiff;
|
|
136
|
-
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Secondary: Euclidean distance (smooth transitions within rings)
|
|
140
|
-
const euclideanDiff = a.euclideanDistance - b.euclideanDistance;
|
|
141
|
-
if ( Math.abs( euclideanDiff ) > 0.01 ) {
|
|
142
|
-
|
|
143
|
-
return euclideanDiff;
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Tertiary: Angle for spiral effect within same distance
|
|
148
|
-
const angleDiff = a.angle - b.angle;
|
|
149
|
-
if ( Math.abs( angleDiff ) > 0.01 ) {
|
|
150
|
-
|
|
151
|
-
return angleDiff;
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Final: Position offset for deterministic ordering
|
|
156
|
-
return a.positionOffset - b.positionOffset;
|
|
157
|
-
|
|
158
|
-
} );
|
|
159
|
-
|
|
160
|
-
return tilePositions.map( pos => pos.index );
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Handle tile rendering logic for a given frame and render mode
|
|
166
|
-
* @param {Object} renderer - The Three.js renderer
|
|
167
|
-
* @param {number} renderMode - Current render mode (0 = full, 1 = tiled)
|
|
168
|
-
* @param {number} frameValue - Current frame number
|
|
169
|
-
* @returns {Object} - Tile rendering info {tileIndex, tileBounds, shouldSwapTargets, isCompleteCycle}
|
|
170
|
-
*/
|
|
171
|
-
handleTileRendering( renderer, renderMode, frameValue ) {
|
|
172
|
-
|
|
173
|
-
let shouldSwapTargets = true;
|
|
174
|
-
let currentTileIndex = - 1;
|
|
175
|
-
let tileBounds = null;
|
|
176
|
-
let isCompleteCycle = true;
|
|
177
|
-
|
|
178
|
-
if ( renderMode === 1 ) {
|
|
179
|
-
|
|
180
|
-
if ( frameValue === 0 ) {
|
|
181
|
-
|
|
182
|
-
currentTileIndex = - 1;
|
|
183
|
-
isCompleteCycle = true;
|
|
184
|
-
|
|
185
|
-
} else {
|
|
186
|
-
|
|
187
|
-
const linearTileIndex = ( frameValue - 1 ) % this.totalTilesCache;
|
|
188
|
-
currentTileIndex = this.spiralOrder[ linearTileIndex ];
|
|
189
|
-
tileBounds = this.calculateTileBounds( currentTileIndex, this.tiles, this.width, this.height );
|
|
190
|
-
isCompleteCycle = ( linearTileIndex === this.totalTilesCache - 1 );
|
|
191
|
-
shouldSwapTargets = isCompleteCycle;
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
} else {
|
|
196
|
-
|
|
197
|
-
currentTileIndex = - 1;
|
|
198
|
-
isCompleteCycle = true;
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
this.tileIndex = currentTileIndex;
|
|
203
|
-
|
|
204
|
-
return {
|
|
205
|
-
tileIndex: currentTileIndex,
|
|
206
|
-
tileBounds,
|
|
207
|
-
shouldSwapTargets,
|
|
208
|
-
isCompleteCycle
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Set the number of tiles and regenerate order
|
|
215
|
-
* @param {number} newTileCount - New tile count per row/column
|
|
216
|
-
*/
|
|
217
|
-
setTileCount( newTileCount ) {
|
|
218
|
-
|
|
219
|
-
// Validate tile count and provide warnings
|
|
220
|
-
if ( newTileCount < 1 ) {
|
|
221
|
-
|
|
222
|
-
console.warn( 'TileManager: Tile count must be at least 1, clamping to 1' );
|
|
223
|
-
newTileCount = 1;
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if ( newTileCount > 10 ) {
|
|
228
|
-
|
|
229
|
-
console.warn( 'TileManager: Tile count > 10 may cause performance issues' );
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if ( newTileCount > 6 ) {
|
|
234
|
-
|
|
235
|
-
const totalTiles = newTileCount * newTileCount;
|
|
236
|
-
console.warn( `TileManager: ${newTileCount}x${newTileCount} = ${totalTiles} tiles may impact performance and memory usage` );
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
this.tiles = newTileCount;
|
|
242
|
-
this.totalTilesCache = newTileCount * newTileCount;
|
|
243
|
-
this.tileIndex = 0;
|
|
244
|
-
this.spiralOrder = this.generateSpiralOrder( newTileCount );
|
|
245
|
-
this.tileBoundsCache.clear(); // Clear cache when tile count changes
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Update dimensions when render target size changes
|
|
251
|
-
* @param {number} width - New width
|
|
252
|
-
* @param {number} height - New height
|
|
253
|
-
*/
|
|
254
|
-
setSize( width, height ) {
|
|
255
|
-
|
|
256
|
-
this.width = width;
|
|
257
|
-
this.height = height;
|
|
258
|
-
this.tileBoundsCache.clear(); // Clear cache when size changes
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Calculate completion threshold for tiled rendering
|
|
264
|
-
* @param {number} maxFrames - Maximum frames to render
|
|
265
|
-
* @returns {number} - Total frames needed for completion
|
|
266
|
-
*/
|
|
267
|
-
calculateCompletionThreshold( maxFrames ) {
|
|
268
|
-
|
|
269
|
-
return this.totalTilesCache * maxFrames;
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Get current tile information
|
|
275
|
-
* @returns {Object} - Current tile state
|
|
276
|
-
*/
|
|
277
|
-
getCurrentTileInfo() {
|
|
278
|
-
|
|
279
|
-
return {
|
|
280
|
-
tileIndex: this.tileIndex,
|
|
281
|
-
tiles: this.tiles,
|
|
282
|
-
totalTiles: this.totalTilesCache,
|
|
283
|
-
currentBounds: this.currentTileBounds
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Clean up resources
|
|
290
|
-
*/
|
|
291
|
-
dispose() {
|
|
292
|
-
|
|
293
|
-
this.tileBoundsCache.clear();
|
|
294
|
-
this.currentTileBounds = null;
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
}
|