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
package/src/TSL/Common.js
CHANGED
|
@@ -204,7 +204,7 @@ export const applySoftSuppressionRGB = wgslFn( `
|
|
|
204
204
|
export const classifyMaterial = Fn( ( [ metalness, roughness, transmission, clearcoat, emissive ] ) => {
|
|
205
205
|
|
|
206
206
|
const isMetallic = metalness.greaterThan( 0.7 ).toVar();
|
|
207
|
-
const isRough = roughness.greaterThan( 0.8 )
|
|
207
|
+
const isRough = roughness.greaterThan( 0.8 );
|
|
208
208
|
const isSmooth = roughness.lessThan( 0.3 ).toVar();
|
|
209
209
|
const isTransmissive = transmission.greaterThan( 0.5 ).toVar();
|
|
210
210
|
const hasClearcoat = clearcoat.greaterThan( 0.5 ).toVar();
|
|
@@ -218,8 +218,7 @@ export const classifyMaterial = Fn( ( [ metalness, roughness, transmission, clea
|
|
|
218
218
|
.add( float( 0.25 ).mul( float( isSmooth ) ) )
|
|
219
219
|
.add( float( 0.45 ).mul( float( isTransmissive ) ) )
|
|
220
220
|
.add( float( 0.35 ).mul( float( hasClearcoat ) ) )
|
|
221
|
-
.add( float( 0.3 ).mul( float( isEmissive ) ) )
|
|
222
|
-
.toVar();
|
|
221
|
+
.add( float( 0.3 ).mul( float( isEmissive ) ) );
|
|
223
222
|
|
|
224
223
|
// Add material interaction complexity
|
|
225
224
|
const interactionComplexity = float( 0.0 ).toVar();
|
|
@@ -255,7 +254,7 @@ export const selectOptimalMISStrategy = Fn( ( [ roughness, metalness, bounceInde
|
|
|
255
254
|
const brdfWeight = float( 0.5 ).toVar();
|
|
256
255
|
const lightWeight = float( 0.5 ).toVar();
|
|
257
256
|
const useBRDFSampling = tslBool( true );
|
|
258
|
-
const useLightSampling = throughputStrength.greaterThan( 0.01 )
|
|
257
|
+
const useLightSampling = throughputStrength.greaterThan( 0.01 );
|
|
259
258
|
|
|
260
259
|
If( roughness.lessThan( 0.1 ).and( metalness.greaterThan( 0.8 ) ), () => {
|
|
261
260
|
|
package/src/TSL/Debugger.js
CHANGED
|
@@ -124,15 +124,15 @@ export const TraceDebugMode = Fn( ( [
|
|
|
124
124
|
} ).toVar();
|
|
125
125
|
|
|
126
126
|
const envLuminance = dot( envSample.xyz, REC709_LUMINANCE_COEFFICIENTS ).toVar();
|
|
127
|
-
const rawLuminance = envLuminance
|
|
127
|
+
const rawLuminance = envLuminance;
|
|
128
128
|
|
|
129
129
|
// Adaptive scaling
|
|
130
130
|
const adaptiveScale = max( debugVisScale.mul( 0.1 ), 0.001 );
|
|
131
|
-
const scaledLuminance = envLuminance.div( adaptiveScale )
|
|
131
|
+
const scaledLuminance = envLuminance.div( adaptiveScale );
|
|
132
132
|
|
|
133
133
|
// Logarithmic scaling for better dynamic range
|
|
134
134
|
const logLuminance = log( envLuminance.add( 1e-6 ) );
|
|
135
|
-
const logScaled = logLuminance.add( 10.0 ).div( 10.0 )
|
|
135
|
+
const logScaled = logLuminance.add( 10.0 ).div( 10.0 );
|
|
136
136
|
|
|
137
137
|
// Choose scaling based on debugVisScale
|
|
138
138
|
const finalValue = select( debugVisScale.greaterThan( 1.0 ), scaledLuminance, logScaled ).toVar();
|
|
@@ -327,7 +327,7 @@ export const TraceDebugMode = Fn( ( [
|
|
|
327
327
|
material, hitInfo.uv, hitInfo.normal,
|
|
328
328
|
) ).toVar();
|
|
329
329
|
|
|
330
|
-
const albedoA = matSamples.albedo.rgb
|
|
330
|
+
const albedoA = matSamples.albedo.rgb;
|
|
331
331
|
const normalA = normalize( matSamples.normal ).toVar();
|
|
332
332
|
|
|
333
333
|
// Generate per-pixel per-frame random seed for stochastic bounce direction
|
|
@@ -339,11 +339,11 @@ export const TraceDebugMode = Fn( ( [
|
|
|
339
339
|
// Cosine-weighted hemisphere sample around the surface normal
|
|
340
340
|
const xi_r1 = RandomValue( rngState ).toVar();
|
|
341
341
|
const xi_r2 = RandomValue( rngState ).toVar();
|
|
342
|
-
const xi = vec2( xi_r1, xi_r2 )
|
|
342
|
+
const xi = vec2( xi_r1, xi_r2 );
|
|
343
343
|
const bounceDir = cosineWeightedSample( { N: normalA, xi } ).toVar();
|
|
344
344
|
|
|
345
345
|
// Trace secondary ray from the hit point (offset along normal to avoid self-intersection)
|
|
346
|
-
const bounceOrigin = hitInfo.hitPoint.add( normalA.mul( 0.001 ) )
|
|
346
|
+
const bounceOrigin = hitInfo.hitPoint.add( normalA.mul( 0.001 ) );
|
|
347
347
|
const bounceRay = Ray( { origin: bounceOrigin, direction: bounceDir } );
|
|
348
348
|
|
|
349
349
|
const bounceHit = HitInfo.wrap( traverseBVH(
|
package/src/TSL/Displacement.js
CHANGED
|
@@ -45,15 +45,15 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
45
45
|
const triIdx = hitInfo.triangleIndex;
|
|
46
46
|
|
|
47
47
|
const pA = getDatafromStorageBuffer( triangleBuffer, triIdx, int( 0 ), int( TRI_STRIDE ) ).xyz.toVar();
|
|
48
|
-
const pB = getDatafromStorageBuffer( triangleBuffer, triIdx, int( 1 ), int( TRI_STRIDE ) ).xyz
|
|
49
|
-
const pC = getDatafromStorageBuffer( triangleBuffer, triIdx, int( 2 ), int( TRI_STRIDE ) ).xyz
|
|
48
|
+
const pB = getDatafromStorageBuffer( triangleBuffer, triIdx, int( 1 ), int( TRI_STRIDE ) ).xyz;
|
|
49
|
+
const pC = getDatafromStorageBuffer( triangleBuffer, triIdx, int( 2 ), int( TRI_STRIDE ) ).xyz;
|
|
50
50
|
|
|
51
51
|
const uvData1 = getDatafromStorageBuffer( triangleBuffer, triIdx, int( 6 ), int( TRI_STRIDE ) ).toVar();
|
|
52
|
-
const uvData2 = getDatafromStorageBuffer( triangleBuffer, triIdx, int( 7 ), int( TRI_STRIDE ) )
|
|
52
|
+
const uvData2 = getDatafromStorageBuffer( triangleBuffer, triIdx, int( 7 ), int( TRI_STRIDE ) );
|
|
53
53
|
|
|
54
54
|
const uvA = uvData1.xy.toVar();
|
|
55
|
-
const uvB = uvData1.zw
|
|
56
|
-
const uvC = uvData2.xy
|
|
55
|
+
const uvB = uvData1.zw;
|
|
56
|
+
const uvC = uvData2.xy;
|
|
57
57
|
|
|
58
58
|
// Compute tangent vectors from triangle edges + UV differences
|
|
59
59
|
const edge1 = pB.sub( pA ).toVar();
|
|
@@ -94,8 +94,8 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
94
94
|
const rayProjDotT = dot( rayProj, T );
|
|
95
95
|
const rayProjDotB = dot( rayProj, B );
|
|
96
96
|
|
|
97
|
-
const du_dt = BdotB.mul( rayProjDotT ).sub( TdotB.mul( rayProjDotB ) ).mul( invDetJ )
|
|
98
|
-
const dv_dt = TdotT.mul( rayProjDotB ).sub( TdotB.mul( rayProjDotT ) ).mul( invDetJ )
|
|
97
|
+
const du_dt = BdotB.mul( rayProjDotT ).sub( TdotB.mul( rayProjDotB ) ).mul( invDetJ );
|
|
98
|
+
const dv_dt = TdotT.mul( rayProjDotB ).sub( TdotB.mul( rayProjDotT ) ).mul( invDetJ );
|
|
99
99
|
const dUV_dt = vec2( du_dt, dv_dt ).toVar();
|
|
100
100
|
|
|
101
101
|
// Ray height change per unit dt: how fast the ray moves along the surface normal
|
|
@@ -103,7 +103,7 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
103
103
|
|
|
104
104
|
// March range: displacement shell extends ±0.5*scale from base surface
|
|
105
105
|
// Compute dt range to traverse the full shell
|
|
106
|
-
const absNdotD = max( abs( dh_ray_dt ), 0.001 )
|
|
106
|
+
const absNdotD = max( abs( dh_ray_dt ), 0.001 );
|
|
107
107
|
const dtShell = scale.div( absNdotD ).toVar();
|
|
108
108
|
|
|
109
109
|
// Adaptive step count: full steps on primary ray, half on deeper bounces
|
|
@@ -113,7 +113,7 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
113
113
|
|
|
114
114
|
// Start from above the shell, march through
|
|
115
115
|
const dtStart = dtShell.negate().toVar();
|
|
116
|
-
const dtEnd = dtShell
|
|
116
|
+
const dtEnd = dtShell;
|
|
117
117
|
const dtStep = dtEnd.sub( dtStart ).div( float( marchSteps ) ).toVar();
|
|
118
118
|
|
|
119
119
|
// Track for binary refinement
|
|
@@ -129,10 +129,10 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
129
129
|
const dt = dtStart.add( dtStep.mul( float( i ) ) ).toVar();
|
|
130
130
|
|
|
131
131
|
// UV at this point along the ray
|
|
132
|
-
const marchUV = hitInfo.uv.add( dUV_dt.mul( dt ) )
|
|
132
|
+
const marchUV = hitInfo.uv.add( dUV_dt.mul( dt ) );
|
|
133
133
|
|
|
134
134
|
// Ray height above base surface at this dt
|
|
135
|
-
const rayHeight = dt.mul( dh_ray_dt )
|
|
135
|
+
const rayHeight = dt.mul( dh_ray_dt );
|
|
136
136
|
|
|
137
137
|
// Displaced surface height
|
|
138
138
|
const heightSample = sampleDisplacementMap(
|
|
@@ -165,7 +165,7 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
165
165
|
Loop( { start: int( 0 ), end: int( BINARY_STEPS ), type: 'int', condition: '<' }, () => {
|
|
166
166
|
|
|
167
167
|
const midDt = loDt.add( hiDt ).mul( 0.5 ).toVar();
|
|
168
|
-
const midUV = hitInfo.uv.add( dUV_dt.mul( midDt ) )
|
|
168
|
+
const midUV = hitInfo.uv.add( dUV_dt.mul( midDt ) );
|
|
169
169
|
const midRayHeight = midDt.mul( dh_ray_dt );
|
|
170
170
|
const midSample = sampleDisplacementMap(
|
|
171
171
|
displacementMaps, material.displacementMapIndex, midUV, material.displacementTransform,
|
|
@@ -187,7 +187,7 @@ export const refineDisplacedIntersection = Fn( ( [
|
|
|
187
187
|
// Final intersection
|
|
188
188
|
const finalDt = loDt.add( hiDt ).mul( 0.5 ).toVar();
|
|
189
189
|
const finalUV = hitInfo.uv.add( dUV_dt.mul( finalDt ) ).toVar();
|
|
190
|
-
const finalPoint = hitInfo.hitPoint.add( rayDir.mul( finalDt ) )
|
|
190
|
+
const finalPoint = hitInfo.hitPoint.add( rayDir.mul( finalDt ) );
|
|
191
191
|
|
|
192
192
|
const finalHeight = sampleDisplacementMap(
|
|
193
193
|
displacementMaps, material.displacementMapIndex, finalUV, material.displacementTransform,
|
package/src/TSL/Environment.js
CHANGED
|
@@ -47,19 +47,19 @@ export const sampleEquirect = Fn( ( [ environment, direction, environmentMatrix,
|
|
|
47
47
|
|
|
48
48
|
// sin(theta) matches the CDF's solid-angle weighting (lum * sinTheta)
|
|
49
49
|
const sinTheta = sin( uv.y.mul( Math.PI ) ).toVar();
|
|
50
|
-
const lum = dot( color, REC709_LUMINANCE_COEFFICIENTS )
|
|
51
|
-
const weightedLum = lum.mul( sinTheta )
|
|
50
|
+
const lum = dot( color, REC709_LUMINANCE_COEFFICIENTS );
|
|
51
|
+
const weightedLum = lum.mul( sinTheta );
|
|
52
52
|
// MIS Compensation: subtract delta to match the sharpened CDF
|
|
53
|
-
const compensatedWeight = max( float( 0.0 ), weightedLum.sub( envCompensationDelta ) )
|
|
54
|
-
const pdf = compensatedWeight.div( envTotalSum )
|
|
53
|
+
const compensatedWeight = max( float( 0.0 ), weightedLum.sub( envCompensationDelta ) );
|
|
54
|
+
const pdf = compensatedWeight.div( envTotalSum );
|
|
55
55
|
|
|
56
56
|
// Inline equirectDirectionPdf using the uv + sinTheta already in scope —
|
|
57
57
|
// the helper would otherwise re-derive uv via atan2+acos and recompute sin.
|
|
58
58
|
const dirPdf = sinTheta.greaterThan( 0.0 ).select(
|
|
59
59
|
float( 1.0 ).div( float( 2.0 * Math.PI * Math.PI ).mul( sinTheta ) ),
|
|
60
60
|
float( 0.0 )
|
|
61
|
-
)
|
|
62
|
-
const finalPdf = float( envResolution.x ).mul( float( envResolution.y ) ).mul( pdf ).mul( dirPdf )
|
|
61
|
+
);
|
|
62
|
+
const finalPdf = float( envResolution.x ).mul( float( envResolution.y ) ).mul( pdf ).mul( dirPdf );
|
|
63
63
|
|
|
64
64
|
result.assign( vec4( color, finalPdf ) );
|
|
65
65
|
|
|
@@ -127,18 +127,18 @@ export const sampleEquirectProbability = Fn( ( [
|
|
|
127
127
|
|
|
128
128
|
// Calculate PDF — sin(theta) weighting + MIS Compensation (Karlík et al. 2019)
|
|
129
129
|
const sinTheta = sin( uv.y.mul( Math.PI ) ).toVar();
|
|
130
|
-
const lum = dot( color.div( environmentIntensity ), REC709_LUMINANCE_COEFFICIENTS )
|
|
131
|
-
const weightedLum = lum.mul( sinTheta )
|
|
132
|
-
const compensatedWeight = max( float( 0.0 ), weightedLum.sub( envCompensationDelta ) )
|
|
133
|
-
const pdf = compensatedWeight.div( envTotalSum )
|
|
130
|
+
const lum = dot( color.div( environmentIntensity ), REC709_LUMINANCE_COEFFICIENTS );
|
|
131
|
+
const weightedLum = lum.mul( sinTheta );
|
|
132
|
+
const compensatedWeight = max( float( 0.0 ), weightedLum.sub( envCompensationDelta ) );
|
|
133
|
+
const pdf = compensatedWeight.div( envTotalSum );
|
|
134
134
|
|
|
135
135
|
// Inline equirectDirectionPdf — uv + sinTheta are already in scope, so we
|
|
136
136
|
// skip the helper's redundant uv-from-direction + sin recompute.
|
|
137
137
|
const dirPdf = sinTheta.greaterThan( 0.0 ).select(
|
|
138
138
|
float( 1.0 ).div( float( 2.0 * Math.PI * Math.PI ).mul( sinTheta ) ),
|
|
139
139
|
float( 0.0 )
|
|
140
|
-
)
|
|
141
|
-
const finalPdf = float( envResolution.x ).mul( float( envResolution.y ) ).mul( pdf ).mul( dirPdf )
|
|
140
|
+
);
|
|
141
|
+
const finalPdf = float( envResolution.x ).mul( float( envResolution.y ) ).mul( pdf ).mul( dirPdf );
|
|
142
142
|
|
|
143
143
|
return vec4( direction, finalPdf );
|
|
144
144
|
|
package/src/TSL/LightsCore.js
CHANGED
|
@@ -291,18 +291,17 @@ export const sampleSpotGoboMask = /*@__PURE__*/ Fn( ( [ light, lightDir ] ) => {
|
|
|
291
291
|
vec3( 1.0, 0.0, 0.0 ),
|
|
292
292
|
);
|
|
293
293
|
const T = normalize( cross( up, forward ) ).toVar();
|
|
294
|
-
const B = cross( forward, T )
|
|
294
|
+
const B = cross( forward, T );
|
|
295
295
|
|
|
296
296
|
// Project onto plane perpendicular to forward at distance 1
|
|
297
297
|
const invCos = float( 1.0 ).div( cosAlpha ).toVar();
|
|
298
|
-
const px = dot( toSurface, T ).mul( invCos )
|
|
299
|
-
const py = dot( toSurface, B ).mul( invCos )
|
|
298
|
+
const px = dot( toSurface, T ).mul( invCos );
|
|
299
|
+
const py = dot( toSurface, B ).mul( invCos );
|
|
300
300
|
|
|
301
301
|
// Cone edge → ±tan(angle); map to UV [0,1]
|
|
302
|
-
const
|
|
303
|
-
const
|
|
304
|
-
const
|
|
305
|
-
const v = clamp( py.mul( invTan ).add( 0.5 ), float( 0.0 ), float( 1.0 ) ).toVar();
|
|
302
|
+
const invTan = float( 0.5 ).div( max( tan( light.angle ), float( 1e-4 ) ) ).toVar();
|
|
303
|
+
const u = clamp( px.mul( invTan ).add( 0.5 ), float( 0.0 ), float( 1.0 ) );
|
|
304
|
+
const v = clamp( py.mul( invTan ).add( 0.5 ), float( 0.0 ), float( 1.0 ) );
|
|
306
305
|
|
|
307
306
|
if ( _goboMapsTexNode ) {
|
|
308
307
|
|
|
@@ -354,15 +353,15 @@ export const sampleDirectionalGoboMask = /*@__PURE__*/ Fn( ( [ light, surfacePoi
|
|
|
354
353
|
vec3( 1.0, 0.0, 0.0 ),
|
|
355
354
|
);
|
|
356
355
|
const T = normalize( cross( up, axis ) ).toVar();
|
|
357
|
-
const B = cross( axis, T )
|
|
356
|
+
const B = cross( axis, T );
|
|
358
357
|
|
|
359
358
|
const invScale = float( 1.0 ).div( max( light.goboScale, float( 1e-4 ) ) ).toVar();
|
|
360
359
|
const u = dot( surfacePoint, T ).mul( invScale ).add( 0.5 ).toVar();
|
|
361
360
|
const v = dot( surfacePoint, B ).mul( invScale ).add( 0.5 ).toVar();
|
|
362
361
|
|
|
363
362
|
// Tile by fract so a single mask can cover any scene size.
|
|
364
|
-
const uTiled = u.sub( u.floor() )
|
|
365
|
-
const vTiled = v.sub( v.floor() )
|
|
363
|
+
const uTiled = u.sub( u.floor() );
|
|
364
|
+
const vTiled = v.sub( v.floor() );
|
|
366
365
|
|
|
367
366
|
if ( _goboMapsTexNode ) {
|
|
368
367
|
|
|
@@ -417,9 +416,9 @@ export const sampleIESProfile = /*@__PURE__*/ Fn( ( [ light, lightDir ] ) => {
|
|
|
417
416
|
|
|
418
417
|
// Vertical angle: between forward axis and emission direction. 0 = on axis (V=0),
|
|
419
418
|
// PI = anti-axis (V=1).
|
|
420
|
-
const cosV = clamp( dot( toSurface, forward ), float( - 1.0 ), float( 1.0 ) )
|
|
421
|
-
const vAngle = acos( cosV )
|
|
422
|
-
const v = clamp( vAngle.div( float( Math.PI ) ), float( 0.0 ), float( 1.0 ) )
|
|
419
|
+
const cosV = clamp( dot( toSurface, forward ), float( - 1.0 ), float( 1.0 ) );
|
|
420
|
+
const vAngle = acos( cosV );
|
|
421
|
+
const v = clamp( vAngle.div( float( Math.PI ) ), float( 0.0 ), float( 1.0 ) );
|
|
423
422
|
|
|
424
423
|
// Horizontal angle: project emission direction onto plane perpendicular to forward.
|
|
425
424
|
const up = select(
|
|
@@ -428,13 +427,13 @@ export const sampleIESProfile = /*@__PURE__*/ Fn( ( [ light, lightDir ] ) => {
|
|
|
428
427
|
vec3( 1.0, 0.0, 0.0 ),
|
|
429
428
|
);
|
|
430
429
|
const T = normalize( cross( up, forward ) ).toVar();
|
|
431
|
-
const B = cross( forward, T )
|
|
430
|
+
const B = cross( forward, T );
|
|
432
431
|
|
|
433
432
|
const px = dot( toSurface, T );
|
|
434
433
|
const py = dot( toSurface, B );
|
|
435
434
|
// atan2 → [-PI, PI]; remap to [0, 2PI] then to [0, 1].
|
|
436
435
|
const phi = atan( py, px );
|
|
437
|
-
const u = phi.div( float( 2.0 * Math.PI ) ).add( 0.5 )
|
|
436
|
+
const u = phi.div( float( 2.0 * Math.PI ) ).add( 0.5 );
|
|
438
437
|
|
|
439
438
|
if ( _iesProfilesTexNode ) {
|
|
440
439
|
|
package/src/TSL/LightsDirect.js
CHANGED
|
@@ -268,11 +268,8 @@ export const traceShadowRay = Fn( ( [
|
|
|
268
268
|
|
|
269
269
|
export const calculateRayOffset = Fn( ( [ hitPoint, normal, material ] ) => {
|
|
270
270
|
|
|
271
|
-
// Base epsilon scaled by scene size
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
// Adjust for material properties
|
|
275
|
-
const materialEpsilon = scaleEpsilon.toVar();
|
|
271
|
+
// Base epsilon scaled by scene size; adjusted by material properties below.
|
|
272
|
+
const materialEpsilon = max( float( 1e-4 ), length( hitPoint ).mul( 1e-6 ) ).toVar();
|
|
276
273
|
|
|
277
274
|
If( material.transmission.greaterThan( 0.0 ), () => {
|
|
278
275
|
|
|
@@ -71,17 +71,17 @@ export const calculateTransmissionPDF = Fn( ( [ V, L, N, ior, roughness, enterin
|
|
|
71
71
|
|
|
72
72
|
} );
|
|
73
73
|
|
|
74
|
-
const VoH = abs( dot( V, H ) )
|
|
74
|
+
const VoH = abs( dot( V, H ) );
|
|
75
75
|
const LoH = abs( dot( L, H ) ).toVar();
|
|
76
76
|
const NoH = abs( dot( N, H ) ).toVar();
|
|
77
77
|
|
|
78
78
|
// GGX distribution
|
|
79
|
-
const D = DistributionGGX( NoH, roughness )
|
|
79
|
+
const D = DistributionGGX( NoH, roughness );
|
|
80
80
|
|
|
81
81
|
// Jacobian for transmission
|
|
82
82
|
const denom_inner = VoH.add( LoH.mul( eta ) ).toVar();
|
|
83
|
-
const denom = denom_inner.mul( denom_inner )
|
|
84
|
-
const jacobian = LoH.mul( eta ).mul( eta ).div( max( denom, EPSILON ) )
|
|
83
|
+
const denom = denom_inner.mul( denom_inner );
|
|
84
|
+
const jacobian = LoH.mul( eta ).mul( eta ).div( max( denom, EPSILON ) );
|
|
85
85
|
|
|
86
86
|
return D.mul( NoH ).mul( jacobian );
|
|
87
87
|
|
|
@@ -92,10 +92,10 @@ export const calculateClearcoatPDF = Fn( ( [ V, L, N, clearcoatRoughness ] ) =>
|
|
|
92
92
|
|
|
93
93
|
const H_raw = V.add( L ).toVar();
|
|
94
94
|
const lenSq = dot( H_raw, H_raw ).toVar();
|
|
95
|
-
const H = select( lenSq.greaterThan( EPSILON ), H_raw.div( sqrt( lenSq ) ), N )
|
|
95
|
+
const H = select( lenSq.greaterThan( EPSILON ), H_raw.div( sqrt( lenSq ) ), N );
|
|
96
96
|
|
|
97
|
-
const NoH = max( dot( N, H ), 0.0 )
|
|
98
|
-
const NoV = max( dot( N, V ), 0.0 )
|
|
97
|
+
const NoH = max( dot( N, H ), 0.0 );
|
|
98
|
+
const NoV = max( dot( N, V ), 0.0 );
|
|
99
99
|
|
|
100
100
|
return calculateVNDFPDF( NoH, NoV, clearcoatRoughness );
|
|
101
101
|
|
|
@@ -259,15 +259,14 @@ export const calculateIndirectLighting = Fn( ( [
|
|
|
259
259
|
const validInput = samplingInfo.diffuseImportance.greaterThanEqual( 0.0 )
|
|
260
260
|
.and( samplingInfo.specularImportance.greaterThanEqual( 0.0 ) )
|
|
261
261
|
.and( samplingInfo.transmissionImportance.greaterThanEqual( 0.0 ) )
|
|
262
|
-
.and( samplingInfo.clearcoatImportance.greaterThanEqual( 0.0 ) )
|
|
263
|
-
.toVar();
|
|
262
|
+
.and( samplingInfo.clearcoatImportance.greaterThanEqual( 0.0 ) );
|
|
264
263
|
|
|
265
264
|
If( validInput.not(), () => {
|
|
266
265
|
|
|
267
266
|
// Fallback to diffuse sampling
|
|
268
267
|
const r1_fb = RandomValue( rngState ).toVar();
|
|
269
268
|
const r2_fb = RandomValue( rngState ).toVar();
|
|
270
|
-
const sampleRand = vec2( r1_fb, r2_fb )
|
|
269
|
+
const sampleRand = vec2( r1_fb, r2_fb );
|
|
271
270
|
r_direction.assign( cosineWeightedSample( N, sampleRand ) );
|
|
272
271
|
r_throughput.assign( material.color.xyz );
|
|
273
272
|
r_misWeight.assign( 1.0 );
|
|
@@ -312,7 +311,7 @@ export const calculateIndirectLighting = Fn( ( [
|
|
|
312
311
|
} ).ElseIf( selectedStrategy.equal( int( 3 ) ), () => {
|
|
313
312
|
|
|
314
313
|
// Strategy 3: Transmission
|
|
315
|
-
const entering = dot( V, N ).greaterThan( 0.0 )
|
|
314
|
+
const entering = dot( V, N ).greaterThan( 0.0 );
|
|
316
315
|
// pathWavelength=0 — MIS evaluation reads only direction/PDF, no spectral tint
|
|
317
316
|
const mtResult = MicrofacetTransmissionResult.wrap( sampleMicrofacetTransmission(
|
|
318
317
|
V, N, material.ior, material.roughness, entering, material.dispersion, sampleRand, rngState, float( 0.0 )
|
|
@@ -333,7 +332,7 @@ export const calculateIndirectLighting = Fn( ( [
|
|
|
333
332
|
// For transmission directions (below surface), use |cos| instead of max(cos, 0)
|
|
334
333
|
const rawNoL = dot( N, sampleDir ).toVar();
|
|
335
334
|
const NoL = max( rawNoL, 0.0 ).toVar();
|
|
336
|
-
const absNoL = abs( rawNoL )
|
|
335
|
+
const absNoL = abs( rawNoL );
|
|
337
336
|
|
|
338
337
|
// Calculate combined PDF for MIS (material strategies only)
|
|
339
338
|
const combinedPdf = float( 0.0 ).toVar();
|
|
@@ -351,25 +350,26 @@ export const calculateIndirectLighting = Fn( ( [
|
|
|
351
350
|
|
|
352
351
|
If( weights.useDiffuse, () => {
|
|
353
352
|
|
|
354
|
-
|
|
355
|
-
combinedPdf.addAssign( weights.diffuseWeight.mul( diffusePdf ) );
|
|
353
|
+
combinedPdf.addAssign( weights.diffuseWeight.mul( cosineWeightedPDF( NoL ) ) );
|
|
356
354
|
|
|
357
355
|
} );
|
|
358
356
|
|
|
359
357
|
If( weights.useTransmission.and( material.transmission.greaterThan( 0.0 ) ), () => {
|
|
360
358
|
|
|
361
359
|
// Calculate transmission PDF for this direction
|
|
362
|
-
const entering = dot( V, N ).greaterThan( 0.0 )
|
|
363
|
-
|
|
364
|
-
|
|
360
|
+
const entering = dot( V, N ).greaterThan( 0.0 );
|
|
361
|
+
combinedPdf.addAssign( weights.transmissionWeight.mul(
|
|
362
|
+
calculateTransmissionPDF( V, sampleDir, N, material.ior, material.roughness, entering )
|
|
363
|
+
) );
|
|
365
364
|
|
|
366
365
|
} );
|
|
367
366
|
|
|
368
367
|
If( weights.useClearcoat.and( material.clearcoat.greaterThan( 0.0 ) ), () => {
|
|
369
368
|
|
|
370
369
|
// Calculate clearcoat PDF for this direction
|
|
371
|
-
|
|
372
|
-
|
|
370
|
+
combinedPdf.addAssign( weights.clearcoatWeight.mul(
|
|
371
|
+
calculateClearcoatPDF( V, sampleDir, N, material.clearcoatRoughness )
|
|
372
|
+
) );
|
|
373
373
|
|
|
374
374
|
} );
|
|
375
375
|
|
|
@@ -382,10 +382,9 @@ export const calculateIndirectLighting = Fn( ( [
|
|
|
382
382
|
|
|
383
383
|
// Throughput calculation: use |cos| for transmission, max(cos,0) for reflection strategies
|
|
384
384
|
const cosineWeight = select( selectedStrategy.equal( int( 3 ) ), absNoL, NoL );
|
|
385
|
-
const throughput = sampleBrdfValue.mul( cosineWeight ).mul( misWeight ).div( samplePdf ).toVar();
|
|
386
385
|
|
|
387
386
|
r_direction.assign( sampleDir );
|
|
388
|
-
r_throughput.assign(
|
|
387
|
+
r_throughput.assign( sampleBrdfValue.mul( cosineWeight ).mul( misWeight ).div( samplePdf ) );
|
|
389
388
|
r_misWeight.assign( misWeight );
|
|
390
389
|
r_pdf.assign( samplePdf );
|
|
391
390
|
r_combinedPdf.assign( combinedPdf );
|
|
@@ -107,8 +107,7 @@ export const sampleRectAreaLight = Fn( ( [ light, rayOrigin, ruv, lightSelection
|
|
|
107
107
|
// Sample random position on rectangle (u/v are half-vectors, so map [0,1] → [-1,1])
|
|
108
108
|
const randomPos = light.position
|
|
109
109
|
.add( light.u.mul( ruv.x.mul( 2.0 ).sub( 1.0 ) ) )
|
|
110
|
-
.add( light.v.mul( ruv.y.mul( 2.0 ).sub( 1.0 ) ) )
|
|
111
|
-
.toVar();
|
|
110
|
+
.add( light.v.mul( ruv.y.mul( 2.0 ).sub( 1.0 ) ) );
|
|
112
111
|
|
|
113
112
|
const toLight = randomPos.sub( rayOrigin ).toVar();
|
|
114
113
|
const lightDistSq = dot( toLight, toLight ).toVar();
|
|
@@ -222,7 +221,7 @@ export const samplePointLightWithAttenuation = Fn( ( [ light, rayOrigin, lightSe
|
|
|
222
221
|
If( lightDistSq.greaterThanEqual( 1e-20 ), () => {
|
|
223
222
|
|
|
224
223
|
const lightDist = sqrt( lightDistSq ).toVar();
|
|
225
|
-
const lightDir = toLight.div( lightDist )
|
|
224
|
+
const lightDir = toLight.div( lightDist );
|
|
226
225
|
|
|
227
226
|
// Calculate distance attenuation using the light's actual distance and decay properties
|
|
228
227
|
const distanceAttenuation = getDistanceAttenuation( { lightDistance: lightDist, cutoffDistance: light.distance, decayExponent: light.decay } );
|
|
@@ -400,7 +399,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
400
399
|
|
|
401
400
|
If( totalWeight.lessThanEqual( 0.0 ), () => {
|
|
402
401
|
|
|
403
|
-
const lightSelection = randomSeed.x.mul( float( totalLights ) )
|
|
402
|
+
const lightSelection = randomSeed.x.mul( float( totalLights ) );
|
|
404
403
|
const selectedLight = int( lightSelection ).toVar();
|
|
405
404
|
// Guard division by zero
|
|
406
405
|
const lightSelectionPdf = float( 1.0 ).div( max( float( totalLights ), 1.0 ) ).toVar();
|
|
@@ -544,7 +543,7 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
544
543
|
w.mul( cosTheta ).add( u.mul( cos( phi ) ).add( v.mul( sin( phi ) ) ).mul( sinTheta ) )
|
|
545
544
|
) );
|
|
546
545
|
// Guard division: (1.0 - cosHalfAngle) could be zero if angle is 0
|
|
547
|
-
const solidAngle = float( TWO_PI ).mul( max( float( 1.0 ).sub( cosHalfAngle ), 1e-10 ) )
|
|
546
|
+
const solidAngle = float( TWO_PI ).mul( max( float( 1.0 ).sub( cosHalfAngle ), 1e-10 ) );
|
|
548
547
|
dirPdf.assign( float( 1.0 ).div( solidAngle ) );
|
|
549
548
|
|
|
550
549
|
} );
|
|
@@ -626,9 +625,9 @@ export const sampleLightWithImportance = Fn( ( [
|
|
|
626
625
|
// to avoid recomputing H + dots.
|
|
627
626
|
export const calculateMaterialPDFFromDots = Fn( ( [ material, dots ] ) => {
|
|
628
627
|
|
|
629
|
-
const NoV = dots.NoV
|
|
628
|
+
const NoV = dots.NoV;
|
|
630
629
|
const NoL = dots.NoL.toVar();
|
|
631
|
-
const NoH = dots.NoH
|
|
630
|
+
const NoH = dots.NoH;
|
|
632
631
|
|
|
633
632
|
// Calculate lobe weights
|
|
634
633
|
const diffuseWeight = float( 1.0 ).sub( material.metalness ).mul(
|
|
@@ -660,7 +659,7 @@ export const calculateMaterialPDFFromDots = Fn( ( [ material, dots ] ) => {
|
|
|
660
659
|
// Specular PDF (VNDF sampling used in path tracer)
|
|
661
660
|
If( specularWeight.greaterThan( 0.0 ).and( NoL.greaterThan( 0.0 ) ), () => {
|
|
662
661
|
|
|
663
|
-
const roughness = max( material.roughness, 0.02 )
|
|
662
|
+
const roughness = max( material.roughness, 0.02 );
|
|
664
663
|
pdf.addAssign( specularWeight.mul( calculateVNDFPDF( NoH, NoV, roughness ) ) );
|
|
665
664
|
|
|
666
665
|
} );
|
|
@@ -774,10 +773,10 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
774
773
|
const sampleBRDF = tslBool( false ).toVar();
|
|
775
774
|
|
|
776
775
|
// Calculate effective weights for probability (only include light weight if lights exist)
|
|
777
|
-
const effectiveLightWeight = select( hasDiscreteLights, lightWeight, float( 0.0 ) )
|
|
776
|
+
const effectiveLightWeight = select( hasDiscreteLights, lightWeight, float( 0.0 ) );
|
|
778
777
|
// Guard division
|
|
779
|
-
const invTotalSamplingWeight = float( 1.0 ).div( max( totalSamplingWeight, 1e-10 ) )
|
|
780
|
-
const cumulativeLight = effectiveLightWeight.mul( invTotalSamplingWeight )
|
|
778
|
+
const invTotalSamplingWeight = float( 1.0 ).div( max( totalSamplingWeight, 1e-10 ) );
|
|
779
|
+
const cumulativeLight = effectiveLightWeight.mul( invTotalSamplingWeight );
|
|
781
780
|
|
|
782
781
|
If( rand.lessThan( cumulativeLight ).and( useLightSampling ).and( hasDiscreteLights ), () => {
|
|
783
782
|
|
|
@@ -813,11 +812,11 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
813
812
|
If( lightSample.valid.and( lightSample.pdf.greaterThan( 0.0 ) ), () => {
|
|
814
813
|
|
|
815
814
|
const NoL = max( float( 0.0 ), dot( hitNormal, lightSample.direction ) ).toVar();
|
|
816
|
-
const lightImportance = lightSample.emission.x.add( lightSample.emission.y ).add( lightSample.emission.z )
|
|
815
|
+
const lightImportance = lightSample.emission.x.add( lightSample.emission.y ).add( lightSample.emission.z );
|
|
817
816
|
|
|
818
817
|
If( NoL.greaterThan( 0.0 ).and( lightImportance.mul( NoL ).greaterThan( importanceThreshold ) ).and( isDirectionValid( { direction: lightSample.direction, surfaceNormal: hitNormal } ) ), () => {
|
|
819
818
|
|
|
820
|
-
const shadowDistance = min( lightSample.distance.sub( 0.001 ), float( 1000.0 ) )
|
|
819
|
+
const shadowDistance = min( lightSample.distance.sub( 0.001 ), float( 1000.0 ) );
|
|
821
820
|
const visibility = shadow( rayOrigin, lightSample.direction, shadowDistance );
|
|
822
821
|
|
|
823
822
|
If( visibility.greaterThan( 0.0 ), () => {
|
|
@@ -832,8 +831,8 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
832
831
|
|
|
833
832
|
If( bPdf.greaterThan( 0.0 ).and( useBRDFSampling ), () => {
|
|
834
833
|
|
|
835
|
-
const lightPdfWeighted = lightSample.pdf.mul( lightWeight )
|
|
836
|
-
const brdfPdfWeighted = bPdf.mul( brdfWeight )
|
|
834
|
+
const lightPdfWeighted = lightSample.pdf.mul( lightWeight );
|
|
835
|
+
const brdfPdfWeighted = bPdf.mul( brdfWeight );
|
|
837
836
|
|
|
838
837
|
// Apply power heuristic only for area lights — the BRDF path can
|
|
839
838
|
// intersect area lights, so both strategies contribute and MIS is valid.
|
|
@@ -916,7 +915,7 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
916
915
|
|
|
917
916
|
If( hitDistance.greaterThan( 0.0 ), () => {
|
|
918
917
|
|
|
919
|
-
const shadowDistance = min( hitDistance.sub( 0.001 ), float( 1000.0 ) )
|
|
918
|
+
const shadowDistance = min( hitDistance.sub( 0.001 ), float( 1000.0 ) );
|
|
920
919
|
const visibility = shadow( rayOrigin, brdfSampleDirection, shadowDistance );
|
|
921
920
|
|
|
922
921
|
If( visibility.greaterThan( 0.0 ), () => {
|
|
@@ -925,16 +924,16 @@ export const calculateDirectLightingUnified = Fn( ( [
|
|
|
925
924
|
|
|
926
925
|
If( lightFacing.greaterThan( 0.0 ), () => {
|
|
927
926
|
|
|
928
|
-
const lightDistSq = hitDistance.mul( hitDistance )
|
|
927
|
+
const lightDistSq = hitDistance.mul( hitDistance );
|
|
929
928
|
// Guard division
|
|
930
929
|
const lightPdf = lightDistSq.div( max( light.area.mul( lightFacing ), EPSILON ) ).toVar();
|
|
931
930
|
lightPdf.divAssign( max( float( totalLights ), 1.0 ) );
|
|
932
931
|
|
|
933
|
-
const brdfPdfWeighted = brdfSamplePdf.mul( brdfWeight )
|
|
934
|
-
const lightPdfWeighted = lightPdf.mul( lightWeight )
|
|
932
|
+
const brdfPdfWeighted = brdfSamplePdf.mul( brdfWeight );
|
|
933
|
+
const lightPdfWeighted = lightPdf.mul( lightWeight );
|
|
935
934
|
const misW = powerHeuristic( { pdf1: brdfPdfWeighted, pdf2: lightPdfWeighted } ).toVar();
|
|
936
935
|
|
|
937
|
-
const lightEmission = light.color.mul( light.intensity )
|
|
936
|
+
const lightEmission = light.color.mul( light.intensity );
|
|
938
937
|
// Guard division
|
|
939
938
|
const brdfContribution = lightEmission.mul( brdfSampleValue ).mul( NoL ).mul( visibility ).mul( misW ).div( max( brdfSamplePdf, 1e-10 ) );
|
|
940
939
|
totalContribution.addAssign( brdfContribution.mul( totalSamplingWeight ).div( max( brdfWeight, 1e-10 ) ) );
|
|
@@ -77,7 +77,7 @@ export const evaluateMaterialResponseFromDots = Fn( ( [ material, dots ] ) => {
|
|
|
77
77
|
// Precalculate shared terms
|
|
78
78
|
const D = DistributionGGX( dots.NoH, material.roughness );
|
|
79
79
|
const G = GeometrySmith( dots.NoV, dots.NoL, material.roughness );
|
|
80
|
-
const F = fresnelSchlick( dots.VoH, F0 )
|
|
80
|
+
const F = fresnelSchlick( dots.VoH, F0 );
|
|
81
81
|
|
|
82
82
|
// Single-scatter specular BRDF
|
|
83
83
|
const specularSS = D.mul( G ).mul( F ).div( max( float( 4.0 ).mul( dots.NoV ).mul( dots.NoL ), EPSILON ) );
|
|
@@ -145,7 +145,7 @@ export const evaluateLayeredBRDF = Fn( ( [ dots, material ] ) => {
|
|
|
145
145
|
|
|
146
146
|
const D = DistributionGGX( dots.NoH, material.roughness );
|
|
147
147
|
const G = GeometrySmith( dots.NoV, dots.NoL, material.roughness );
|
|
148
|
-
const F = fresnelSchlick( dots.VoH, F0 )
|
|
148
|
+
const F = fresnelSchlick( dots.VoH, F0 );
|
|
149
149
|
const baseBRDFSS = D.mul( G ).mul( F ).div( max( float( 4.0 ).mul( dots.NoV ).mul( dots.NoL ), EPSILON ) );
|
|
150
150
|
|
|
151
151
|
// Shared DFG evaluation — compensation factor and total directional albedo
|
|
@@ -156,7 +156,7 @@ export const evalIridescence = Fn( ( [ outsideIOR, eta2, cosTheta1, thinFilmThic
|
|
|
156
156
|
const iridescenceIor = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) ).toVar();
|
|
157
157
|
|
|
158
158
|
// Evaluate the cosTheta on the base layer (Snell law)
|
|
159
|
-
const sinTheta2Sq = square( { x: outsideIOR.div( iridescenceIor ) } ).mul( float( 1.0 ).sub( square( { x: cosTheta1 } ) ) )
|
|
159
|
+
const sinTheta2Sq = square( { x: outsideIOR.div( iridescenceIor ) } ).mul( float( 1.0 ).sub( square( { x: cosTheta1 } ) ) );
|
|
160
160
|
|
|
161
161
|
// Handle TIR
|
|
162
162
|
const cosTheta2Sq = float( 1.0 ).sub( sinTheta2Sq ).toVar();
|
|
@@ -171,11 +171,11 @@ export const evalIridescence = Fn( ( [ outsideIOR, eta2, cosTheta1, thinFilmThic
|
|
|
171
171
|
const cosTheta2 = sqrt( cosTheta2Sq ).toVar();
|
|
172
172
|
|
|
173
173
|
// First interface
|
|
174
|
-
const R0 = iorToFresnel0( iridescenceIor, outsideIOR )
|
|
174
|
+
const R0 = iorToFresnel0( iridescenceIor, outsideIOR );
|
|
175
175
|
const R12 = fresnelSchlickFloat( cosTheta1, R0 ).toVar();
|
|
176
176
|
const T121 = float( 1.0 ).sub( R12 ).toVar();
|
|
177
|
-
const phi12 = iridescenceIor.lessThan( outsideIOR ).select( float( PI ), float( 0.0 ) )
|
|
178
|
-
const phi21 = float( PI ).sub( phi12 )
|
|
177
|
+
const phi12 = iridescenceIor.lessThan( outsideIOR ).select( float( PI ), float( 0.0 ) );
|
|
178
|
+
const phi21 = float( PI ).sub( phi12 );
|
|
179
179
|
|
|
180
180
|
// Second interface
|
|
181
181
|
const baseIOR = fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ).toVar();
|
|
@@ -189,7 +189,7 @@ export const evalIridescence = Fn( ( [ outsideIOR, eta2, cosTheta1, thinFilmThic
|
|
|
189
189
|
baseIOR.x.lessThan( iridescenceIor ).select( float( PI ), float( 0.0 ) ),
|
|
190
190
|
baseIOR.y.lessThan( iridescenceIor ).select( float( PI ), float( 0.0 ) ),
|
|
191
191
|
baseIOR.z.lessThan( iridescenceIor ).select( float( PI ), float( 0.0 ) )
|
|
192
|
-
)
|
|
192
|
+
);
|
|
193
193
|
|
|
194
194
|
const OPD = float( 2.0 ).mul( iridescenceIor ).mul( thinFilmThickness ).mul( cosTheta2 ).toVar();
|
|
195
195
|
const phi = vec3( phi21 ).add( phi23 ).toVar();
|
|
@@ -134,16 +134,16 @@ const wavelengthToXYZ = /*@__PURE__*/ Fn( ( [ wl ] ) => {
|
|
|
134
134
|
export const sampleWavelengthForDispersion = Fn( ( [ baseIOR, dispersionStrength, random ] ) => {
|
|
135
135
|
|
|
136
136
|
const wl = mix( float( 380.0 ), float( 700.0 ), random ).toVar();
|
|
137
|
-
const sampledIOR = iorFromWavelength( baseIOR, dispersionStrength, wl )
|
|
137
|
+
const sampledIOR = iorFromWavelength( baseIOR, dispersionStrength, wl );
|
|
138
138
|
|
|
139
139
|
const xyz = wavelengthToXYZ( wl ).toVar();
|
|
140
140
|
const rgb = vec3(
|
|
141
141
|
xyz.x.mul( 3.2406 ).sub( xyz.y.mul( 1.5372 ) ).sub( xyz.z.mul( 0.4986 ) ),
|
|
142
142
|
xyz.x.mul( - 0.9689 ).add( xyz.y.mul( 1.8758 ) ).add( xyz.z.mul( 0.0415 ) ),
|
|
143
143
|
xyz.x.mul( 0.0557 ).sub( xyz.y.mul( 0.2040 ) ).add( xyz.z.mul( 1.0570 ) ),
|
|
144
|
-
)
|
|
144
|
+
);
|
|
145
145
|
|
|
146
|
-
const colorWeight = max( rgb, vec3( 0.0 ) ).mul( vec3( 1.819, 2.773, 2.928 ) )
|
|
146
|
+
const colorWeight = max( rgb, vec3( 0.0 ) ).mul( vec3( 1.819, 2.773, 2.928 ) );
|
|
147
147
|
|
|
148
148
|
return SpectralSample( {
|
|
149
149
|
wavelength: wl,
|
|
@@ -390,9 +390,7 @@ export const handleTransmission = Fn( ( [
|
|
|
390
390
|
const metallicReflect = float( 0.95 );
|
|
391
391
|
|
|
392
392
|
// Blend based on metalness
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
reflectProb.assign( baseReflectProb );
|
|
393
|
+
reflectProb.assign( mix( dielectricReflect, metallicReflect, material.metalness ) );
|
|
396
394
|
|
|
397
395
|
} );
|
|
398
396
|
|
package/src/TSL/PathTracer.js
CHANGED
|
@@ -182,9 +182,10 @@ export const pathTracerMain = ( params ) => {
|
|
|
182
182
|
|
|
183
183
|
const baseSeed = getDecorrelatedSeed( { pixelCoord, rayIndex: int( 0 ), frame } ).toVar();
|
|
184
184
|
|
|
185
|
-
// MRT
|
|
185
|
+
// MRT — linearDepth is ray distance (sky/miss = 1e6), the convention
|
|
186
|
+
// MotionVector + ASVGF expect downstream.
|
|
186
187
|
const worldNormal = vec3( 0.0, 0.0, 1.0 ).toVar();
|
|
187
|
-
const linearDepth = float(
|
|
188
|
+
const linearDepth = float( 1e6 ).toVar();
|
|
188
189
|
|
|
189
190
|
// Accumulate per-sample alpha for transparent background (0.0 = env, 1.0 = geometry)
|
|
190
191
|
const pixelAlpha = float( 0.0 ).toVar();
|
|
@@ -321,10 +322,7 @@ export const pathTracerMain = ( params ) => {
|
|
|
321
322
|
If( traceResult.firstHitDistance.lessThan( 1e9 ), () => {
|
|
322
323
|
|
|
323
324
|
worldNormal.assign( normalize( traceResult.objectNormal ) );
|
|
324
|
-
|
|
325
|
-
linearDepth.assign( computeNDCDepth( {
|
|
326
|
-
worldPos: traceResult.firstHitPoint, cameraProjectionMatrix, cameraViewMatrix,
|
|
327
|
-
} ) );
|
|
325
|
+
linearDepth.assign( traceResult.firstHitDistance );
|
|
328
326
|
|
|
329
327
|
} );
|
|
330
328
|
|