rayzee 5.11.0 → 6.0.1
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 +81 -24
- package/dist/assets/AIUpscalerWorker-AXN-lKWN.js +2 -0
- package/dist/assets/AIUpscalerWorker-AXN-lKWN.js.map +1 -0
- package/dist/rayzee.es.js +1238 -1825
- package/dist/rayzee.es.js.map +1 -1
- package/dist/rayzee.umd.js +50 -74
- package/dist/rayzee.umd.js.map +1 -1
- package/package.json +1 -4
- package/src/AssetConfig.js +56 -0
- package/src/EngineDefaults.js +5 -3
- package/src/EngineEvents.js +1 -0
- package/src/Passes/AIUpscaler.js +44 -22
- package/src/Passes/OIDNDenoiser.js +4 -104
- package/src/PathTracerApp.js +54 -65
- package/src/Processor/AssetLoader.js +5 -2
- package/src/Processor/Workers/AIUpscalerWorker.js +21 -6
- package/src/Stages/ASVGF.js +6 -27
- package/src/Stages/AdaptiveSampling.js +10 -26
- package/src/Stages/PathTracer.js +4 -5
- package/src/TSL/BVHTraversal.js +3 -30
- package/src/TSL/Clearcoat.js +1 -6
- package/src/TSL/Common.js +1 -14
- package/src/TSL/Debugger.js +0 -12
- package/src/TSL/EmissiveSampling.js +0 -16
- package/src/TSL/Environment.js +0 -7
- package/src/TSL/LightsDirect.js +3 -379
- package/src/TSL/LightsIndirect.js +2 -1
- package/src/TSL/LightsSampling.js +0 -171
- package/src/TSL/MaterialEvaluation.js +3 -103
- package/src/TSL/MaterialProperties.js +1 -56
- package/src/TSL/MaterialSampling.js +2 -284
- package/src/TSL/MaterialTransmission.js +80 -276
- package/src/TSL/PathTracerCore.js +8 -1
- package/src/TSL/Random.js +0 -23
- package/src/TSL/Struct.js +0 -21
- package/src/TSL/TextureSampling.js +0 -69
- package/src/index.js +5 -2
- package/src/managers/DenoisingManager.js +13 -5
- package/src/managers/VideoRenderManager.js +4 -4
- package/dist/assets/AIUpscalerWorker-D58dcMrY.js +0 -2
- package/dist/assets/AIUpscalerWorker-D58dcMrY.js.map +0 -1
- package/src/Processor/createRenderTargetHelper.js +0 -521
- package/src/TSL/RayIntersection.js +0 -162
- package/src/managers/helpers/StatsHelper.js +0 -45
|
@@ -15,19 +15,14 @@ import {
|
|
|
15
15
|
Loop,
|
|
16
16
|
select,
|
|
17
17
|
abs,
|
|
18
|
-
acos,
|
|
19
|
-
sin,
|
|
20
|
-
cos,
|
|
21
18
|
dot,
|
|
22
|
-
normalize,
|
|
23
19
|
reflect,
|
|
24
20
|
refract,
|
|
25
21
|
max,
|
|
26
22
|
min,
|
|
27
23
|
mix,
|
|
28
24
|
clamp,
|
|
29
|
-
|
|
30
|
-
fract,
|
|
25
|
+
exp,
|
|
31
26
|
} from 'three/tsl';
|
|
32
27
|
|
|
33
28
|
import { struct } from './patches.js';
|
|
@@ -46,6 +41,7 @@ export const TransmissionResult = struct( {
|
|
|
46
41
|
direction: 'vec3', // New ray direction after transmission/reflection
|
|
47
42
|
throughput: 'vec3', // Color throughput including absorption
|
|
48
43
|
didReflect: 'bool', // Whether the ray was reflected instead of transmitted
|
|
44
|
+
pathWavelength: 'float', // 0 if path is not yet spectral, else locked wavelength in nm
|
|
49
45
|
} );
|
|
50
46
|
|
|
51
47
|
export const MaterialInteractionResult = struct( {
|
|
@@ -57,6 +53,7 @@ export const MaterialInteractionResult = struct( {
|
|
|
57
53
|
direction: 'vec3', // New ray direction if continuing
|
|
58
54
|
throughput: 'vec3', // Color modification for the ray
|
|
59
55
|
alpha: 'float', // Alpha modification
|
|
56
|
+
pathWavelength: 'float', // 0 if path is not yet spectral, else locked wavelength in nm
|
|
60
57
|
} );
|
|
61
58
|
|
|
62
59
|
export const Medium = struct( {
|
|
@@ -77,6 +74,8 @@ export const MicrofacetTransmissionResult = struct( {
|
|
|
77
74
|
halfVector: 'vec3', // Sampled half-vector
|
|
78
75
|
didReflect: 'bool', // Whether TIR occurred
|
|
79
76
|
pdf: 'float', // PDF of the sampled direction
|
|
77
|
+
colorWeight: 'vec3', // Spectral tint to apply once; vec3(1) if locked or non-dispersive
|
|
78
|
+
pathWavelength: 'float', // 0 if path is not yet spectral, else locked wavelength in nm
|
|
80
79
|
} );
|
|
81
80
|
|
|
82
81
|
// Maximum number of nested media
|
|
@@ -104,171 +103,55 @@ export const MediumStack = struct( {
|
|
|
104
103
|
} );
|
|
105
104
|
|
|
106
105
|
// ================================================================================
|
|
107
|
-
//
|
|
106
|
+
// DISPERSION
|
|
108
107
|
// ================================================================================
|
|
109
108
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const result = float( 1.0 ).toVar();
|
|
113
|
-
|
|
114
|
-
If( mediumStack.depth.greaterThan( int( 0 ) ), () => {
|
|
115
|
-
|
|
116
|
-
// Read IOR from current depth
|
|
117
|
-
If( mediumStack.depth.equal( int( 1 ) ), () => {
|
|
118
|
-
|
|
119
|
-
result.assign( mediumStack.m1_ior );
|
|
120
|
-
|
|
121
|
-
} );
|
|
122
|
-
If( mediumStack.depth.equal( int( 2 ) ), () => {
|
|
123
|
-
|
|
124
|
-
result.assign( mediumStack.m2_ior );
|
|
125
|
-
|
|
126
|
-
} );
|
|
127
|
-
If( mediumStack.depth.equal( int( 3 ) ), () => {
|
|
109
|
+
// Cauchy IOR n(λ) = baseIOR + 0.03·dispersion / λ_µm²
|
|
110
|
+
export const iorFromWavelength = /*@__PURE__*/ Fn( ( [ baseIOR, dispersionStrength, wavelength ] ) => {
|
|
128
111
|
|
|
129
|
-
|
|
112
|
+
const wlMicron = wavelength.div( 1000.0 );
|
|
113
|
+
return baseIOR.add( dispersionStrength.mul( 0.03 ).div( wlMicron.mul( wlMicron ) ) );
|
|
130
114
|
|
|
131
|
-
|
|
115
|
+
} );
|
|
132
116
|
|
|
133
|
-
|
|
117
|
+
// Wyman et al. JCGT 2013 piecewise-Gaussian fit to CIE 1931 2° observer
|
|
118
|
+
const cieGauss = /*@__PURE__*/ Fn( ( [ x, mu, sigmaLo, sigmaHi ] ) => {
|
|
134
119
|
|
|
135
|
-
|
|
120
|
+
const sigma = select( x.lessThan( mu ), sigmaLo, sigmaHi );
|
|
121
|
+
const t = x.sub( mu ).mul( sigma );
|
|
122
|
+
return exp( float( - 0.5 ).mul( t ).mul( t ) );
|
|
136
123
|
|
|
137
124
|
} );
|
|
138
125
|
|
|
139
|
-
|
|
140
|
-
// DISPERSION
|
|
141
|
-
// ================================================================================
|
|
126
|
+
const wavelengthToXYZ = /*@__PURE__*/ Fn( ( [ wl ] ) => {
|
|
142
127
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return max( dispersiveIOR, vec3f( 1.001f ) );
|
|
152
|
-
}
|
|
153
|
-
` );
|
|
128
|
+
const X = cieGauss( wl, 442.0, 0.0624, 0.0374 ).mul( 0.362 )
|
|
129
|
+
.add( cieGauss( wl, 599.8, 0.0264, 0.0323 ).mul( 1.056 ) )
|
|
130
|
+
.sub( cieGauss( wl, 501.1, 0.0490, 0.0382 ).mul( 0.065 ) );
|
|
131
|
+
const Y = cieGauss( wl, 568.8, 0.0213, 0.0247 ).mul( 0.821 )
|
|
132
|
+
.add( cieGauss( wl, 530.9, 0.0613, 0.0322 ).mul( 0.286 ) );
|
|
133
|
+
const Z = cieGauss( wl, 437.0, 0.0845, 0.0278 ).mul( 1.217 )
|
|
134
|
+
.add( cieGauss( wl, 459.0, 0.0385, 0.0725 ).mul( 0.681 ) );
|
|
135
|
+
return vec3( X, Y, Z );
|
|
154
136
|
|
|
155
|
-
|
|
156
|
-
export const wavelengthToRGB = /*@__PURE__*/ wgslFn( `
|
|
157
|
-
fn wavelengthToRGB( wavelength: f32 ) -> vec3f {
|
|
158
|
-
var color = vec3f( 0.0f );
|
|
159
|
-
// Violet: 380-440
|
|
160
|
-
if ( wavelength >= 380.0f && wavelength < 440.0f ) {
|
|
161
|
-
let t = ( wavelength - 380.0f ) / 60.0f;
|
|
162
|
-
color = vec3f( 0.6f + 0.4f * ( 1.0f - t ), 0.0f, 1.0f );
|
|
163
|
-
}
|
|
164
|
-
// Blue: 440-490
|
|
165
|
-
if ( wavelength >= 440.0f && wavelength < 490.0f ) {
|
|
166
|
-
let t = ( wavelength - 440.0f ) / 50.0f;
|
|
167
|
-
color = vec3f( 0.6f * ( 1.0f - t ), 0.0f, 1.0f );
|
|
168
|
-
}
|
|
169
|
-
// Blue-Cyan: 490-510
|
|
170
|
-
if ( wavelength >= 490.0f && wavelength < 510.0f ) {
|
|
171
|
-
let t = ( wavelength - 490.0f ) / 20.0f;
|
|
172
|
-
color = vec3f( 0.0f, t, 1.0f );
|
|
173
|
-
}
|
|
174
|
-
// Cyan-Green-Yellow: 510-580
|
|
175
|
-
if ( wavelength >= 510.0f && wavelength < 580.0f ) {
|
|
176
|
-
let t = ( wavelength - 510.0f ) / 70.0f;
|
|
177
|
-
if ( t < 0.5f ) {
|
|
178
|
-
color = vec3f( 0.0f, 1.0f, 1.0f - t * 2.0f );
|
|
179
|
-
} else {
|
|
180
|
-
color = vec3f( ( t - 0.5f ) * 2.0f, 1.0f, 0.0f );
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
// Yellow-Orange-Red: 580-645
|
|
184
|
-
if ( wavelength >= 580.0f && wavelength < 645.0f ) {
|
|
185
|
-
let t = ( wavelength - 580.0f ) / 65.0f;
|
|
186
|
-
color = vec3f( 1.0f, 1.0f - t, 0.0f );
|
|
187
|
-
}
|
|
188
|
-
// Red: 645-700
|
|
189
|
-
if ( wavelength >= 645.0f && wavelength <= 700.0f ) {
|
|
190
|
-
color = vec3f( 1.0f, 0.0f, 0.0f );
|
|
191
|
-
}
|
|
192
|
-
// Apply intensity falloff at spectrum edges
|
|
193
|
-
if ( wavelength < 420.0f ) {
|
|
194
|
-
color = color * ( ( wavelength - 380.0f ) / 40.0f );
|
|
195
|
-
}
|
|
196
|
-
if ( wavelength > 680.0f ) {
|
|
197
|
-
color = color * ( ( 700.0f - wavelength ) / 20.0f );
|
|
198
|
-
}
|
|
199
|
-
return color;
|
|
200
|
-
}
|
|
201
|
-
` );
|
|
137
|
+
} );
|
|
202
138
|
|
|
203
|
-
//
|
|
139
|
+
// Sample a wavelength on [380,700]nm and return its IOR + sRGB colorWeight (CIE 1931 →
|
|
140
|
+
// sRGB, gamut-clipped). The (1.819, 2.773, 2.928) factors normalize the clipped per-λ
|
|
141
|
+
// average to vec3(1), so clear glass converges to white as samples accumulate.
|
|
204
142
|
export const sampleWavelengthForDispersion = Fn( ( [ baseIOR, dispersionStrength, random ] ) => {
|
|
205
143
|
|
|
206
|
-
// Map random value to visible spectrum (380-700nm)
|
|
207
144
|
const wl = mix( float( 380.0 ), float( 700.0 ), random ).toVar();
|
|
145
|
+
const sampledIOR = iorFromWavelength( baseIOR, dispersionStrength, wl ).toVar();
|
|
208
146
|
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const sampledIOR = A.add( B.div( wlMicron.mul( wlMicron ) ) ).toVar();
|
|
216
|
-
|
|
217
|
-
// PURE SATURATED spectral colors
|
|
218
|
-
const colorWeight = vec3( 0.0 ).toVar();
|
|
147
|
+
const xyz = wavelengthToXYZ( wl ).toVar();
|
|
148
|
+
const rgb = vec3(
|
|
149
|
+
xyz.x.mul( 3.2406 ).sub( xyz.y.mul( 1.5372 ) ).sub( xyz.z.mul( 0.4986 ) ),
|
|
150
|
+
xyz.x.mul( - 0.9689 ).add( xyz.y.mul( 1.8758 ) ).add( xyz.z.mul( 0.0415 ) ),
|
|
151
|
+
xyz.x.mul( 0.0557 ).sub( xyz.y.mul( 0.2040 ) ).add( xyz.z.mul( 1.0570 ) ),
|
|
152
|
+
).toVar();
|
|
219
153
|
|
|
220
|
-
|
|
221
|
-
If( wl.greaterThanEqual( 380.0 ).and( wl.lessThan( 420.0 ) ), () => {
|
|
222
|
-
|
|
223
|
-
colorWeight.assign( vec3( 0.9, 0.0, 1.0 ) );
|
|
224
|
-
|
|
225
|
-
} );
|
|
226
|
-
|
|
227
|
-
// Blue: 420-480
|
|
228
|
-
If( wl.greaterThanEqual( 420.0 ).and( wl.lessThan( 480.0 ) ), () => {
|
|
229
|
-
|
|
230
|
-
colorWeight.assign( vec3( 0.0, 0.0, 1.0 ) );
|
|
231
|
-
|
|
232
|
-
} );
|
|
233
|
-
|
|
234
|
-
// Cyan: 480-500
|
|
235
|
-
If( wl.greaterThanEqual( 480.0 ).and( wl.lessThan( 500.0 ) ), () => {
|
|
236
|
-
|
|
237
|
-
colorWeight.assign( vec3( 0.0, 1.0, 1.0 ) );
|
|
238
|
-
|
|
239
|
-
} );
|
|
240
|
-
|
|
241
|
-
// Green: 500-530
|
|
242
|
-
If( wl.greaterThanEqual( 500.0 ).and( wl.lessThan( 530.0 ) ), () => {
|
|
243
|
-
|
|
244
|
-
colorWeight.assign( vec3( 0.0, 1.0, 0.0 ) );
|
|
245
|
-
|
|
246
|
-
} );
|
|
247
|
-
|
|
248
|
-
// Yellow: 530-570
|
|
249
|
-
If( wl.greaterThanEqual( 530.0 ).and( wl.lessThan( 570.0 ) ), () => {
|
|
250
|
-
|
|
251
|
-
colorWeight.assign( vec3( 1.0, 1.0, 0.0 ) );
|
|
252
|
-
|
|
253
|
-
} );
|
|
254
|
-
|
|
255
|
-
// Orange: 570-620
|
|
256
|
-
If( wl.greaterThanEqual( 570.0 ).and( wl.lessThan( 620.0 ) ), () => {
|
|
257
|
-
|
|
258
|
-
colorWeight.assign( vec3( 1.0, 0.5, 0.0 ) );
|
|
259
|
-
|
|
260
|
-
} );
|
|
261
|
-
|
|
262
|
-
// Red: 620-700
|
|
263
|
-
If( wl.greaterThanEqual( 620.0 ).and( wl.lessThanEqual( 700.0 ) ), () => {
|
|
264
|
-
|
|
265
|
-
colorWeight.assign( vec3( 1.0, 0.0, 0.0 ) );
|
|
266
|
-
|
|
267
|
-
} );
|
|
268
|
-
|
|
269
|
-
// Maximum saturation
|
|
270
|
-
colorWeight.assign( pow( colorWeight, vec3( 0.4 ) ) );
|
|
271
|
-
colorWeight.assign( clamp( colorWeight, vec3( 0.0 ), vec3( 2.0 ) ) );
|
|
154
|
+
const colorWeight = max( rgb, vec3( 0.0 ) ).mul( vec3( 1.819, 2.773, 2.928 ) ).toVar();
|
|
272
155
|
|
|
273
156
|
return SpectralSample( {
|
|
274
157
|
wavelength: wl,
|
|
@@ -338,7 +221,7 @@ export const calculateShadowTransmittance = Fn( ( [ rayDir, normal, material, en
|
|
|
338
221
|
// ================================================================================
|
|
339
222
|
|
|
340
223
|
export const sampleMicrofacetTransmission = Fn( ( [
|
|
341
|
-
V, N, ior, roughness, entering, dispersion, xi, rngState
|
|
224
|
+
V, N, ior, roughness, entering, dispersion, xi, rngState, pathWavelength
|
|
342
225
|
] ) => {
|
|
343
226
|
|
|
344
227
|
const result = MicrofacetTransmissionResult( {
|
|
@@ -346,6 +229,8 @@ export const sampleMicrofacetTransmission = Fn( ( [
|
|
|
346
229
|
halfVector: vec3( 0.0 ),
|
|
347
230
|
didReflect: false,
|
|
348
231
|
pdf: float( 0.0 ),
|
|
232
|
+
colorWeight: vec3( 1.0 ),
|
|
233
|
+
pathWavelength: pathWavelength,
|
|
349
234
|
} ).toVar();
|
|
350
235
|
|
|
351
236
|
// For smooth surfaces with dispersion, use perfect refraction with spectral IOR
|
|
@@ -357,9 +242,20 @@ export const sampleMicrofacetTransmission = Fn( ( [
|
|
|
357
242
|
const eta = ior;
|
|
358
243
|
const etaRatio = select( entering, float( 1.0 ).div( eta ), eta ).toVar();
|
|
359
244
|
|
|
360
|
-
//
|
|
361
|
-
|
|
362
|
-
|
|
245
|
+
// Reuse the path's locked wavelength if any; else sample a new one and tint once.
|
|
246
|
+
If( pathWavelength.greaterThan( 0.0 ), () => {
|
|
247
|
+
|
|
248
|
+
const lockedIOR = iorFromWavelength( ior, dispersion, pathWavelength );
|
|
249
|
+
etaRatio.assign( select( entering, float( 1.0 ).div( lockedIOR ), lockedIOR ) );
|
|
250
|
+
|
|
251
|
+
} ).Else( () => {
|
|
252
|
+
|
|
253
|
+
const spectralSample = SpectralSample.wrap( sampleWavelengthForDispersion( ior, dispersion, RandomValue( rngState ) ) );
|
|
254
|
+
etaRatio.assign( select( entering, float( 1.0 ).div( spectralSample.ior ), spectralSample.ior ) );
|
|
255
|
+
result.colorWeight.assign( spectralSample.colorWeight );
|
|
256
|
+
result.pathWavelength.assign( spectralSample.wavelength );
|
|
257
|
+
|
|
258
|
+
} );
|
|
363
259
|
|
|
364
260
|
// Perfect refraction using surface normal
|
|
365
261
|
const refractDir = refract( V.negate(), N, etaRatio ).toVar();
|
|
@@ -390,11 +286,22 @@ export const sampleMicrofacetTransmission = Fn( ( [
|
|
|
390
286
|
// Compute IOR ratio
|
|
391
287
|
const etaRatio = select( entering, float( 1.0 ).div( ior ), ior ).toVar();
|
|
392
288
|
|
|
393
|
-
//
|
|
289
|
+
// Reuse the path's locked wavelength if any; else sample a new one and tint once.
|
|
394
290
|
If( dispersion.greaterThan( 0.0 ), () => {
|
|
395
291
|
|
|
396
|
-
|
|
397
|
-
|
|
292
|
+
If( pathWavelength.greaterThan( 0.0 ), () => {
|
|
293
|
+
|
|
294
|
+
const lockedIOR = iorFromWavelength( ior, dispersion, pathWavelength );
|
|
295
|
+
etaRatio.assign( select( entering, float( 1.0 ).div( lockedIOR ), lockedIOR ) );
|
|
296
|
+
|
|
297
|
+
} ).Else( () => {
|
|
298
|
+
|
|
299
|
+
const spectralSample = SpectralSample.wrap( sampleWavelengthForDispersion( ior, dispersion, RandomValue( rngState ) ) );
|
|
300
|
+
etaRatio.assign( select( entering, float( 1.0 ).div( spectralSample.ior ), spectralSample.ior ) );
|
|
301
|
+
result.colorWeight.assign( spectralSample.colorWeight );
|
|
302
|
+
result.pathWavelength.assign( spectralSample.wavelength );
|
|
303
|
+
|
|
304
|
+
} );
|
|
398
305
|
|
|
399
306
|
} );
|
|
400
307
|
|
|
@@ -446,13 +353,14 @@ export const sampleMicrofacetTransmission = Fn( ( [
|
|
|
446
353
|
|
|
447
354
|
export const handleTransmission = Fn( ( [
|
|
448
355
|
rayDir, normal, material, entering, rngState,
|
|
449
|
-
currentMediumIOR, previousMediumIOR,
|
|
356
|
+
currentMediumIOR, previousMediumIOR, pathWavelength,
|
|
450
357
|
] ) => {
|
|
451
358
|
|
|
452
359
|
const result = TransmissionResult( {
|
|
453
360
|
direction: vec3( 0.0 ),
|
|
454
361
|
throughput: vec3( 1.0 ),
|
|
455
362
|
didReflect: false,
|
|
363
|
+
pathWavelength: pathWavelength,
|
|
456
364
|
} ).toVar();
|
|
457
365
|
|
|
458
366
|
// Setup surface normal based on ray direction
|
|
@@ -509,10 +417,10 @@ export const handleTransmission = Fn( ( [
|
|
|
509
417
|
|
|
510
418
|
If( doReflect, () => {
|
|
511
419
|
|
|
512
|
-
// Reflection
|
|
420
|
+
// Reflection at a transmissive surface — no wavelength locking
|
|
513
421
|
If( material.roughness.greaterThan( 0.05 ), () => {
|
|
514
422
|
|
|
515
|
-
const mtResult = MicrofacetTransmissionResult.wrap( sampleMicrofacetTransmission( V, N, material.ior, material.roughness, entering, float( 0.0 ), xi, rngState ) );
|
|
423
|
+
const mtResult = MicrofacetTransmissionResult.wrap( sampleMicrofacetTransmission( V, N, material.ior, material.roughness, entering, float( 0.0 ), xi, rngState, float( 0.0 ) ) );
|
|
516
424
|
result.direction.assign( mtResult.direction );
|
|
517
425
|
|
|
518
426
|
} ).Else( () => {
|
|
@@ -529,125 +437,20 @@ export const handleTransmission = Fn( ( [
|
|
|
529
437
|
// Transmission/refraction path
|
|
530
438
|
If( material.roughness.greaterThan( 0.05 ).or( material.dispersion.greaterThan( 0.0 ) ), () => {
|
|
531
439
|
|
|
532
|
-
const mtResult = MicrofacetTransmissionResult.wrap( sampleMicrofacetTransmission( V, N, material.ior, material.roughness, entering, material.dispersion, xi, rngState ) );
|
|
440
|
+
const mtResult = MicrofacetTransmissionResult.wrap( sampleMicrofacetTransmission( V, N, material.ior, material.roughness, entering, material.dispersion, xi, rngState, pathWavelength ) );
|
|
441
|
+
result.pathWavelength.assign( mtResult.pathWavelength );
|
|
533
442
|
|
|
534
|
-
// If TIR occurred during sampling, respect it
|
|
535
443
|
If( mtResult.didReflect, () => {
|
|
536
444
|
|
|
445
|
+
// TIR during intended transmission: compensate for selection probability
|
|
537
446
|
result.direction.assign( mtResult.direction );
|
|
538
447
|
result.didReflect.assign( true );
|
|
539
|
-
// TIR during intended transmission: compensate for selection probability
|
|
540
448
|
result.throughput.assign( material.color.xyz.div( max( float( 1.0 ).sub( reflectProb ), 0.05 ) ) );
|
|
541
449
|
|
|
542
450
|
} ).Else( () => {
|
|
543
451
|
|
|
544
452
|
result.direction.assign( mtResult.direction );
|
|
545
|
-
|
|
546
|
-
// Handle dispersion coloring
|
|
547
|
-
If( material.dispersion.greaterThan( 0.0 ), () => {
|
|
548
|
-
|
|
549
|
-
// Calculate refracted ray deviation from original direction
|
|
550
|
-
const originalDir = normalize( rayDir );
|
|
551
|
-
const refractedDir = normalize( result.direction );
|
|
552
|
-
|
|
553
|
-
// Calculate angle-dependent dispersion factor
|
|
554
|
-
const edgeFactor = float( 1.0 ).sub( abs( dot( N, originalDir ) ) );
|
|
555
|
-
const deviationAngle = acos( clamp( dot( originalDir, refractedDir ), - 1.0, 1.0 ) );
|
|
556
|
-
|
|
557
|
-
// Create spatial variation using ray direction and normal
|
|
558
|
-
const combinedVec = normalize( originalDir.add( N ) );
|
|
559
|
-
const spatialVariation = sin( combinedVec.x.mul( 15.0 ) ).mul( cos( combinedVec.y.mul( 12.0 ) ) ).mul( sin( combinedVec.z.mul( 18.0 ) ) );
|
|
560
|
-
|
|
561
|
-
// Add additional variation using refracted direction
|
|
562
|
-
const refractVariation = sin( refractedDir.x.mul( 8.0 ).add( refractedDir.y.mul( 6.0 ) ).add( refractedDir.z.mul( 10.0 ) ) );
|
|
563
|
-
|
|
564
|
-
// Combine multiple factors for better color distribution
|
|
565
|
-
const baseColorIndex = deviationAngle.mul( material.dispersion ).mul( 3.0 );
|
|
566
|
-
const spatialBoost = spatialVariation.mul( 0.3 );
|
|
567
|
-
const refractBoost = refractVariation.mul( 0.2 );
|
|
568
|
-
const edgeBoost = edgeFactor.mul( 0.4 );
|
|
569
|
-
|
|
570
|
-
// Create continuous color mapping across the prism
|
|
571
|
-
const colorIndex = fract( baseColorIndex.add( spatialBoost ).add( refractBoost ).add( edgeBoost ) ).toVar();
|
|
572
|
-
|
|
573
|
-
// ROYGBIV spectrum mapping with smooth transitions
|
|
574
|
-
const rainbowColor = vec3( 0.0 ).toVar();
|
|
575
|
-
|
|
576
|
-
// Red zone
|
|
577
|
-
If( colorIndex.lessThan( 0.143 ), () => {
|
|
578
|
-
|
|
579
|
-
const t = colorIndex.div( 0.143 );
|
|
580
|
-
rainbowColor.assign( mix( vec3( 0.8, 0.0, 0.0 ), vec3( 1.0, 0.0, 0.0 ), t ) );
|
|
581
|
-
|
|
582
|
-
} );
|
|
583
|
-
|
|
584
|
-
// Red to Orange
|
|
585
|
-
If( colorIndex.greaterThanEqual( 0.143 ).and( colorIndex.lessThan( 0.286 ) ), () => {
|
|
586
|
-
|
|
587
|
-
const t = colorIndex.sub( 0.143 ).div( 0.143 );
|
|
588
|
-
rainbowColor.assign( mix( vec3( 1.0, 0.0, 0.0 ), vec3( 1.0, 0.6, 0.0 ), t ) );
|
|
589
|
-
|
|
590
|
-
} );
|
|
591
|
-
|
|
592
|
-
// Orange to Yellow
|
|
593
|
-
If( colorIndex.greaterThanEqual( 0.286 ).and( colorIndex.lessThan( 0.429 ) ), () => {
|
|
594
|
-
|
|
595
|
-
const t = colorIndex.sub( 0.286 ).div( 0.143 );
|
|
596
|
-
rainbowColor.assign( mix( vec3( 1.0, 0.6, 0.0 ), vec3( 1.0, 1.0, 0.0 ), t ) );
|
|
597
|
-
|
|
598
|
-
} );
|
|
599
|
-
|
|
600
|
-
// Yellow to Green
|
|
601
|
-
If( colorIndex.greaterThanEqual( 0.429 ).and( colorIndex.lessThan( 0.571 ) ), () => {
|
|
602
|
-
|
|
603
|
-
const t = colorIndex.sub( 0.429 ).div( 0.142 );
|
|
604
|
-
rainbowColor.assign( mix( vec3( 1.0, 1.0, 0.0 ), vec3( 0.0, 1.0, 0.0 ), t ) );
|
|
605
|
-
|
|
606
|
-
} );
|
|
607
|
-
|
|
608
|
-
// Green to Blue
|
|
609
|
-
If( colorIndex.greaterThanEqual( 0.571 ).and( colorIndex.lessThan( 0.714 ) ), () => {
|
|
610
|
-
|
|
611
|
-
const t = colorIndex.sub( 0.571 ).div( 0.143 );
|
|
612
|
-
rainbowColor.assign( mix( vec3( 0.0, 1.0, 0.0 ), vec3( 0.0, 0.4, 1.0 ), t ) );
|
|
613
|
-
|
|
614
|
-
} );
|
|
615
|
-
|
|
616
|
-
// Blue to Indigo
|
|
617
|
-
If( colorIndex.greaterThanEqual( 0.714 ).and( colorIndex.lessThan( 0.857 ) ), () => {
|
|
618
|
-
|
|
619
|
-
const t = colorIndex.sub( 0.714 ).div( 0.143 );
|
|
620
|
-
rainbowColor.assign( mix( vec3( 0.0, 0.4, 1.0 ), vec3( 0.3, 0.0, 0.8 ), t ) );
|
|
621
|
-
|
|
622
|
-
} );
|
|
623
|
-
|
|
624
|
-
// Indigo to Violet
|
|
625
|
-
If( colorIndex.greaterThanEqual( 0.857 ), () => {
|
|
626
|
-
|
|
627
|
-
const t = colorIndex.sub( 0.857 ).div( 0.143 );
|
|
628
|
-
rainbowColor.assign( mix( vec3( 0.3, 0.0, 0.8 ), vec3( 0.6, 0.0, 1.0 ), t ) );
|
|
629
|
-
|
|
630
|
-
} );
|
|
631
|
-
|
|
632
|
-
// Calculate dispersion strength with proper variation
|
|
633
|
-
const normalizedDispersion = clamp( material.dispersion.div( 5.0 ), 0.0, 1.0 );
|
|
634
|
-
const angleBoost = float( 1.0 ).add( edgeFactor.mul( 1.5 ) );
|
|
635
|
-
|
|
636
|
-
// Make dispersion visibility more gradual
|
|
637
|
-
const baseVisibility = normalizedDispersion.mul( angleBoost );
|
|
638
|
-
const combinedVariation = spatialVariation.add( refractVariation );
|
|
639
|
-
const spatialMod = float( 0.5 ).add( float( 0.5 ).mul( sin( combinedVariation.mul( 3.14159 ) ) ) );
|
|
640
|
-
const dispersionVisibility = clamp( baseVisibility.mul( spatialMod ), 0.0, 0.8 );
|
|
641
|
-
|
|
642
|
-
// Mix rainbow color with clear base for realistic prism effect
|
|
643
|
-
result.throughput.assign( mix( vec3( 1.0 ), rainbowColor, dispersionVisibility ) );
|
|
644
|
-
|
|
645
|
-
} ).Else( () => {
|
|
646
|
-
|
|
647
|
-
// No dispersion - pure white transmission
|
|
648
|
-
result.throughput.assign( vec3( 1.0 ) );
|
|
649
|
-
|
|
650
|
-
} );
|
|
453
|
+
result.throughput.assign( mtResult.colorWeight );
|
|
651
454
|
|
|
652
455
|
} );
|
|
653
456
|
|
|
@@ -693,10 +496,9 @@ export const handleTransmission = Fn( ( [
|
|
|
693
496
|
|
|
694
497
|
export const handleMaterialTransparency = Fn( ( [
|
|
695
498
|
ray, hitPoint, normal, material, rngState,
|
|
696
|
-
// RenderState fields passed individually (since inout not supported)
|
|
697
499
|
transmissiveTraversals,
|
|
698
|
-
// Precomputed medium IOR values from stack
|
|
699
500
|
currentMediumIOR, previousMediumIOR,
|
|
501
|
+
pathWavelength,
|
|
700
502
|
] ) => {
|
|
701
503
|
|
|
702
504
|
const result = MaterialInteractionResult( {
|
|
@@ -708,6 +510,7 @@ export const handleMaterialTransparency = Fn( ( [
|
|
|
708
510
|
direction: ray.direction,
|
|
709
511
|
throughput: vec3( 1.0 ),
|
|
710
512
|
alpha: float( 1.0 ),
|
|
513
|
+
pathWavelength: pathWavelength,
|
|
711
514
|
} ).toVar();
|
|
712
515
|
|
|
713
516
|
// -----------------------------------------------------------------
|
|
@@ -786,7 +589,7 @@ export const handleMaterialTransparency = Fn( ( [
|
|
|
786
589
|
|
|
787
590
|
const transResult = TransmissionResult.wrap( handleTransmission(
|
|
788
591
|
ray.direction, normal, material, entering, transmissionSeed,
|
|
789
|
-
currentMediumIOR, previousMediumIOR,
|
|
592
|
+
currentMediumIOR, previousMediumIOR, pathWavelength,
|
|
790
593
|
) );
|
|
791
594
|
|
|
792
595
|
result.direction.assign( transResult.direction );
|
|
@@ -796,6 +599,7 @@ export const handleMaterialTransparency = Fn( ( [
|
|
|
796
599
|
result.didReflect.assign( transResult.didReflect );
|
|
797
600
|
result.entering.assign( entering );
|
|
798
601
|
result.alpha.assign( float( 1.0 ).sub( material.transmission ) );
|
|
602
|
+
result.pathWavelength.assign( transResult.pathWavelength );
|
|
799
603
|
|
|
800
604
|
} );
|
|
801
605
|
|
|
@@ -291,8 +291,9 @@ export const generateSampledDirection = Fn( ( [
|
|
|
291
291
|
If( sampled.not(), () => {
|
|
292
292
|
|
|
293
293
|
const entering = dot( V, N ).greaterThan( 0.0 ).toVar();
|
|
294
|
+
// pathWavelength=0 — only direction/PDF are consumed here, throughput goes via handleTransmission
|
|
294
295
|
const mtResult = MicrofacetTransmissionResult.wrap( sampleMicrofacetTransmission(
|
|
295
|
-
V, N, material.ior, material.roughness, entering, material.dispersion, xi, rngState,
|
|
296
|
+
V, N, material.ior, material.roughness, entering, material.dispersion, xi, rngState, float( 0.0 ),
|
|
296
297
|
) );
|
|
297
298
|
resultDirection.assign( mtResult.direction );
|
|
298
299
|
resultPdf.assign( max( mtResult.pdf, MIN_PDF ) );
|
|
@@ -619,6 +620,10 @@ export const Trace = Fn( ( [
|
|
|
619
620
|
const mediumStack_ior_2 = float( 1.0 ).toVar();
|
|
620
621
|
const mediumStack_ior_3 = float( 1.0 ).toVar();
|
|
621
622
|
|
|
623
|
+
// Locked at the first dispersive transmission; reused for subsequent transmissions on
|
|
624
|
+
// the path so multi-bounce dispersion doesn't collapse under repeated colorWeight ×.
|
|
625
|
+
const pathWavelength = float( 0.0 ).toVar();
|
|
626
|
+
|
|
622
627
|
// Render state
|
|
623
628
|
const stateTraversals = maxBounceCount.toVar();
|
|
624
629
|
const stateTransmissiveTraversals = transmissiveBounces.toVar();
|
|
@@ -802,7 +807,9 @@ export const Trace = Fn( ( [
|
|
|
802
807
|
currentRay, hitInfo.hitPoint, N, material, rngState,
|
|
803
808
|
stateTransmissiveTraversals,
|
|
804
809
|
currentMediumIOR, previousMediumIOR,
|
|
810
|
+
pathWavelength,
|
|
805
811
|
) ).toVar();
|
|
812
|
+
pathWavelength.assign( interaction.pathWavelength );
|
|
806
813
|
|
|
807
814
|
If( interaction.continueRay, () => {
|
|
808
815
|
|
package/src/TSL/Random.js
CHANGED
|
@@ -138,21 +138,6 @@ export const RandomValue = ( state ) => {
|
|
|
138
138
|
|
|
139
139
|
};
|
|
140
140
|
|
|
141
|
-
// Generate random float with better precision
|
|
142
|
-
|
|
143
|
-
export const RandomValueHighPrecision = ( state ) => {
|
|
144
|
-
|
|
145
|
-
// Capture s1 immediately (.toVar()) before advancing state, so it isn't
|
|
146
|
-
// re-evaluated lazily after the second pcgHash advances state further.
|
|
147
|
-
const s1 = pcgHash( { state } ).toVar();
|
|
148
|
-
state.assign( s1 );
|
|
149
|
-
const s2 = pcgHash( { state } ).toVar();
|
|
150
|
-
|
|
151
|
-
// Combine two 24-bit values for 48-bit precision
|
|
152
|
-
return float( s1.shiftRight( 8 ) ).add( float( s2.shiftRight( 8 ) ).mul( 1.0 / 16777216.0 ) ).mul( 1.0 / 16777216.0 );
|
|
153
|
-
|
|
154
|
-
};
|
|
155
|
-
|
|
156
141
|
// -----------------------------------------------------------------------------
|
|
157
142
|
// Directional sampling functions
|
|
158
143
|
// -----------------------------------------------------------------------------
|
|
@@ -198,14 +183,6 @@ const computeSTBNAtlasCoord = ( pixelCoords, sampleIndex, dimensionIndex, frame
|
|
|
198
183
|
|
|
199
184
|
};
|
|
200
185
|
|
|
201
|
-
// Sample 1D scalar STBN value in [0,1]
|
|
202
|
-
export const sampleSTBNScalar = ( pixelCoords, sampleIndex, dimensionIndex, frame ) => {
|
|
203
|
-
|
|
204
|
-
const coord = computeSTBNAtlasCoord( pixelCoords, sampleIndex, dimensionIndex, frame );
|
|
205
|
-
return stbnScalarTextureNode.load( coord ).x;
|
|
206
|
-
|
|
207
|
-
};
|
|
208
|
-
|
|
209
186
|
// Sample decorrelated 2D STBN pair in [0,1]²
|
|
210
187
|
export const sampleSTBN2D = ( pixelCoords, sampleIndex, dimensionPairIndex, frame ) => {
|
|
211
188
|
|
package/src/TSL/Struct.js
CHANGED
|
@@ -216,22 +216,6 @@ export const MISStrategy = struct( {
|
|
|
216
216
|
useEnvSampling: 'bool',
|
|
217
217
|
} );
|
|
218
218
|
|
|
219
|
-
// IMPROVEMENT: Multi-layer MIS type aliases and extensions
|
|
220
|
-
// Use existing structs with clear naming for multi-lobe MIS
|
|
221
|
-
// #define MaterialWeights BRDFWeights
|
|
222
|
-
// #define SamplingResult DirectionSample
|
|
223
|
-
|
|
224
|
-
// Enhanced material weights for multi-lobe sampling
|
|
225
|
-
export const MultiLobeWeights = struct( {
|
|
226
|
-
diffuse: 'float',
|
|
227
|
-
specular: 'float',
|
|
228
|
-
clearcoat: 'float',
|
|
229
|
-
transmission: 'float',
|
|
230
|
-
sheen: 'float',
|
|
231
|
-
iridescence: 'float',
|
|
232
|
-
totalWeight: 'float',
|
|
233
|
-
} );
|
|
234
|
-
|
|
235
219
|
// General rendering state (used across all rendering paths)
|
|
236
220
|
export const RenderState = struct( {
|
|
237
221
|
traversals: 'int', // Remaining general bounces
|
|
@@ -241,8 +225,3 @@ export const RenderState = struct( {
|
|
|
241
225
|
actualBounceDepth: 'int', // True depth without manipulation
|
|
242
226
|
} );
|
|
243
227
|
|
|
244
|
-
export const pathTracerOutputStruct = struct( {
|
|
245
|
-
gColor: 'vec4', // RGB + alpha
|
|
246
|
-
gNormalDepth: 'vec4', // Normal(RGB) + depth(A)
|
|
247
|
-
gAlbedo: 'vec4' // Albedo color + alpha
|
|
248
|
-
} );
|
|
@@ -369,75 +369,6 @@ export const sampleAllMaterialTextures = Fn( ( [
|
|
|
369
369
|
|
|
370
370
|
} );
|
|
371
371
|
|
|
372
|
-
// ================================================================================
|
|
373
|
-
// LEGACY SAMPLING FUNCTIONS
|
|
374
|
-
// ================================================================================
|
|
375
|
-
|
|
376
|
-
export const sampleAlbedoTexture = Fn( ( [
|
|
377
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
378
|
-
material, uv
|
|
379
|
-
] ) => {
|
|
380
|
-
|
|
381
|
-
const samples = MaterialSamples.wrap( sampleAllMaterialTextures(
|
|
382
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
383
|
-
material, uv, vec3( 0.0, 1.0, 0.0 )
|
|
384
|
-
) );
|
|
385
|
-
return samples.albedo;
|
|
386
|
-
|
|
387
|
-
} );
|
|
388
|
-
|
|
389
|
-
export const sampleEmissiveMap = Fn( ( [
|
|
390
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
391
|
-
material, uv
|
|
392
|
-
] ) => {
|
|
393
|
-
|
|
394
|
-
const samples = MaterialSamples.wrap( sampleAllMaterialTextures(
|
|
395
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
396
|
-
material, uv, vec3( 0.0, 1.0, 0.0 )
|
|
397
|
-
) );
|
|
398
|
-
return samples.emissive;
|
|
399
|
-
|
|
400
|
-
} );
|
|
401
|
-
|
|
402
|
-
export const sampleMetalnessMap = Fn( ( [
|
|
403
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
404
|
-
material, uv
|
|
405
|
-
] ) => {
|
|
406
|
-
|
|
407
|
-
const samples = MaterialSamples.wrap( sampleAllMaterialTextures(
|
|
408
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
409
|
-
material, uv, vec3( 0.0, 1.0, 0.0 )
|
|
410
|
-
) );
|
|
411
|
-
return samples.metalness;
|
|
412
|
-
|
|
413
|
-
} );
|
|
414
|
-
|
|
415
|
-
export const sampleRoughnessMap = Fn( ( [
|
|
416
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
417
|
-
material, uv
|
|
418
|
-
] ) => {
|
|
419
|
-
|
|
420
|
-
const samples = MaterialSamples.wrap( sampleAllMaterialTextures(
|
|
421
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
422
|
-
material, uv, vec3( 0.0, 1.0, 0.0 )
|
|
423
|
-
) );
|
|
424
|
-
return samples.roughness;
|
|
425
|
-
|
|
426
|
-
} );
|
|
427
|
-
|
|
428
|
-
export const sampleNormalMap = Fn( ( [
|
|
429
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
430
|
-
material, uv, normal
|
|
431
|
-
] ) => {
|
|
432
|
-
|
|
433
|
-
const samples = MaterialSamples.wrap( sampleAllMaterialTextures(
|
|
434
|
-
albedoMaps, normalMaps, bumpMaps, metalnessMaps, roughnessMaps, emissiveMaps,
|
|
435
|
-
material, uv, normal
|
|
436
|
-
) );
|
|
437
|
-
return samples.normal;
|
|
438
|
-
|
|
439
|
-
} );
|
|
440
|
-
|
|
441
372
|
// Sample displacement map at given UV coordinates
|
|
442
373
|
export const sampleDisplacementMap = Fn( ( [ displacementMaps, displacementMapIndex, uv, transform ] ) => {
|
|
443
374
|
|