rayzee 7.20.3 → 7.22.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.
Files changed (42) hide show
  1. package/README.md +75 -4
  2. package/dist/rayzee.es.js +2821 -2854
  3. package/dist/rayzee.umd.js +60 -58
  4. package/package.json +1 -1
  5. package/src/EngineDefaults.js +9 -2
  6. package/src/PathTracerApp.js +83 -0
  7. package/src/Pipeline/BindingAudit.js +178 -0
  8. package/src/Pipeline/RenderPipeline.js +5 -2
  9. package/src/Processor/BVHBuilder.js +1 -4
  10. package/src/Processor/CameraOptimizer.js +0 -5
  11. package/src/Processor/KernelManager.js +17 -70
  12. package/src/Processor/PackedRayBuffer.js +33 -8
  13. package/src/Processor/QueueManager.js +4 -3
  14. package/src/Processor/SceneProcessor.js +10 -4
  15. package/src/Processor/TreeletOptimizer.js +0 -7
  16. package/src/RenderSettings.js +41 -6
  17. package/src/Stages/ASVGF.js +10 -10
  18. package/src/Stages/BilateralFilter.js +70 -11
  19. package/src/Stages/EdgeFilter.js +44 -6
  20. package/src/Stages/NormalDepth.js +26 -15
  21. package/src/Stages/PathTracer.js +36 -34
  22. package/src/Stages/PathTracerStage.js +15 -46
  23. package/src/TSL/BVHTraversal.js +1 -67
  24. package/src/TSL/CameraRay.js +141 -0
  25. package/src/TSL/Common.js +1 -47
  26. package/src/TSL/DebugKernel.js +4 -5
  27. package/src/TSL/EmissiveSampling.js +40 -44
  28. package/src/TSL/ExtendKernel.js +11 -3
  29. package/src/TSL/FinalWriteKernel.js +1 -14
  30. package/src/TSL/GenerateKernel.js +11 -8
  31. package/src/TSL/LightsCore.js +3 -0
  32. package/src/TSL/LightsDirect.js +4 -4
  33. package/src/TSL/LightsSampling.js +138 -327
  34. package/src/TSL/PathTracerCore.js +2 -2
  35. package/src/TSL/Random.js +2 -2
  36. package/src/TSL/ShadeKernel.js +39 -17
  37. package/src/TSL/SortGlobalKernels.js +52 -7
  38. package/src/TSL/Struct.js +0 -10
  39. package/src/index.js +5 -0
  40. package/src/managers/DenoisingManager.js +13 -0
  41. package/src/managers/EnvironmentManager.js +6 -8
  42. package/src/managers/UniformManager.js +12 -2
@@ -545,61 +545,57 @@ export const calculateEmissiveTriangleContributionDebug = Fn( ( [
545
545
  distance: float( 0.0 ),
546
546
  } ).toVar();
547
547
 
548
- // Skip for very rough diffuse surfaces on secondary bounces
549
- const skip = bounceIndex.greaterThan( int( 1 ) ).and( material.roughness.greaterThan( 0.9 ) ).and( material.metalness.lessThan( 0.1 ) );
548
+ // No rough-diffuse secondary-bounce skip: dropping NEE while the emissive-hit MIS still
549
+ // down-weights BSDF hits deletes emitter energy from deep GI.
550
550
 
551
- If( skip.not(), () => {
552
-
553
- // Sample emissive triangle (CDF importance-weighted)
554
- const emissiveSample = EmissiveSample.wrap( sampleEmissiveTriangle(
555
- hitPoint, normal, rngState,
556
- pixelCoord, resolution, frame, dimBase,
557
- emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower,
558
- triangleBuffer,
559
- ) );
560
-
561
- If( emissiveSample.valid.and( emissiveSample.pdf.greaterThan( 0.0 ) ), () => {
551
+ // Sample emissive triangle (CDF importance-weighted)
552
+ const emissiveSample = EmissiveSample.wrap( sampleEmissiveTriangle(
553
+ hitPoint, normal, rngState,
554
+ pixelCoord, resolution, frame, dimBase,
555
+ emissiveTriangleBuffer, emissiveVec4Offset, emissiveTriangleCount, emissiveTotalPower,
556
+ triangleBuffer,
557
+ ) );
562
558
 
563
- result.hasEmissive.assign( true );
564
- result.emissionOnly.assign( emissiveSample.emission );
565
- result.distance.assign( emissiveSample.distance );
559
+ If( emissiveSample.valid.and( emissiveSample.pdf.greaterThan( 0.0 ) ), () => {
566
560
 
567
- // Check geometric validity
568
- const NoL = max( float( 0.0 ), dot( normal, emissiveSample.direction ) );
561
+ result.hasEmissive.assign( true );
562
+ result.emissionOnly.assign( emissiveSample.emission );
563
+ result.distance.assign( emissiveSample.distance );
569
564
 
570
- If( NoL.greaterThan( 0.0 ), () => {
565
+ // Check geometric validity
566
+ const NoL = max( float( 0.0 ), dot( normal, emissiveSample.direction ) );
571
567
 
572
- // Calculate ray offset for shadow ray
573
- const rayOffset = calculateRayOffsetFn( hitPoint, normal, material );
574
- const rayOrigin = hitPoint.add( rayOffset );
568
+ If( NoL.greaterThan( 0.0 ), () => {
575
569
 
576
- // Trace shadow ray
577
- const shadowDist = emissiveSample.distance.sub( 0.001 );
578
- const visibility = traceShadowRayFn( rayOrigin, emissiveSample.direction, shadowDist );
570
+ // Calculate ray offset for shadow ray
571
+ const rayOffset = calculateRayOffsetFn( hitPoint, normal, material );
572
+ const rayOrigin = hitPoint.add( rayOffset );
579
573
 
580
- If( visibility.greaterThan( 0.0 ), () => {
574
+ // Trace shadow ray
575
+ const shadowDist = emissiveSample.distance.sub( 0.001 );
576
+ const visibility = traceShadowRayFn( rayOrigin, emissiveSample.direction, shadowDist );
581
577
 
582
- // Share H + dot products between BRDF eval and PDF (computeDotProducts
583
- // would otherwise run twice with identical inputs).
584
- const dots = DotProducts.wrap( computeDotProductsAniso( normal, viewDir, emissiveSample.direction, material ) );
585
- const brdfValue = evaluateMaterialResponseFromDots( material, dots );
586
- const brdfPdf = calculateMaterialPDFFromDots( material, dots );
578
+ If( visibility.greaterThan( 0.0 ), () => {
587
579
 
588
- // MIS weight: balance light sampling vs BRDF sampling
589
- const misWeight = select(
590
- brdfPdf.greaterThan( 0.0 ),
591
- powerHeuristic( { pdf1: emissiveSample.pdf, pdf2: brdfPdf } ),
592
- float( 1.0 )
593
- );
580
+ // Share H + dot products between BRDF eval and PDF (computeDotProducts
581
+ // would otherwise run twice with identical inputs).
582
+ const dots = DotProducts.wrap( computeDotProductsAniso( normal, viewDir, emissiveSample.direction, material ) );
583
+ const brdfValue = evaluateMaterialResponseFromDots( material, dots );
584
+ const brdfPdf = calculateMaterialPDFFromDots( material, dots );
594
585
 
595
- // MC estimator: Le * brdf * cos_surface / pdf_solidAngle
596
- // emissiveBoost is an additional user-controlled intensity multiplier
597
- result.contribution.assign(
598
- emissiveSample.emission.mul( brdfValue ).mul( NoL )
599
- .div( emissiveSample.pdf ).mul( visibility ).mul( emissiveBoost ).mul( misWeight )
600
- );
586
+ // MIS weight: balance light sampling vs BRDF sampling
587
+ const misWeight = select(
588
+ brdfPdf.greaterThan( 0.0 ),
589
+ powerHeuristic( { pdf1: emissiveSample.pdf, pdf2: brdfPdf } ),
590
+ float( 1.0 )
591
+ );
601
592
 
602
- } );
593
+ // MC estimator: Le * brdf * cos_surface / pdf_solidAngle
594
+ // emissiveBoost is an additional user-controlled intensity multiplier
595
+ result.contribution.assign(
596
+ emissiveSample.emission.mul( brdfValue ).mul( NoL )
597
+ .div( emissiveSample.pdf ).mul( visibility ).mul( emissiveBoost ).mul( misWeight )
598
+ );
603
599
 
604
600
  } );
605
601
 
@@ -13,17 +13,17 @@ import {
13
13
  import { traverseBVH } from './BVHTraversal.js';
14
14
  import { Ray, HitInfo } from './Struct.js';
15
15
  import {
16
- readRayOrigin, readRayDirection, readMediumStack,
16
+ readRayOrigin, readRayDirection, readRayBounceFlags, readMediumStack,
17
17
  writeHitPacked,
18
18
  } from '../Processor/PackedRayBuffer.js';
19
- import { COUNTER } from '../Processor/QueueManager.js';
19
+ import { COUNTER, RAY_FLAG } from '../Processor/QueueManager.js';
20
20
 
21
21
  const WG_SIZE = 256;
22
22
 
23
23
  export function buildExtendKernel( params ) {
24
24
 
25
25
  const {
26
- bvhBuffer, triangleBuffer, materialBuffer,
26
+ bvhBuffer, triangleBuffer,
27
27
  rayBufferRO,
28
28
  hitBufferRW,
29
29
  activeIndicesRO,
@@ -45,6 +45,14 @@ export function buildExtendKernel( params ) {
45
45
 
46
46
  const rayID = activeIndicesRO.element( threadIdx );
47
47
 
48
+ // Parity with Shade's guard. Free — the flags share DIR_FLAGS.w with the direction read below.
49
+ // Only culls work on the pinned-dispatch path, whose identity list still holds dead rays.
50
+ If( readRayBounceFlags( rayBufferRO, rayID ).bitAnd( uint( RAY_FLAG.ACTIVE ) ).equal( uint( 0 ) ), () => {
51
+
52
+ Return();
53
+
54
+ } );
55
+
48
56
  const origin = readRayOrigin( rayBufferRO, rayID ).toVar();
49
57
  const direction = readRayDirection( rayBufferRO, rayID ).toVar();
50
58
 
@@ -54,7 +54,7 @@ export function buildFinalWriteKernel( params ) {
54
54
 
55
55
  // Tier-2 per-pixel freeze: streakBufferRW = per-pixel freeze-candidate streak (stamped here);
56
56
  // frozenMaskRO = dilated frozen mask from buildActivePixels (pass-through gates on it, not streak).
57
- usePixelFreeze, pixelFreezeThreshold, pixelFreezeStability, streakBufferRW, frozenMaskRO,
57
+ usePixelFreeze, pixelFreezeThreshold, streakBufferRW, frozenMaskRO,
58
58
  } = params;
59
59
 
60
60
  const auxOn = auxGBufferEnabled.greaterThan( uint( 0 ) );
@@ -184,25 +184,12 @@ export function buildFinalWriteKernel( params ) {
184
184
 
185
185
  } );
186
186
 
187
- If( wasFrozen, () => {
188
-
189
- // Already frozen (passed through above) — count it.
190
- atomicAdd( counters.element( uint( COUNTER.FROZEN_COUNT ) ), uint( 1 ) );
191
-
192
- } );
193
-
194
187
  If( wasFrozen.not().and( frame.greaterThan( uint( 0 ) ) ), () => {
195
188
 
196
189
  const freezeCandidate = frame.greaterThanEqual( uint( adaptiveMinSamples ) )
197
190
  .and( relErr.lessThan( pixelFreezeThreshold ) );
198
191
  const newStreak = select( freezeCandidate, streakBufferRW.element( pixelId ).add( uint( 1 ) ), uint( 0 ) ).toVar();
199
192
  streakBufferRW.element( pixelId ).assign( newStreak );
200
- // Just reached K → frozen next frame; count it now so the frozen population is current.
201
- If( newStreak.greaterThanEqual( uint( pixelFreezeStability ) ), () => {
202
-
203
- atomicAdd( counters.element( uint( COUNTER.FROZEN_COUNT ) ), uint( 1 ) );
204
-
205
- } );
206
193
 
207
194
  } );
208
195
 
@@ -22,7 +22,7 @@ import {
22
22
  getStratifiedSample,
23
23
  } from './Random.js';
24
24
 
25
- import { generateRayFromCamera } from './BVHTraversal.js';
25
+ import { generateRayFromCamera } from './CameraRay.js';
26
26
  import { Ray } from './Struct.js';
27
27
  import { RAY_FLAG, COUNTER } from '../Processor/QueueManager.js';
28
28
  import {
@@ -39,6 +39,7 @@ export function buildGenerateKernel( params ) {
39
39
  rayBufferRW, rngBufferRW, gBufferRW,
40
40
  resolution, frame,
41
41
  cameraWorldMatrix, cameraProjectionMatrixInverse,
42
+ cameraProjection, panoLonRange, panoLatRange, panoLevelHorizon,
42
43
  enableDOF, focalLength, aperture, focusDistance, sceneScale, apertureScale, anamorphicRatio,
43
44
  renderWidth,
44
45
  chunkRowBase, chunkRows, // row band offset (global first row) + row count for this chunk
@@ -56,9 +57,6 @@ export function buildGenerateKernel( params ) {
56
57
 
57
58
  const pixelCoord = vec2( float( gx ).add( 0.5 ), float( gy ).add( 0.5 ) );
58
59
 
59
- const screenPosition = pixelCoord.div( resolution ).mul( 2.0 ).sub( 1.0 ).toVar();
60
- screenPosition.y.assign( screenPosition.y.negate() );
61
-
62
60
  const baseSeed = getDecorrelatedSeed( { pixelCoord, rayIndex: int( 0 ), frame } ).toVar();
63
61
  const seed = pcgHash( { state: baseSeed } ).toVar();
64
62
 
@@ -68,13 +66,18 @@ export function buildGenerateKernel( params ) {
68
66
  // position from the first scatter direction (they were reading the identical cell).
69
67
  const stratifiedJitter = getStratifiedSample( pixelCoord, int( 1 ), int( 1 ), seed, resolution, frame ).toVar();
70
68
 
71
- const jitterScale = vec2( 2.0 ).div( resolution );
72
- const jitter = stratifiedJitter.sub( 0.5 ).mul( jitterScale );
73
- const jitteredScreenPosition = screenPosition.add( jitter );
69
+ // Y is subtracted because uv.y grows downward while the NDC this used to build grew upward —
70
+ // keeps the sub-pixel sample sequence bit-identical to the pre-panorama ray gen.
71
+ const jitterOffset = stratifiedJitter.sub( 0.5 );
72
+ const jitteredUV = vec2(
73
+ pixelCoord.x.add( jitterOffset.x ),
74
+ pixelCoord.y.sub( jitterOffset.y )
75
+ ).div( resolution );
74
76
 
75
77
  const ray = Ray.wrap( generateRayFromCamera(
76
- jitteredScreenPosition, seed,
78
+ jitteredUV, seed,
77
79
  cameraWorldMatrix, cameraProjectionMatrixInverse,
80
+ cameraProjection, panoLonRange, panoLatRange, panoLevelHorizon,
78
81
  enableDOF, focalLength, aperture, focusDistance, sceneScale, apertureScale, anamorphicRatio,
79
82
  ) );
80
83
 
@@ -83,6 +83,9 @@ export const LightSample = struct( {
83
83
  distance: 'float',
84
84
  lightType: 'int',
85
85
  valid: 'bool',
86
+ // Reservoir denominator, carried out so the BSDF-hit MIS partner can rebuild the
87
+ // selection pdf without walking every light buffer a second time.
88
+ selectionTotalWeight: 'float',
86
89
  } );
87
90
 
88
91
  export const IndirectLightingResult = struct( {
@@ -74,7 +74,7 @@ export const traceShadowRay = Fn( ( [
74
74
  const rayOrigin = origin.toVar();
75
75
  const remainingDist = float( maxDist ).toVar();
76
76
 
77
- const MAX_SHADOW_TRANSMISSIONS = 8;
77
+ const MAX_SHADOW_TRANSMISSIONS = 16;
78
78
 
79
79
  Loop( { start: int( 0 ), end: int( MAX_SHADOW_TRANSMISSIONS ) }, () => {
80
80
 
@@ -160,7 +160,7 @@ export const traceShadowRay = Fn( ( [
160
160
  const blendAlpha = clamp( shadowMaterial.color.a.mul( shadowMaterial.opacity ).mul( texAlpha ), 0.0, 1.0 );
161
161
  transmittance.mulAssign( float( 1.0 ).sub( blendAlpha ) );
162
162
 
163
- If( transmittance.lessThan( 0.005 ), () => {
163
+ If( transmittance.lessThan( 1e-4 ), () => {
164
164
 
165
165
  transmittance.assign( 0.0 );
166
166
  Break();
@@ -221,7 +221,7 @@ export const traceShadowRay = Fn( ( [
221
221
  transmittance.mulAssign( matTransmittance );
222
222
 
223
223
  // Early exit if almost no light passes through
224
- If( transmittance.lessThan( 0.005 ), () => {
224
+ If( transmittance.lessThan( 1e-4 ), () => {
225
225
 
226
226
  transmittance.assign( 0.0 );
227
227
  Break();
@@ -237,7 +237,7 @@ export const traceShadowRay = Fn( ( [
237
237
  // Handle transparent materials
238
238
  transmittance.mulAssign( float( 1.0 ).sub( shadowMaterial.opacity ) );
239
239
 
240
- If( transmittance.lessThan( 0.005 ), () => {
240
+ If( transmittance.lessThan( 1e-4 ), () => {
241
241
 
242
242
  transmittance.assign( 0.0 );
243
243
  Break();