rayzee 7.2.1 → 7.4.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 +2 -3
- package/dist/rayzee.es.js +1517 -1370
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +52 -38
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +2 -2
- package/src/EngineDefaults.js +15 -7
- package/src/PathTracerApp.js +24 -2
- package/src/Processor/CameraOptimizer.js +0 -5
- package/src/Processor/EmissiveTriangleBuilder.js +4 -3
- package/src/Processor/PackedRayBuffer.js +3 -4
- package/src/Processor/QueueManager.js +1 -0
- package/src/RenderSettings.js +16 -2
- package/src/Stages/PathTracer.js +34 -52
- package/src/Stages/PathTracerStage.js +0 -13
- package/src/TSL/Common.js +62 -0
- package/src/TSL/DebugKernel.js +2 -3
- package/src/TSL/Environment.js +22 -1
- package/src/TSL/FinalWriteKernel.js +28 -29
- package/src/TSL/GenerateKernel.js +11 -16
- package/src/TSL/LightsSampling.js +35 -14
- package/src/TSL/ShadeKernel.js +276 -51
- package/src/TSL/Struct.js +8 -0
- package/src/managers/DenoisingManager.js +7 -0
- package/src/managers/UniformManager.js +7 -2
- package/src/managers/VideoRenderManager.js +0 -2
|
@@ -34,12 +34,13 @@ export function buildFinalWriteKernel( params ) {
|
|
|
34
34
|
transparentBackground,
|
|
35
35
|
prevAccumTexture, prevNormalDepthTexture, prevAlbedoTexture,
|
|
36
36
|
renderWidth, renderHeight,
|
|
37
|
-
// Multi-sample: average S sample-slots per pixel (slot = pixel + k*w*h, w*h from the resolution uniform).
|
|
38
|
-
samplesPerPass = 1,
|
|
39
37
|
visMode,
|
|
38
|
+
// Aux MRT (normalDepth + albedo) feeds only the denoiser/OIDN. Gated by a live uniform (1 = denoiser
|
|
39
|
+
// on): when off, skip the G-buffer decode, the prev-frame aux mix, and the two aux stores.
|
|
40
|
+
auxGBufferEnabled,
|
|
40
41
|
} = params;
|
|
41
42
|
|
|
42
|
-
const
|
|
43
|
+
const auxOn = auxGBufferEnabled.greaterThan( uint( 0 ) );
|
|
43
44
|
|
|
44
45
|
const computeFn = Fn( () => {
|
|
45
46
|
|
|
@@ -51,31 +52,21 @@ export function buildFinalWriteKernel( params ) {
|
|
|
51
52
|
const pixelIndex = gy.mul( int( resolution.x ) ).add( gx );
|
|
52
53
|
const rayID = uint( pixelIndex );
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
if ( S <= 1 ) return readRayRadiance( rayBufferRO, rayID );
|
|
58
|
-
const acc = readRayRadiance( rayBufferRO, rayID ).toVar();
|
|
59
|
-
const mrps = uint( resolution.x ).mul( uint( resolution.y ) ).toVar(); // w*h from the resolution uniform, not baked
|
|
60
|
-
for ( let k = 1; k < S; k ++ ) {
|
|
61
|
-
|
|
62
|
-
acc.addAssign( readRayRadiance( rayBufferRO, rayID.add( uint( k ).mul( mrps ) ) ) );
|
|
63
|
-
|
|
64
|
-
}
|
|
55
|
+
const sampleColor = readRayRadiance( rayBufferRO, rayID );
|
|
56
|
+
const finalColor = sampleColor.xyz.toVar();
|
|
57
|
+
const outputAlpha = select( transparentBackground, sampleColor.w, float( 1.0 ) ).toVar();
|
|
65
58
|
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
// MRT comes from the per-pixel G-buffer (rayID == pixelIndex). Half-packed: decode.
|
|
60
|
+
// auxOn gates the decode + stores so a no-denoiser frame does no G-buffer read and no aux writes.
|
|
61
|
+
const finalNormalDepth = vec4( 0.0 ).toVar();
|
|
62
|
+
const finalAlbedo = vec4( 0.0 ).xyz.toVar();
|
|
63
|
+
If( auxOn, () => {
|
|
68
64
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const normalDepth = gbDecodeNormalDepth( gbuf );
|
|
73
|
-
const albedoID = vec4( gbDecodeAlbedo( gbuf ), 0.0 );
|
|
65
|
+
const gbuf = readGBuffer( gBufferRO, rayID );
|
|
66
|
+
finalNormalDepth.assign( gbDecodeNormalDepth( gbuf ) );
|
|
67
|
+
finalAlbedo.assign( vec4( gbDecodeAlbedo( gbuf ), 0.0 ).xyz );
|
|
74
68
|
|
|
75
|
-
|
|
76
|
-
const finalNormalDepth = normalDepth.toVar();
|
|
77
|
-
const finalAlbedo = albedoID.xyz.toVar();
|
|
78
|
-
const outputAlpha = select( transparentBackground, sampleColor.w, float( 1.0 ) ).toVar();
|
|
69
|
+
} );
|
|
79
70
|
|
|
80
71
|
const pixelCoord = vec2( float( gx ).add( 0.5 ), float( gy ).add( 0.5 ) );
|
|
81
72
|
const prevUV = pixelCoord.div( resolution );
|
|
@@ -87,8 +78,12 @@ export function buildFinalWriteKernel( params ) {
|
|
|
87
78
|
const prevAccumSample = texture( prevAccumTexture, prevUV, 0 ).toVar();
|
|
88
79
|
|
|
89
80
|
finalColor.assign( mix( prevAccumSample.xyz, sampleColor.xyz, accumulationAlpha ) );
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
If( auxOn, () => {
|
|
82
|
+
|
|
83
|
+
finalNormalDepth.assign( mix( texture( prevNormalDepthTexture, prevUV, 0 ), finalNormalDepth, accumulationAlpha ) );
|
|
84
|
+
finalAlbedo.assign( mix( texture( prevAlbedoTexture, prevUV, 0 ).xyz, finalAlbedo, accumulationAlpha ) );
|
|
85
|
+
|
|
86
|
+
} );
|
|
92
87
|
|
|
93
88
|
If( transparentBackground, () => {
|
|
94
89
|
|
|
@@ -107,8 +102,12 @@ export function buildFinalWriteKernel( params ) {
|
|
|
107
102
|
|
|
108
103
|
const uintCoord = uvec2( uint( gx ), uint( gy ) );
|
|
109
104
|
textureStore( writeColorTex, uintCoord, vec4( finalColor, outputAlpha ) ).toWriteOnly();
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
If( auxOn, () => {
|
|
106
|
+
|
|
107
|
+
textureStore( writeNDTex, uintCoord, finalNormalDepth ).toWriteOnly();
|
|
108
|
+
textureStore( writeAlbedoTex, uintCoord, vec4( finalAlbedo, 1.0 ) ).toWriteOnly();
|
|
109
|
+
|
|
110
|
+
} );
|
|
112
111
|
|
|
113
112
|
} );
|
|
114
113
|
|
|
@@ -33,39 +33,32 @@ export function buildGenerateKernel( params ) {
|
|
|
33
33
|
cameraWorldMatrix, cameraProjectionMatrixInverse,
|
|
34
34
|
enableDOF, focalLength, aperture, focusDistance, sceneScale, apertureScale, anamorphicRatio,
|
|
35
35
|
renderWidth, renderHeight,
|
|
36
|
-
// Multi-sample: S primary rays/pixel/frame; S>1 dispatch covers h*S rows, ray lands in slot subSample*(w*h) + pixelIndex.
|
|
37
|
-
samplesPerPass = 1,
|
|
38
36
|
transmissiveBounces, // per-ray refraction budget (megakernel parity: PathTracerCore.js:606)
|
|
39
37
|
transparentBackground, // alpha inits to 1 here (megakernel parity: PathTracerCore.js:554) — env-escape-without-opaque zeroes it in Shade
|
|
38
|
+
auxGBufferEnabled, // live uniform: 1 = init the per-pixel G-buffer (denoiser on), 0 = skip it
|
|
40
39
|
} = params;
|
|
41
40
|
|
|
42
|
-
const
|
|
41
|
+
const auxOn = auxGBufferEnabled.greaterThan( uint( 0 ) );
|
|
43
42
|
|
|
44
43
|
const computeFn = Fn( () => {
|
|
45
44
|
|
|
46
45
|
const gx = int( workgroupId.x ).mul( WG_SIZE ).add( int( localId.x ) );
|
|
47
|
-
const
|
|
46
|
+
const gy = int( workgroupId.y ).mul( WG_SIZE ).add( int( localId.y ) );
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
const gy = S > 1 ? gyRaw.sub( subSample.mul( renderHeight ) ).toVar() : gyRaw;
|
|
51
|
-
const yBound = S > 1 ? renderHeight.mul( int( S ) ) : renderHeight;
|
|
52
|
-
|
|
53
|
-
If( gx.lessThan( renderWidth ).and( gyRaw.lessThan( yBound ) ), () => {
|
|
48
|
+
If( gx.lessThan( renderWidth ).and( gy.lessThan( renderHeight ) ), () => {
|
|
54
49
|
|
|
55
50
|
const pixelCoord = vec2( float( gx ).add( 0.5 ), float( gy ).add( 0.5 ) );
|
|
56
51
|
const pixelIndex = gy.mul( int( resolution.x ) ).add( gx );
|
|
57
|
-
//
|
|
58
|
-
const rayID =
|
|
59
|
-
? uint( pixelIndex ).add( uint( subSample ).mul( uint( resolution.x ).mul( uint( resolution.y ) ) ) )
|
|
60
|
-
: uint( pixelIndex );
|
|
52
|
+
// One ray per pixel: rayID is the pixel index.
|
|
53
|
+
const rayID = uint( pixelIndex );
|
|
61
54
|
|
|
62
55
|
const screenPosition = pixelCoord.div( resolution ).mul( 2.0 ).sub( 1.0 ).toVar();
|
|
63
56
|
screenPosition.y.assign( screenPosition.y.negate() );
|
|
64
57
|
|
|
65
|
-
const baseSeed = getDecorrelatedSeed( { pixelCoord, rayIndex:
|
|
58
|
+
const baseSeed = getDecorrelatedSeed( { pixelCoord, rayIndex: int( 0 ), frame } ).toVar();
|
|
66
59
|
const seed = pcgHash( { state: baseSeed } ).toVar();
|
|
67
60
|
|
|
68
|
-
const stratifiedJitter = getStratifiedSample( pixelCoord,
|
|
61
|
+
const stratifiedJitter = getStratifiedSample( pixelCoord, int( 0 ), int( 1 ), seed, resolution, frame ).toVar();
|
|
69
62
|
|
|
70
63
|
const jitterScale = vec2( 2.0 ).div( resolution );
|
|
71
64
|
const jitter = stratifiedJitter.sub( 0.5 ).mul( jitterScale );
|
|
@@ -78,6 +71,8 @@ export function buildGenerateKernel( params ) {
|
|
|
78
71
|
) );
|
|
79
72
|
|
|
80
73
|
writeRayOriginMeta( rayBufferRW, rayID, ray.origin, int( 0 ), int( 0 ) );
|
|
74
|
+
// A fresh camera ray is NOT redirected — REDIRECTED stays clear so it sees the direct backdrop;
|
|
75
|
+
// ShadeKernel sets REDIRECTED on the first direction-changing interaction (see RAY_FLAG.REDIRECTED).
|
|
81
76
|
writeRayDirFlags( rayBufferRW, rayID, ray.direction, uint( RAY_FLAG.ACTIVE ) );
|
|
82
77
|
// pdf inits to 0 = prevBouncePdf (megakernel parity PathTracerCore.js:556). The bounce>0 env/emissive
|
|
83
78
|
// MIS gate skips until an opaque scatter writes a real combinedPdf; free bounces preserve it.
|
|
@@ -87,7 +82,7 @@ export function buildGenerateKernel( params ) {
|
|
|
87
82
|
// keeps alpha 1 → solid. Non-transparent mode is inert (FinalWrite forces alpha 1).
|
|
88
83
|
writeRayRadiance( rayBufferRW, rayID, vec4( vec3( 0.0 ), select( transparentBackground, float( 1.0 ), float( 0.0 ) ) ) );
|
|
89
84
|
|
|
90
|
-
If(
|
|
85
|
+
If( auxOn, () => {
|
|
91
86
|
|
|
92
87
|
// default: normal +Z, depth 1 (far), black albedo (background/miss)
|
|
93
88
|
writeGBuffer( gBufferRW, uint( pixelIndex ), vec3( 0.0, 0.0, 1.0 ), float( 1.0 ), vec3( 0.0 ) );
|
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
sampleIESProfile,
|
|
59
59
|
} from './LightsCore.js';
|
|
60
60
|
|
|
61
|
-
import { MISStrategy, DotProducts } from './Struct.js';
|
|
61
|
+
import { MISStrategy, DotProducts, DirectLightingDual } from './Struct.js';
|
|
62
62
|
import {
|
|
63
63
|
calculateDirectionalLightImportance,
|
|
64
64
|
estimateLightImportance,
|
|
@@ -707,9 +707,14 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
707
707
|
envCDFTexture,
|
|
708
708
|
envTotalSum, envCompensationDelta, envResolution,
|
|
709
709
|
enableEnvironmentLight,
|
|
710
|
+
// Shadow catcher: when true, also accumulate the unoccluded (visibility=1) reference
|
|
711
|
+
// at every light/env site so the caller can form a shadow ratio. Dead path otherwise.
|
|
712
|
+
wantUnoccluded,
|
|
710
713
|
] ) => {
|
|
711
714
|
|
|
712
715
|
const totalContribution = vec3( 0.0 ).toVar();
|
|
716
|
+
// Unoccluded reference (visibility forced to 1) — only filled when wantUnoccluded.
|
|
717
|
+
const unoccludedContribution = vec3( 0.0 ).toVar();
|
|
713
718
|
const rayOrigin = hitPoint.add( hitNormal.mul( 0.001 ) ).toVar();
|
|
714
719
|
|
|
715
720
|
// Binds BVH params so shadow-ray sites at varying call depths use a 3-arg call
|
|
@@ -819,7 +824,7 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
819
824
|
const shadowDistance = min( lightSample.distance.sub( 0.001 ), float( 1000.0 ) );
|
|
820
825
|
const visibility = shadow( rayOrigin, lightSample.direction, shadowDistance );
|
|
821
826
|
|
|
822
|
-
If( visibility.greaterThan( 0.0 ), () => {
|
|
827
|
+
If( visibility.greaterThan( 0.0 ).or( wantUnoccluded ), () => {
|
|
823
828
|
|
|
824
829
|
// Share H + dot products between BRDF eval and PDF — otherwise each
|
|
825
830
|
// would recompute normalize(V+L) + 5 dot products independently.
|
|
@@ -846,9 +851,15 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
846
851
|
|
|
847
852
|
} );
|
|
848
853
|
|
|
849
|
-
//
|
|
850
|
-
|
|
851
|
-
|
|
854
|
+
// Base contribution WITHOUT visibility; shadowed = base × visibility (identical to the
|
|
855
|
+
// pre-dual-sum math), unoccluded = base × 1 (shadow-catcher reference only).
|
|
856
|
+
const baseContribution = lightSample.emission.mul( brdfValue ).mul( NoL ).mul( misW ).div( max( lightSample.pdf, 1e-10 ) ).mul( totalSamplingWeight ).div( max( lightWeight, 1e-10 ) );
|
|
857
|
+
totalContribution.addAssign( baseContribution.mul( visibility ) );
|
|
858
|
+
If( wantUnoccluded, () => {
|
|
859
|
+
|
|
860
|
+
unoccludedContribution.addAssign( baseContribution );
|
|
861
|
+
|
|
862
|
+
} );
|
|
852
863
|
|
|
853
864
|
} );
|
|
854
865
|
|
|
@@ -918,7 +929,7 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
918
929
|
const shadowDistance = min( hitDistance.sub( 0.001 ), float( 1000.0 ) );
|
|
919
930
|
const visibility = shadow( rayOrigin, brdfSampleDirection, shadowDistance );
|
|
920
931
|
|
|
921
|
-
If( visibility.greaterThan( 0.0 ), () => {
|
|
932
|
+
If( visibility.greaterThan( 0.0 ).or( wantUnoccluded ), () => {
|
|
922
933
|
|
|
923
934
|
const lightFacing = max( float( 0.0 ), dot( brdfSampleDirection, light.normal ).negate() ).toVar();
|
|
924
935
|
|
|
@@ -934,9 +945,14 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
934
945
|
const misW = powerHeuristic( { pdf1: brdfPdfWeighted, pdf2: lightPdfWeighted } ).toVar();
|
|
935
946
|
|
|
936
947
|
const lightEmission = light.color.mul( light.intensity );
|
|
937
|
-
//
|
|
938
|
-
const
|
|
939
|
-
totalContribution.addAssign(
|
|
948
|
+
// Base contribution WITHOUT visibility (see discrete-light site).
|
|
949
|
+
const baseContribution = lightEmission.mul( brdfSampleValue ).mul( NoL ).mul( misW ).div( max( brdfSamplePdf, 1e-10 ) ).mul( totalSamplingWeight ).div( max( brdfWeight, 1e-10 ) );
|
|
950
|
+
totalContribution.addAssign( baseContribution.mul( visibility ) );
|
|
951
|
+
If( wantUnoccluded, () => {
|
|
952
|
+
|
|
953
|
+
unoccludedContribution.addAssign( baseContribution );
|
|
954
|
+
|
|
955
|
+
} );
|
|
940
956
|
|
|
941
957
|
} );
|
|
942
958
|
|
|
@@ -984,7 +1000,7 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
984
1000
|
|
|
985
1001
|
const visibility = shadow( rayOrigin, envDirection, float( 1000.0 ) );
|
|
986
1002
|
|
|
987
|
-
If( visibility.greaterThan( 0.0 ), () => {
|
|
1003
|
+
If( visibility.greaterThan( 0.0 ).or( wantUnoccluded ), () => {
|
|
988
1004
|
|
|
989
1005
|
// Share H + dots between env BRDF/PDF — same redundancy fix as the
|
|
990
1006
|
// discrete-light path above.
|
|
@@ -1000,9 +1016,14 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
1000
1016
|
float( 1.0 )
|
|
1001
1017
|
).toVar();
|
|
1002
1018
|
|
|
1003
|
-
//
|
|
1004
|
-
const
|
|
1005
|
-
totalContribution.addAssign(
|
|
1019
|
+
// Base contribution WITHOUT visibility (deterministic estimator; no stochastic scaling).
|
|
1020
|
+
const baseContribution = envColor.mul( brdfValue ).mul( NoL ).mul( misW ).div( max( envPdf, 1e-10 ) );
|
|
1021
|
+
totalContribution.addAssign( baseContribution.mul( visibility ) );
|
|
1022
|
+
If( wantUnoccluded, () => {
|
|
1023
|
+
|
|
1024
|
+
unoccludedContribution.addAssign( baseContribution );
|
|
1025
|
+
|
|
1026
|
+
} );
|
|
1006
1027
|
|
|
1007
1028
|
} );
|
|
1008
1029
|
|
|
@@ -1018,6 +1039,6 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
1018
1039
|
// NOTE: Emissive triangle sampling is handled separately in pathtracer_core.fs
|
|
1019
1040
|
// to bypass firefly suppression. Do not add it here to avoid double-counting.
|
|
1020
1041
|
|
|
1021
|
-
return totalContribution;
|
|
1042
|
+
return DirectLightingDual( { shadowed: totalContribution, unoccluded: unoccludedContribution } );
|
|
1022
1043
|
|
|
1023
1044
|
} );
|