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
|
@@ -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
|
-
} );
|