rayzee 5.10.2 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -22
- package/dist/assets/AIUpscalerWorker-AXN-lKWN.js +2 -0
- package/dist/assets/AIUpscalerWorker-AXN-lKWN.js.map +1 -0
- package/dist/rayzee.es.js +1299 -1843
- 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 +59 -63
- package/src/Processor/AssetLoader.js +5 -2
- package/src/Processor/TextureCreator.js +42 -15
- 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 +2 -18
- package/src/TSL/Clearcoat.js +1 -2
- package/src/TSL/Common.js +0 -13
- package/src/TSL/EmissiveSampling.js +0 -16
- package/src/TSL/Environment.js +0 -7
- package/src/TSL/LightsDirect.js +3 -379
- 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 +0 -93
- 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/OverlayManager.js +14 -2
- 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
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Fn, float, vec3,
|
|
3
|
-
If, max, min,
|
|
2
|
+
Fn, float, vec3,
|
|
3
|
+
If, max, min, clamp, mix
|
|
4
4
|
} from 'three/tsl';
|
|
5
5
|
|
|
6
|
-
import { DotProducts
|
|
6
|
+
import { DotProducts } from './Struct.js';
|
|
7
7
|
import {
|
|
8
8
|
PI, PI_INV, EPSILON, MIN_CLEARCOAT_ROUGHNESS,
|
|
9
9
|
computeDotProducts,
|
|
@@ -118,110 +118,10 @@ export const evaluateMaterialResponse = Fn( ( [ V, L, N, material ] ) => {
|
|
|
118
118
|
|
|
119
119
|
} );
|
|
120
120
|
|
|
121
|
-
// -----------------------------------------------------------------------------
|
|
122
|
-
// Cached Material Response Evaluation (Optimized)
|
|
123
|
-
// -----------------------------------------------------------------------------
|
|
124
|
-
|
|
125
|
-
export const evaluateMaterialResponseCached = Fn( ( [ V, L, N, material, cache ] ) => {
|
|
126
|
-
|
|
127
|
-
const result = vec3( 0.0 ).toVar();
|
|
128
|
-
|
|
129
|
-
If( cache.isPurelyDiffuse, () => {
|
|
130
|
-
|
|
131
|
-
result.assign( cache.diffuseColor );
|
|
132
|
-
|
|
133
|
-
} ).Else( () => {
|
|
134
|
-
|
|
135
|
-
const H = V.add( L ).toVar();
|
|
136
|
-
const lenSq = dot( H, H );
|
|
137
|
-
H.assign( lenSq.greaterThan( EPSILON ).select( H.div( sqrt( lenSq ) ), vec3( 0.0, 0.0, 1.0 ) ) );
|
|
138
|
-
const NoL = max( dot( N, L ), EPSILON );
|
|
139
|
-
const NoH = max( dot( N, H ), EPSILON );
|
|
140
|
-
const VoH = max( dot( V, H ), EPSILON );
|
|
141
|
-
|
|
142
|
-
const isTransmission = cache.NoV.mul( NoL ).lessThan( 0.0 );
|
|
143
|
-
If( isTransmission.and( material.transmission.greaterThan( 0.0 ) ), () => {
|
|
144
|
-
|
|
145
|
-
result.assign( evaluateMaterialResponse( V, L, N, material ) );
|
|
146
|
-
|
|
147
|
-
} ).Else( () => {
|
|
148
|
-
|
|
149
|
-
const F0 = cache.F0.toVar();
|
|
150
|
-
|
|
151
|
-
// Iridescence
|
|
152
|
-
If( material.iridescence.greaterThan( 0.0 ), () => {
|
|
153
|
-
|
|
154
|
-
// Per glTF KHR_materials_iridescence spec: use max thickness when no texture
|
|
155
|
-
const thickness = material.iridescenceThicknessRange.y;
|
|
156
|
-
const iridescenceFresnel = evalIridescence( float( 1.0 ), material.iridescenceIOR, VoH, thickness, F0 );
|
|
157
|
-
F0.assign( clamp( mix( F0, iridescenceFresnel, material.iridescence ), vec3( 0.0 ), vec3( 1.0 ) ) );
|
|
158
|
-
|
|
159
|
-
} );
|
|
160
|
-
|
|
161
|
-
// Use precomputed values
|
|
162
|
-
const denom = NoH.mul( NoH ).mul( cache.alpha2.sub( 1.0 ) ).add( 1.0 );
|
|
163
|
-
const D = cache.alpha2.div( max( float( PI ).mul( denom ).mul( denom ), EPSILON ) );
|
|
164
|
-
|
|
165
|
-
const ggx1 = NoL.div( NoL.mul( float( 1.0 ).sub( cache.k ) ).add( cache.k ) );
|
|
166
|
-
const ggx2 = cache.NoV.div( cache.NoV.mul( float( 1.0 ).sub( cache.k ) ).add( cache.k ) );
|
|
167
|
-
const G = ggx1.mul( ggx2 );
|
|
168
|
-
|
|
169
|
-
const F = fresnelSchlick( VoH, F0 ).toVar();
|
|
170
|
-
|
|
171
|
-
// Single-scatter specular BRDF
|
|
172
|
-
const specularDenom = max( float( 4.0 ).mul( cache.NoV ).mul( NoL ), EPSILON );
|
|
173
|
-
const specularSS = D.mul( G ).mul( F ).div( specularDenom );
|
|
174
|
-
|
|
175
|
-
// Kulla-Conty multiscatter energy compensation for rough surfaces
|
|
176
|
-
const specular = specularSS.mul( multiscatterCompensation( F0, cache.NoV, material.roughness ) );
|
|
177
|
-
// Diffuse energy budget from hemisphere-integrated specular albedo (includes multiscatter)
|
|
178
|
-
const E_total = specularDirectionalAlbedo( F0, cache.NoV, material.roughness );
|
|
179
|
-
const kD = vec3( 1.0 ).sub( E_total ).mul( float( 1.0 ).sub( material.metalness ) );
|
|
180
|
-
const diffuse = kD.mul( material.color.rgb ).mul( PI_INV );
|
|
181
|
-
|
|
182
|
-
const baseLayer = diffuse.add( specular ).toVar();
|
|
183
|
-
|
|
184
|
-
// Sheen
|
|
185
|
-
If( material.sheen.greaterThan( 0.0 ), () => {
|
|
186
|
-
|
|
187
|
-
const sheenDist = SheenDistribution( NoH, material.sheenRoughness );
|
|
188
|
-
const sheenTerm = material.sheenColor.mul( material.sheen ).mul( sheenDist ).mul( NoL );
|
|
189
|
-
// Hemisphere-averaged sheen reflectance for energy-conserving base layer attenuation
|
|
190
|
-
const avgSheenFactor = float( 1.0 ).sub( material.sheenRoughness ).mul( 0.5 ).add( 0.25 );
|
|
191
|
-
const sheenReflectance = clamp( material.sheenColor.mul( material.sheen ).mul( avgSheenFactor ), vec3( 0.0 ), vec3( 1.0 ) );
|
|
192
|
-
const sheenAttenuation = vec3( 1.0 ).sub( sheenReflectance );
|
|
193
|
-
|
|
194
|
-
result.assign( baseLayer.mul( sheenAttenuation ).add( sheenTerm ) );
|
|
195
|
-
|
|
196
|
-
} ).Else( () => {
|
|
197
|
-
|
|
198
|
-
result.assign( baseLayer );
|
|
199
|
-
|
|
200
|
-
} );
|
|
201
|
-
|
|
202
|
-
} );
|
|
203
|
-
|
|
204
|
-
} );
|
|
205
|
-
|
|
206
|
-
return result;
|
|
207
|
-
|
|
208
|
-
} );
|
|
209
|
-
|
|
210
121
|
// -----------------------------------------------------------------------------
|
|
211
122
|
// Layered BRDF Evaluation (for clearcoat)
|
|
212
123
|
// -----------------------------------------------------------------------------
|
|
213
124
|
|
|
214
|
-
// Helper function to calculate energy conservation for layered materials
|
|
215
|
-
export const calculateLayerAttenuation = Fn( ( [ clearcoat, VoH ] ) => {
|
|
216
|
-
|
|
217
|
-
// Fresnel term for clearcoat layer (using f0 = 0.04 for dielectric)
|
|
218
|
-
const F = fresnelSchlickFloat( VoH, float( 0.04 ) );
|
|
219
|
-
// Two-interface clearcoat attenuation: (1-F)² blended by clearcoat strength
|
|
220
|
-
// = 1 - clearcoat * F * (2 - F)
|
|
221
|
-
return float( 1.0 ).sub( clearcoat.mul( F ).mul( float( 2.0 ).sub( F ) ) );
|
|
222
|
-
|
|
223
|
-
} );
|
|
224
|
-
|
|
225
125
|
// Evaluate both clearcoat and base layer BRDFs
|
|
226
126
|
export const evaluateLayeredBRDF = Fn( ( [ dots, material ] ) => {
|
|
227
127
|
|
|
@@ -324,64 +324,9 @@ export const calculateBRDFWeights = Fn( ( [ material, mc, cache ] ) => {
|
|
|
324
324
|
} );
|
|
325
325
|
|
|
326
326
|
// -----------------------------------------------------------------------------
|
|
327
|
-
//
|
|
327
|
+
// Importance Sampling Info
|
|
328
328
|
// -----------------------------------------------------------------------------
|
|
329
329
|
|
|
330
|
-
export const getMaterialImportance = Fn( ( [ material, mc ] ) => {
|
|
331
|
-
|
|
332
|
-
const result = float( 0.0 ).toVar();
|
|
333
|
-
|
|
334
|
-
// Early out for specialized materials
|
|
335
|
-
If( material.transmission.greaterThan( 0.0 ).or( material.clearcoat.greaterThan( 0.0 ) ), () => {
|
|
336
|
-
|
|
337
|
-
result.assign( 0.95 );
|
|
338
|
-
|
|
339
|
-
} ).Else( () => {
|
|
340
|
-
|
|
341
|
-
// Base importance from complexity score
|
|
342
|
-
const baseImportance = mc.complexityScore.toVar();
|
|
343
|
-
|
|
344
|
-
// Enhanced emissive importance
|
|
345
|
-
const emissiveImportance = float( 0.0 ).toVar();
|
|
346
|
-
If( mc.isEmissive, () => {
|
|
347
|
-
|
|
348
|
-
const emissiveLuminance = dot( material.emissive, REC709_LUMINANCE_COEFFICIENTS );
|
|
349
|
-
emissiveImportance.assign( min( float( 0.6 ), emissiveLuminance.mul( material.emissiveIntensity ).mul( 0.25 ) ) );
|
|
350
|
-
|
|
351
|
-
} );
|
|
352
|
-
|
|
353
|
-
// Material-specific boosts
|
|
354
|
-
const materialBoost = float( 0.0 ).toVar();
|
|
355
|
-
If( mc.isMetallic.and( mc.isSmooth ), () => {
|
|
356
|
-
|
|
357
|
-
materialBoost.addAssign( 0.25 );
|
|
358
|
-
|
|
359
|
-
} ).ElseIf( mc.isMetallic, () => {
|
|
360
|
-
|
|
361
|
-
materialBoost.addAssign( 0.15 );
|
|
362
|
-
|
|
363
|
-
} );
|
|
364
|
-
|
|
365
|
-
If( mc.isTransmissive, () => {
|
|
366
|
-
|
|
367
|
-
materialBoost.addAssign( 0.2 );
|
|
368
|
-
|
|
369
|
-
} );
|
|
370
|
-
If( mc.hasClearcoat, () => {
|
|
371
|
-
|
|
372
|
-
materialBoost.addAssign( 0.1 );
|
|
373
|
-
|
|
374
|
-
} );
|
|
375
|
-
|
|
376
|
-
const totalImportance = max( baseImportance.add( materialBoost ), emissiveImportance );
|
|
377
|
-
result.assign( clamp( totalImportance, 0.0, 1.0 ) );
|
|
378
|
-
|
|
379
|
-
} );
|
|
380
|
-
|
|
381
|
-
return result;
|
|
382
|
-
|
|
383
|
-
} );
|
|
384
|
-
|
|
385
330
|
export const getImportanceSamplingInfo = Fn( ( [
|
|
386
331
|
material, bounceIndex, mc,
|
|
387
332
|
environmentIntensity, useEnvMapIS, enableEnvironmentLight
|
|
@@ -1,24 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Fn, wgslFn, float, vec3, If, max, min, abs, normalize, reflect, refract, dot, pow
|
|
3
|
-
} from 'three/tsl';
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
MultiLobeWeights, DirectionSample, MaterialCache, MaterialClassification
|
|
7
|
-
} from './Struct.js';
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
PI, PI_INV, MIN_PDF, classifyMaterial,
|
|
11
|
-
} from './Common.js';
|
|
12
|
-
import { dielectricF0 } from './Fresnel.js';
|
|
13
|
-
|
|
14
|
-
import { calculateBRDFWeights, calculateGGXPDF } from './MaterialProperties.js';
|
|
15
|
-
import { RandomValue } from './Random.js';
|
|
1
|
+
import { wgslFn } from 'three/tsl';
|
|
16
2
|
|
|
17
3
|
// =============================================================================
|
|
18
4
|
// MATERIAL SAMPLING
|
|
19
5
|
// =============================================================================
|
|
20
|
-
//
|
|
21
|
-
// including GGX, cosine-weighted hemisphere, VNDF, and multi-lobe MIS.
|
|
6
|
+
// Importance sampling primitives: GGX, cosine-weighted hemisphere, VNDF.
|
|
22
7
|
|
|
23
8
|
// -----------------------------------------------------------------------------
|
|
24
9
|
// Basic Sampling Functions
|
|
@@ -63,12 +48,6 @@ export const cosineWeightedSample = /*@__PURE__*/ wgslFn( `
|
|
|
63
48
|
}
|
|
64
49
|
` );
|
|
65
50
|
|
|
66
|
-
export const cosineWeightedPDF = Fn( ( [ NoL ] ) => {
|
|
67
|
-
|
|
68
|
-
return max( NoL, MIN_PDF ).mul( PI_INV );
|
|
69
|
-
|
|
70
|
-
} );
|
|
71
|
-
|
|
72
51
|
// -----------------------------------------------------------------------------
|
|
73
52
|
// VNDF Sampling (Visible Normal Distribution Function)
|
|
74
53
|
// -----------------------------------------------------------------------------
|
|
@@ -95,264 +74,3 @@ export const sampleGGXVNDF = /*@__PURE__*/ wgslFn( `
|
|
|
95
74
|
return normalize( vec3f( alpha * Nh.x, alpha * Nh.y, max( 0.0f, Nh.z ) ) );
|
|
96
75
|
}
|
|
97
76
|
` );
|
|
98
|
-
|
|
99
|
-
// -----------------------------------------------------------------------------
|
|
100
|
-
// Multi-Lobe MIS Sampling
|
|
101
|
-
// -----------------------------------------------------------------------------
|
|
102
|
-
|
|
103
|
-
// Enhanced sampling weights calculation for multi-lobe MIS
|
|
104
|
-
export const calculateSamplingWeights = Fn( ( [ V, N, material ] ) => {
|
|
105
|
-
|
|
106
|
-
// Get material classification for optimized calculations
|
|
107
|
-
const mc = MaterialClassification.wrap( classifyMaterial( material ) );
|
|
108
|
-
|
|
109
|
-
// Create temporary cache values
|
|
110
|
-
const tempInvRoughness = float( 1.0 ).sub( material.roughness );
|
|
111
|
-
const tempMetalFactor = float( 0.5 ).add( float( 0.5 ).mul( material.metalness ) );
|
|
112
|
-
const tempIorFactor = min( float( 2.0 ).div( material.ior ), 1.0 );
|
|
113
|
-
const tempMaxSheenColor = max( material.sheenColor.r, max( material.sheenColor.g, material.sheenColor.b ) );
|
|
114
|
-
|
|
115
|
-
// Create temporary cache for calculations
|
|
116
|
-
const tempCache = MaterialCache( {
|
|
117
|
-
F0: dielectricF0( material.ior ),
|
|
118
|
-
NoV: float( 0.5 ),
|
|
119
|
-
diffuseColor: material.color.rgb,
|
|
120
|
-
isPurelyDiffuse: false,
|
|
121
|
-
alpha: material.roughness.mul( material.roughness ),
|
|
122
|
-
k: material.roughness.add( 1.0 ).mul( material.roughness.add( 1.0 ) ).div( 8.0 ),
|
|
123
|
-
alpha2: material.roughness.mul( material.roughness ).mul( material.roughness ).mul( material.roughness ),
|
|
124
|
-
invRoughness: tempInvRoughness,
|
|
125
|
-
metalFactor: tempMetalFactor,
|
|
126
|
-
iorFactor: tempIorFactor,
|
|
127
|
-
maxSheenColor: tempMaxSheenColor,
|
|
128
|
-
} );
|
|
129
|
-
|
|
130
|
-
// Calculate base BRDF weights
|
|
131
|
-
const brdfWeights = BRDFWeights.wrap( calculateBRDFWeights( material, mc, tempCache ) );
|
|
132
|
-
|
|
133
|
-
// Calculate view-dependent factors
|
|
134
|
-
const NoV = max( dot( N, V ), 0.0 );
|
|
135
|
-
const fresnelFactor = pow( float( 1.0 ).sub( NoV ), 5.0 );
|
|
136
|
-
|
|
137
|
-
// Energy-conserving Fresnel redistribution: energy lost by diffuse transfers to specular
|
|
138
|
-
// This preserves the total (diffuse + specular) weight while shifting toward specular at grazing angles
|
|
139
|
-
const fresnelTransfer = brdfWeights.diffuse.mul( fresnelFactor );
|
|
140
|
-
const diffuse = brdfWeights.diffuse.sub( fresnelTransfer ).toVar();
|
|
141
|
-
const specular = brdfWeights.specular.add( fresnelTransfer ).toVar();
|
|
142
|
-
|
|
143
|
-
// Other lobes remain unchanged — no artificial inflation
|
|
144
|
-
const clearcoat = brdfWeights.clearcoat.toVar();
|
|
145
|
-
const transmission = brdfWeights.transmission.mul( tempIorFactor ).toVar();
|
|
146
|
-
const sheen = brdfWeights.sheen.toVar();
|
|
147
|
-
const iridescence = brdfWeights.iridescence.toVar();
|
|
148
|
-
|
|
149
|
-
// Calculate total weight for normalization
|
|
150
|
-
const totalWeight = max(
|
|
151
|
-
diffuse.add( specular ).add( clearcoat ).add( transmission ).add( sheen ).add( iridescence ),
|
|
152
|
-
1e-6
|
|
153
|
-
).toVar();
|
|
154
|
-
|
|
155
|
-
// Normalize weights
|
|
156
|
-
const invTotal = float( 1.0 ).div( totalWeight );
|
|
157
|
-
|
|
158
|
-
return MultiLobeWeights( {
|
|
159
|
-
diffuse: diffuse.mul( invTotal ),
|
|
160
|
-
specular: specular.mul( invTotal ),
|
|
161
|
-
clearcoat: clearcoat.mul( invTotal ),
|
|
162
|
-
transmission: transmission.mul( invTotal ),
|
|
163
|
-
sheen: sheen.mul( invTotal ),
|
|
164
|
-
iridescence: iridescence.mul( invTotal ),
|
|
165
|
-
totalWeight,
|
|
166
|
-
} );
|
|
167
|
-
|
|
168
|
-
} );
|
|
169
|
-
|
|
170
|
-
// Calculate MIS weight considering all possible sampling strategies
|
|
171
|
-
export const calculateMultiLobeMISWeight = Fn( ( [
|
|
172
|
-
sampledDirection, V, N, material, weights, selectedPdf
|
|
173
|
-
] ) => {
|
|
174
|
-
|
|
175
|
-
// Calculate PDFs for all possible sampling strategies
|
|
176
|
-
const diffusePdf = float( 0.0 ).toVar();
|
|
177
|
-
const specularPdf = float( 0.0 ).toVar();
|
|
178
|
-
const clearcoatPdf = float( 0.0 ).toVar();
|
|
179
|
-
const transmissionPdf = float( 0.0 ).toVar();
|
|
180
|
-
const sheenPdf = float( 0.0 ).toVar();
|
|
181
|
-
|
|
182
|
-
const NoL = dot( N, sampledDirection );
|
|
183
|
-
|
|
184
|
-
// Diffuse PDF
|
|
185
|
-
If( NoL.greaterThan( 0.0 ), () => {
|
|
186
|
-
|
|
187
|
-
diffusePdf.assign( NoL.div( PI ) );
|
|
188
|
-
|
|
189
|
-
} );
|
|
190
|
-
|
|
191
|
-
// Specular PDF
|
|
192
|
-
const H = normalize( V.add( sampledDirection ) ).toVar();
|
|
193
|
-
const NoH = max( dot( N, H ), 0.0 );
|
|
194
|
-
const VoH = max( dot( V, H ), 0.0 );
|
|
195
|
-
const NoV = max( dot( N, V ), 0.0 );
|
|
196
|
-
|
|
197
|
-
If( NoH.greaterThan( 0.0 ).and( VoH.greaterThan( 0.0 ) ).and( NoV.greaterThan( 0.0 ) ), () => {
|
|
198
|
-
|
|
199
|
-
specularPdf.assign( calculateGGXPDF( NoH, VoH, material.roughness ) );
|
|
200
|
-
|
|
201
|
-
// Clearcoat PDF (using clearcoat roughness)
|
|
202
|
-
If( material.clearcoat.greaterThan( 0.0 ), () => {
|
|
203
|
-
|
|
204
|
-
clearcoatPdf.assign( calculateGGXPDF( NoH, VoH, material.clearcoatRoughness ) );
|
|
205
|
-
|
|
206
|
-
} );
|
|
207
|
-
|
|
208
|
-
} );
|
|
209
|
-
|
|
210
|
-
// Transmission PDF (simplified)
|
|
211
|
-
If( material.transmission.greaterThan( 0.0 ).and( NoL.lessThan( 0.0 ) ), () => {
|
|
212
|
-
|
|
213
|
-
// For transmission, we're sampling the opposite hemisphere
|
|
214
|
-
transmissionPdf.assign( abs( NoL ).div( PI ) );
|
|
215
|
-
|
|
216
|
-
} );
|
|
217
|
-
|
|
218
|
-
// Sheen PDF (approximated as diffuse)
|
|
219
|
-
If( material.sheen.greaterThan( 0.0 ).and( NoL.greaterThan( 0.0 ) ), () => {
|
|
220
|
-
|
|
221
|
-
sheenPdf.assign( NoL.div( PI ) );
|
|
222
|
-
|
|
223
|
-
} );
|
|
224
|
-
|
|
225
|
-
// Calculate weighted PDFs for each lobe
|
|
226
|
-
const weightedDiffusePdf = weights.diffuse.mul( diffusePdf );
|
|
227
|
-
const weightedSpecularPdf = weights.specular.mul( specularPdf );
|
|
228
|
-
const weightedClearcoatPdf = weights.clearcoat.mul( clearcoatPdf );
|
|
229
|
-
const weightedTransmissionPdf = weights.transmission.mul( transmissionPdf );
|
|
230
|
-
const weightedSheenPdf = weights.sheen.mul( sheenPdf );
|
|
231
|
-
const weightedIridescencePdf = weights.iridescence.mul( diffusePdf );
|
|
232
|
-
|
|
233
|
-
// Power heuristic (β=2): sum of squared weighted PDFs
|
|
234
|
-
const sumSquaredPdfs = weightedDiffusePdf.mul( weightedDiffusePdf )
|
|
235
|
-
.add( weightedSpecularPdf.mul( weightedSpecularPdf ) )
|
|
236
|
-
.add( weightedClearcoatPdf.mul( weightedClearcoatPdf ) )
|
|
237
|
-
.add( weightedTransmissionPdf.mul( weightedTransmissionPdf ) )
|
|
238
|
-
.add( weightedSheenPdf.mul( weightedSheenPdf ) )
|
|
239
|
-
.add( weightedIridescencePdf.mul( weightedIridescencePdf ) );
|
|
240
|
-
|
|
241
|
-
// MIS weight: selectedPdf² / Σ(pdf_i²)
|
|
242
|
-
const misWeight = float( 1.0 ).toVar();
|
|
243
|
-
|
|
244
|
-
If( sumSquaredPdfs.greaterThan( 0.0 ).and( selectedPdf.greaterThan( 0.0 ) ), () => {
|
|
245
|
-
|
|
246
|
-
const selectedPdfSquared = selectedPdf.mul( selectedPdf );
|
|
247
|
-
misWeight.assign( selectedPdfSquared.div( sumSquaredPdfs ) );
|
|
248
|
-
|
|
249
|
-
} );
|
|
250
|
-
|
|
251
|
-
return misWeight;
|
|
252
|
-
|
|
253
|
-
} );
|
|
254
|
-
|
|
255
|
-
// Multi-lobe MIS for complex materials
|
|
256
|
-
// Note: evaluateMaterialResponse is imported from MaterialEvaluation.js at usage site
|
|
257
|
-
export const sampleMaterialWithMultiLobeMIS = Fn( ( [
|
|
258
|
-
V, N, material, xi, rngState, evaluateMaterialResponse
|
|
259
|
-
] ) => {
|
|
260
|
-
|
|
261
|
-
// Calculate individual lobe weights
|
|
262
|
-
const weights = calculateSamplingWeights( V, N, material );
|
|
263
|
-
|
|
264
|
-
// Multi-importance sampling across different lobes
|
|
265
|
-
const rand = RandomValue( rngState );
|
|
266
|
-
const cumulativeDiffuse = weights.diffuse;
|
|
267
|
-
const cumulativeSpecular = cumulativeDiffuse.add( weights.specular );
|
|
268
|
-
const cumulativeClearcoat = cumulativeSpecular.add( weights.clearcoat );
|
|
269
|
-
const cumulativeTransmission = cumulativeClearcoat.add( weights.transmission );
|
|
270
|
-
|
|
271
|
-
const sampledDirection = vec3( 0.0 ).toVar();
|
|
272
|
-
const lobePdf = float( 0.0 ).toVar();
|
|
273
|
-
const resultPdf = float( 0.0 ).toVar();
|
|
274
|
-
|
|
275
|
-
If( rand.lessThan( cumulativeDiffuse ), () => {
|
|
276
|
-
|
|
277
|
-
// Diffuse sampling
|
|
278
|
-
sampledDirection.assign( ImportanceSampleCosine( { N, xi } ) );
|
|
279
|
-
lobePdf.assign( max( dot( N, sampledDirection ), 0.0 ).div( PI ) );
|
|
280
|
-
resultPdf.assign( lobePdf.mul( weights.diffuse ) );
|
|
281
|
-
|
|
282
|
-
} ).ElseIf( rand.lessThan( cumulativeSpecular ), () => {
|
|
283
|
-
|
|
284
|
-
// Specular sampling
|
|
285
|
-
const H = ImportanceSampleGGX( { N, roughness: material.roughness, Xi: xi } ).toVar();
|
|
286
|
-
sampledDirection.assign( reflect( V.negate(), H ) );
|
|
287
|
-
|
|
288
|
-
If( dot( N, sampledDirection ).greaterThan( 0.0 ), () => {
|
|
289
|
-
|
|
290
|
-
const NoH = max( dot( N, H ), 0.0 );
|
|
291
|
-
const VoH = max( dot( V, H ), 0.0 );
|
|
292
|
-
lobePdf.assign( calculateGGXPDF( NoH, VoH, material.roughness ) );
|
|
293
|
-
|
|
294
|
-
} );
|
|
295
|
-
|
|
296
|
-
resultPdf.assign( lobePdf.mul( weights.specular ) );
|
|
297
|
-
|
|
298
|
-
} ).ElseIf( rand.lessThan( cumulativeClearcoat ).and( material.clearcoat.greaterThan( 0.0 ) ), () => {
|
|
299
|
-
|
|
300
|
-
// Clearcoat sampling
|
|
301
|
-
const H = ImportanceSampleGGX( { N, roughness: material.clearcoatRoughness, Xi: xi } ).toVar();
|
|
302
|
-
sampledDirection.assign( reflect( V.negate(), H ) );
|
|
303
|
-
|
|
304
|
-
If( dot( N, sampledDirection ).greaterThan( 0.0 ), () => {
|
|
305
|
-
|
|
306
|
-
const NoH = max( dot( N, H ), 0.0 );
|
|
307
|
-
const VoH = max( dot( V, H ), 0.0 );
|
|
308
|
-
lobePdf.assign( calculateGGXPDF( NoH, VoH, material.clearcoatRoughness ) );
|
|
309
|
-
|
|
310
|
-
} );
|
|
311
|
-
|
|
312
|
-
resultPdf.assign( lobePdf.mul( weights.clearcoat ) );
|
|
313
|
-
|
|
314
|
-
} ).ElseIf( rand.lessThan( cumulativeTransmission ).and( material.transmission.greaterThan( 0.0 ) ), () => {
|
|
315
|
-
|
|
316
|
-
// Transmission sampling - simplified approach
|
|
317
|
-
const H = ImportanceSampleGGX( { N, roughness: material.roughness, Xi: xi } ).toVar();
|
|
318
|
-
const refractionDir = refract( V.negate(), H, float( 1.0 ).div( material.ior ) ).toVar();
|
|
319
|
-
|
|
320
|
-
If( dot( refractionDir, refractionDir ).greaterThan( 0.001 ), () => {
|
|
321
|
-
|
|
322
|
-
sampledDirection.assign( normalize( refractionDir ) );
|
|
323
|
-
const NoH = max( dot( N, H ), 0.0 );
|
|
324
|
-
const VoH = max( dot( V, H ), 0.0 );
|
|
325
|
-
lobePdf.assign( calculateGGXPDF( NoH, VoH, material.roughness ) );
|
|
326
|
-
|
|
327
|
-
} ).Else( () => {
|
|
328
|
-
|
|
329
|
-
// Total internal reflection - fallback to specular
|
|
330
|
-
sampledDirection.assign( reflect( V.negate(), H ) );
|
|
331
|
-
lobePdf.assign( 0.1 );
|
|
332
|
-
|
|
333
|
-
} );
|
|
334
|
-
|
|
335
|
-
resultPdf.assign( lobePdf.mul( weights.transmission ) );
|
|
336
|
-
|
|
337
|
-
} ).Else( () => {
|
|
338
|
-
|
|
339
|
-
// Fallback to diffuse sampling for sheen/iridescence
|
|
340
|
-
sampledDirection.assign( ImportanceSampleCosine( { N, xi } ) );
|
|
341
|
-
lobePdf.assign( max( dot( N, sampledDirection ), 0.0 ).div( PI ) );
|
|
342
|
-
resultPdf.assign( lobePdf.mul( weights.sheen.add( weights.iridescence ) ) );
|
|
343
|
-
|
|
344
|
-
} );
|
|
345
|
-
|
|
346
|
-
// Calculate MIS weight considering all possible sampling strategies
|
|
347
|
-
const misWeight = calculateMultiLobeMISWeight( sampledDirection, V, N, material, weights, resultPdf );
|
|
348
|
-
|
|
349
|
-
const resultValue = evaluateMaterialResponse( V, sampledDirection, N, material ).toVar();
|
|
350
|
-
resultValue.mulAssign( misWeight );
|
|
351
|
-
|
|
352
|
-
return DirectionSample( {
|
|
353
|
-
direction: sampledDirection,
|
|
354
|
-
value: resultValue,
|
|
355
|
-
pdf: resultPdf,
|
|
356
|
-
} );
|
|
357
|
-
|
|
358
|
-
} );
|
|
@@ -103,103 +103,10 @@ export const MediumStack = struct( {
|
|
|
103
103
|
depth: 'int',
|
|
104
104
|
} );
|
|
105
105
|
|
|
106
|
-
// ================================================================================
|
|
107
|
-
// MEDIUM STACK HELPERS
|
|
108
|
-
// ================================================================================
|
|
109
|
-
|
|
110
|
-
export const getCurrentMediumIOR = Fn( ( [ mediumStack ] ) => {
|
|
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 ) ), () => {
|
|
128
|
-
|
|
129
|
-
result.assign( mediumStack.m3_ior );
|
|
130
|
-
|
|
131
|
-
} );
|
|
132
|
-
|
|
133
|
-
} );
|
|
134
|
-
|
|
135
|
-
return result;
|
|
136
|
-
|
|
137
|
-
} );
|
|
138
|
-
|
|
139
106
|
// ================================================================================
|
|
140
107
|
// DISPERSION
|
|
141
108
|
// ================================================================================
|
|
142
109
|
|
|
143
|
-
// Calculate wavelength-dependent IOR for dispersion using Cauchy dispersion equation
|
|
144
|
-
export const calculateDispersiveIOR = /*@__PURE__*/ wgslFn( `
|
|
145
|
-
fn calculateDispersiveIOR( baseIOR: f32, dispersionStrength: f32 ) -> vec3f {
|
|
146
|
-
let B = dispersionStrength * 0.03f;
|
|
147
|
-
// Standard CIE wavelengths for RGB (in micrometers)
|
|
148
|
-
let wavelengths = vec3f( 0.7000f, 0.5461f, 0.4358f );
|
|
149
|
-
// Apply Cauchy's equation: n(λ) = A + B/λ²
|
|
150
|
-
let dispersiveIOR = baseIOR + B / ( wavelengths * wavelengths );
|
|
151
|
-
return max( dispersiveIOR, vec3f( 1.001f ) );
|
|
152
|
-
}
|
|
153
|
-
` );
|
|
154
|
-
|
|
155
|
-
// Convert wavelength to RGB using spectral sensitivity curves
|
|
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
|
-
` );
|
|
202
|
-
|
|
203
110
|
// Enhanced spectral sampling for realistic dispersion
|
|
204
111
|
export const sampleWavelengthForDispersion = Fn( ( [ baseIOR, dispersionStrength, random ] ) => {
|
|
205
112
|
|
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
|
-
} );
|