rayzee 7.20.2 → 7.21.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/dist/rayzee.es.js +3855 -3065
- package/dist/rayzee.umd.js +83 -109
- package/package.json +1 -1
- package/src/EngineDefaults.js +4 -3
- package/src/PathTracerApp.js +7 -0
- package/src/Pipeline/BindingAudit.js +178 -0
- package/src/Pipeline/RenderPipeline.js +5 -2
- package/src/Processor/CameraOptimizer.js +0 -5
- package/src/Processor/PackedRayBuffer.js +33 -8
- package/src/Stages/ASVGF.js +10 -10
- package/src/Stages/BilateralFilter.js +70 -11
- package/src/Stages/EdgeFilter.js +44 -6
- package/src/Stages/NormalDepth.js +10 -2
- package/src/Stages/PathTracer.js +7 -3
- package/src/Stages/PathTracerStage.js +15 -46
- package/src/TSL/Clearcoat.js +29 -40
- package/src/TSL/Common.js +1 -47
- package/src/TSL/EmissiveSampling.js +47 -50
- package/src/TSL/LightBVHSampling.js +16 -12
- package/src/TSL/LightsCore.js +3 -2
- package/src/TSL/LightsDirect.js +4 -4
- package/src/TSL/LightsIndirect.js +36 -355
- package/src/TSL/LightsSampling.js +153 -386
- package/src/TSL/MaterialEvaluation.js +44 -71
- package/src/TSL/MaterialProperties.js +209 -176
- package/src/TSL/MaterialTransmission.js +17 -32
- package/src/TSL/PathTracerCore.js +51 -66
- package/src/TSL/Random.js +202 -132
- package/src/TSL/ShadeKernel.js +89 -56
- package/src/TSL/Struct.js +6 -38
- package/src/TSL/Subsurface.js +13 -8
- package/src/index.js +5 -0
- package/src/managers/EnvironmentManager.js +0 -5
- package/src/managers/UniformManager.js +4 -1
|
@@ -1,22 +1,11 @@
|
|
|
1
|
-
import { Fn, float, vec2, vec3,
|
|
1
|
+
import { Fn, texture, float, vec2, vec3, If, dot, max, min, sqrt, cos, exp, mix, clamp, smoothstep } from 'three/tsl';
|
|
2
|
+
import { ClampToEdgeWrapping, DataTexture, FloatType, LinearFilter, RGBAFormat } from 'three';
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
BRDFWeights,
|
|
5
|
-
MaterialCache,
|
|
6
|
-
ImportanceSamplingInfo,
|
|
7
|
-
DFGResult,
|
|
8
|
-
DotProducts,
|
|
4
|
+
import { BRDFWeights, MaterialCache, MaterialClassification, DFGResult } from './Struct.js';
|
|
9
5
|
|
|
10
|
-
} from './
|
|
6
|
+
import { PI, PI_INV, TWO_PI, EPSILON, MIN_ROUGHNESS, MIN_CLEARCOAT_ROUGHNESS, MAX_ROUGHNESS, XYZ_TO_REC709, square, classifyMaterial } from './Common.js';
|
|
11
7
|
|
|
12
|
-
import {
|
|
13
|
-
PI, TWO_PI, EPSILON, MIN_ROUGHNESS,
|
|
14
|
-
XYZ_TO_REC709, square, computeDotProductsAniso,
|
|
15
|
-
} from './Common.js';
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
fresnelSchlickFloat, fresnel0ToIor, iorToFresnel0Vec3, iorToFresnel0, dielectricF0,
|
|
19
|
-
} from './Fresnel.js';
|
|
8
|
+
import { fresnelSchlickFloat, fresnel0ToIor, iorToFresnel0Vec3, iorToFresnel0 } from './Fresnel.js';
|
|
20
9
|
|
|
21
10
|
// -----------------------------------------------------------------------------
|
|
22
11
|
// Microfacet Distribution Functions
|
|
@@ -31,13 +20,39 @@ export const DistributionGGX = Fn( ( [ NoH, roughness ] ) => {
|
|
|
31
20
|
|
|
32
21
|
} );
|
|
33
22
|
|
|
23
|
+
// Inverted-GGX sheen lobe (Estevez & Kulla 2017): GGX with α² replaced by 1/α², which turns the
|
|
24
|
+
// forward peak into the broad grazing rim sheen needs. Sampled by sheenSamplingRoughness below.
|
|
25
|
+
//
|
|
26
|
+
// The old `min( ..., 100 )` firefly guard is gone: it truncated the peak for sheenRoughness below
|
|
27
|
+
// ~0.2, and because the same function supplies the sampling pdf, a clamped D is a pdf the sampler
|
|
28
|
+
// never drew from. Importance sampling cancels D against its own pdf, so the raw value is safe.
|
|
34
29
|
export const SheenDistribution = Fn( ( [ NoH, roughness ] ) => {
|
|
35
30
|
|
|
36
31
|
const clampedRoughness = max( roughness, MIN_ROUGHNESS );
|
|
37
32
|
const alpha = clampedRoughness.mul( clampedRoughness );
|
|
38
|
-
const
|
|
39
|
-
const d = NoH.mul( NoH ).mul(
|
|
40
|
-
return
|
|
33
|
+
const invAlpha2 = float( 1.0 ).div( alpha.mul( alpha ) );
|
|
34
|
+
const d = NoH.mul( NoH ).mul( invAlpha2.sub( 1.0 ) ).add( 1.0 );
|
|
35
|
+
return invAlpha2.div( max( float( PI ).mul( d ).mul( d ), EPSILON ) );
|
|
36
|
+
|
|
37
|
+
} );
|
|
38
|
+
|
|
39
|
+
// The `roughness` to hand ImportanceSampleGGX so it draws from SheenDistribution. That helper
|
|
40
|
+
// builds A² = roughness⁴ while the sheen lobe is A² = 1/sheenRoughness⁴, so the parameter is the
|
|
41
|
+
// reciprocal. Passing sheenRoughness directly — as this used to — samples the ordinary forward
|
|
42
|
+
// GGX lobe and reports the inverted-GGX density for it, which is simply a different distribution.
|
|
43
|
+
export const sheenSamplingRoughness = Fn( ( [ roughness ] ) => {
|
|
44
|
+
|
|
45
|
+
return float( 1.0 ).div( max( roughness, MIN_ROUGHNESS ) );
|
|
46
|
+
|
|
47
|
+
} );
|
|
48
|
+
|
|
49
|
+
// Ashikhmin/Neubelt sheen visibility — carries the 1/(4·NoV·NoL) denominator like the GGX
|
|
50
|
+
// visibility terms, so the lobe is D · V, never D alone.
|
|
51
|
+
export const VisibilitySheen = Fn( ( [ NoV, NoL ] ) => {
|
|
52
|
+
|
|
53
|
+
return clamp( float( 1.0 ).div(
|
|
54
|
+
max( float( 4.0 ).mul( NoL.add( NoV ).sub( NoL.mul( NoV ) ) ), EPSILON )
|
|
55
|
+
), 0.0, 1.0 );
|
|
41
56
|
|
|
42
57
|
} );
|
|
43
58
|
|
|
@@ -45,19 +60,107 @@ export const SheenDistribution = Fn( ( [ NoH, roughness ] ) => {
|
|
|
45
60
|
// Geometry Terms
|
|
46
61
|
// -----------------------------------------------------------------------------
|
|
47
62
|
|
|
63
|
+
// Smith masking G1 for GGX: G1 = 2·NoV / (NoV + sqrt(α² + (1−α²)·NoV²)), α = roughness².
|
|
64
|
+
//
|
|
65
|
+
// This was `k = (roughness + 1)² / 8` — Karis's remap for ANALYTIC LIGHTS in a split-sum IBL
|
|
66
|
+
// pipeline, not a Smith G — and it landed in both the BRDF's G and, via calculateVNDFPDF, the
|
|
67
|
+
// sampling pdf. The anisotropic siblings always used the correct form, so a material shifted
|
|
68
|
+
// brightness between anisotropy 0 and 0.001.
|
|
48
69
|
export const GeometrySchlickGGX = Fn( ( [ NdotV, roughness ] ) => {
|
|
49
70
|
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
|
|
71
|
+
const alpha = roughness.mul( roughness );
|
|
72
|
+
const alpha2 = alpha.mul( alpha );
|
|
73
|
+
const denom = NdotV.add( sqrt( alpha2.add( float( 1.0 ).sub( alpha2 ).mul( NdotV.mul( NdotV ) ) ) ) );
|
|
74
|
+
return float( 2.0 ).mul( NdotV ).div( max( denom, EPSILON ) );
|
|
75
|
+
|
|
76
|
+
} );
|
|
77
|
+
|
|
78
|
+
// Height-correlated Smith visibility for GGX — the isotropic twin of VisibilityGGXAniso, and
|
|
79
|
+
// like it this ALREADY carries the 1/(4·NoV·NoL) denominator: multiply straight by D and F.
|
|
80
|
+
//
|
|
81
|
+
// The isotropic BRDF used to assemble D·G·F/(4·NoV·NoL) with the SEPARABLE Smith G1(V)·G1(L),
|
|
82
|
+
// which assumes masking and shadowing are independent. They are not, and it overestimates G.
|
|
83
|
+
export const VisibilityGGXSmithCorrelated = Fn( ( [ NoV, NoL, roughness ] ) => {
|
|
84
|
+
|
|
85
|
+
const alpha = roughness.mul( roughness );
|
|
86
|
+
const alpha2 = alpha.mul( alpha );
|
|
87
|
+
const lambdaV = NoL.mul( sqrt( alpha2.add( float( 1.0 ).sub( alpha2 ).mul( NoV.mul( NoV ) ) ) ) );
|
|
88
|
+
const lambdaL = NoV.mul( sqrt( alpha2.add( float( 1.0 ).sub( alpha2 ).mul( NoL.mul( NoL ) ) ) ) );
|
|
89
|
+
return float( 0.5 ).div( max( lambdaV.add( lambdaL ), EPSILON ) );
|
|
53
90
|
|
|
54
91
|
} );
|
|
55
92
|
|
|
56
|
-
export const GeometrySmith = Fn( ( [ NoV, NoL, roughness ] ) => {
|
|
57
93
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
94
|
+
// -----------------------------------------------------------------------------
|
|
95
|
+
// Directional albedo LUT
|
|
96
|
+
// -----------------------------------------------------------------------------
|
|
97
|
+
// R specular E(NoV, roughness) at F0 = 1 — the divisor the multiscatter compensation needs
|
|
98
|
+
// G sheen E(NoV, sheenRoughness) — inverted-GGX lobe with Ashikhmin/Neubelt
|
|
99
|
+
// visibility; what the base must be attenuated by
|
|
100
|
+
// B specular E(NoV, roughness) at F0 = 0 — Schlick's bias term
|
|
101
|
+
// A unused (RGBA because RGB float textures are not portable)
|
|
102
|
+
//
|
|
103
|
+
// All hemisphere integrals of this renderer's own lobes. Schlick is linear in F0, so
|
|
104
|
+
// E(F0) = F0·(R - B) + B is exact for any F0 — no fitted shape anywhere in the chain.
|
|
105
|
+
//
|
|
106
|
+
// This replaces Karis's analytic split-sum polynomial. That polynomial is a fit for a DIFFERENT
|
|
107
|
+
// integral (the split-sum IBL approximation) and is simply not the albedo of the BSDF integrated
|
|
108
|
+
// here: measured against this table it is off by up to 0.31 absolute, over-correcting at mid
|
|
109
|
+
// roughness (implying 0.725 where the truth is 0.899) and under-correcting at high roughness
|
|
110
|
+
// (0.45 where the truth is 0.343). Those two are exactly the +11 % and -26 % the white furnace
|
|
111
|
+
// reported on metal.
|
|
112
|
+
//
|
|
113
|
+
// 16×16, row = NoV, column = roughness, sampled at ENDPOINTS (index i is i/15), so the table
|
|
114
|
+
// covers roughness 1.0 rather than stopping at the last texel centre — that shortfall alone left
|
|
115
|
+
// rough metal 8.6 pp dark. Bilinear reconstruction lands within 0.002 rms / 0.018 max of a
|
|
116
|
+
// 262k-sample reference. Regenerate with `npm run bench:lut` if a lobe or its sampler changes —
|
|
117
|
+
// the table is only valid for the exact BRDF/sampler pair it was integrated from, and drifting out
|
|
118
|
+
// of calibration is exactly the failure this replaced.
|
|
119
|
+
const DFG_LUT_SIZE = 16;
|
|
120
|
+
const _dfgLutData = new Float32Array( [
|
|
121
|
+
1.0000, 0.0302, 0.9039, 1.0000, 0.9712, 0.0898, 0.8635, 1.0000, 0.8914, 0.6742, 0.6952, 1.0000, 0.9176, 1.1716, 0.5703, 1.0000, 0.9447, 1.3763, 0.4529, 1.0000, 0.9592, 1.3948, 0.3509, 1.0000, 0.9658, 1.3111, 0.2690, 1.0000, 0.9679, 1.1830, 0.2061, 1.0000, 0.9669, 1.0453, 0.1588, 1.0000, 0.9639, 0.9156, 0.1237, 1.0000, 0.9592, 0.8006, 0.0978, 1.0000, 0.9533, 0.7013, 0.0786, 1.0000, 0.9465, 0.6167, 0.0644, 1.0000, 0.9388, 0.5447, 0.0537, 1.0000, 0.9304, 0.4836, 0.0457, 1.0000, 0.9214, 0.4314, 0.0395, 1.0000,
|
|
122
|
+
1.0000, 0.0026, 0.7082, 1.0000, 0.9976, 0.0086, 0.7027, 1.0000, 0.9605, 0.1171, 0.6408, 1.0000, 0.9009, 0.4003, 0.5153, 1.0000, 0.8888, 0.6973, 0.4009, 1.0000, 0.9003, 0.8744, 0.3086, 1.0000, 0.9117, 0.9308, 0.2352, 1.0000, 0.9171, 0.9098, 0.1786, 1.0000, 0.9163, 0.8501, 0.1361, 1.0000, 0.9105, 0.7761, 0.1045, 1.0000, 0.9007, 0.7008, 0.0813, 1.0000, 0.8876, 0.6301, 0.0641, 1.0000, 0.8720, 0.5661, 0.0514, 1.0000, 0.8545, 0.5094, 0.0418, 1.0000, 0.8354, 0.4596, 0.0345, 1.0000, 0.8152, 0.4160, 0.0290, 1.0000,
|
|
123
|
+
1.0000, 0.0003, 0.4889, 1.0000, 0.9994, 0.0020, 0.4874, 1.0000, 0.9894, 0.0281, 0.4696, 1.0000, 0.9511, 0.1226, 0.4131, 1.0000, 0.9046, 0.2856, 0.3306, 1.0000, 0.8809, 0.4505, 0.2533, 1.0000, 0.8752, 0.5628, 0.1913, 1.0000, 0.8737, 0.6152, 0.1439, 1.0000, 0.8693, 0.6237, 0.1084, 1.0000, 0.8596, 0.6057, 0.0821, 1.0000, 0.8444, 0.5742, 0.0629, 1.0000, 0.8245, 0.5370, 0.0487, 1.0000, 0.8007, 0.4985, 0.0382, 1.0000, 0.7739, 0.4613, 0.0303, 1.0000, 0.7450, 0.4264, 0.0244, 1.0000, 0.7147, 0.3943, 0.0199, 1.0000,
|
|
124
|
+
1.0000, 0.0000, 0.3277, 1.0000, 0.9999, 0.0007, 0.3272, 1.0000, 0.9955, 0.0104, 0.3205, 1.0000, 0.9751, 0.0486, 0.2969, 1.0000, 0.9352, 0.1290, 0.2521, 1.0000, 0.8955, 0.2363, 0.1989, 1.0000, 0.8698, 0.3371, 0.1513, 1.0000, 0.8545, 0.4098, 0.1136, 1.0000, 0.8416, 0.4511, 0.0850, 1.0000, 0.8258, 0.4674, 0.0639, 1.0000, 0.8050, 0.4664, 0.0484, 1.0000, 0.7792, 0.4547, 0.0369, 1.0000, 0.7489, 0.4370, 0.0285, 1.0000, 0.7152, 0.4164, 0.0223, 1.0000, 0.6791, 0.3948, 0.0176, 1.0000, 0.6416, 0.3732, 0.0140, 1.0000,
|
|
125
|
+
1.0000, 0.0000, 0.2121, 1.0000, 0.9999, 0.0000, 0.2119, 1.0000, 0.9976, 0.0047, 0.2095, 1.0000, 0.9854, 0.0224, 0.1999, 1.0000, 0.9564, 0.0638, 0.1784, 1.0000, 0.9163, 0.1289, 0.1472, 1.0000, 0.8792, 0.2046, 0.1149, 1.0000, 0.8508, 0.2736, 0.0871, 1.0000, 0.8274, 0.3262, 0.0653, 1.0000, 0.8038, 0.3603, 0.0489, 1.0000, 0.7767, 0.3787, 0.0368, 1.0000, 0.7450, 0.3853, 0.0278, 1.0000, 0.7089, 0.3836, 0.0213, 1.0000, 0.6693, 0.3766, 0.0164, 1.0000, 0.6275, 0.3662, 0.0127, 1.0000, 0.5845, 0.3541, 0.0100, 1.0000,
|
|
126
|
+
1.0000, 0.0000, 0.1317, 1.0000, 1.0000, 0.0000, 0.1317, 1.0000, 0.9985, 0.0025, 0.1311, 1.0000, 0.9904, 0.0121, 0.1277, 1.0000, 0.9694, 0.0356, 0.1186, 1.0000, 0.9343, 0.0766, 0.1026, 1.0000, 0.8935, 0.1309, 0.0831, 1.0000, 0.8558, 0.1889, 0.0644, 1.0000, 0.8226, 0.2410, 0.0488, 1.0000, 0.7906, 0.2821, 0.0366, 1.0000, 0.7566, 0.3112, 0.0275, 1.0000, 0.7188, 0.3296, 0.0207, 1.0000, 0.6771, 0.3395, 0.0157, 1.0000, 0.6322, 0.3430, 0.0120, 1.0000, 0.5854, 0.3420, 0.0092, 1.0000, 0.5379, 0.3380, 0.0071, 1.0000,
|
|
127
|
+
1.0000, 0.0000, 0.0778, 1.0000, 1.0000, 0.0000, 0.0778, 1.0000, 0.9990, 0.0015, 0.0779, 1.0000, 0.9931, 0.0073, 0.0773, 1.0000, 0.9774, 0.0220, 0.0743, 1.0000, 0.9481, 0.0490, 0.0673, 1.0000, 0.9079, 0.0883, 0.0569, 1.0000, 0.8651, 0.1349, 0.0456, 1.0000, 0.8239, 0.1820, 0.0352, 1.0000, 0.7839, 0.2242, 0.0267, 1.0000, 0.7427, 0.2585, 0.0201, 1.0000, 0.6986, 0.2844, 0.0152, 1.0000, 0.6513, 0.3026, 0.0114, 1.0000, 0.6014, 0.3144, 0.0087, 1.0000, 0.5501, 0.3212, 0.0066, 1.0000, 0.4989, 0.3243, 0.0051, 1.0000,
|
|
128
|
+
1.0000, 0.0000, 0.0432, 1.0000, 1.0000, 0.0000, 0.0432, 1.0000, 0.9992, 0.0010, 0.0435, 1.0000, 0.9947, 0.0048, 0.0440, 1.0000, 0.9826, 0.0145, 0.0437, 1.0000, 0.9582, 0.0333, 0.0415, 1.0000, 0.9208, 0.0622, 0.0368, 1.0000, 0.8760, 0.0993, 0.0307, 1.0000, 0.8291, 0.1403, 0.0244, 1.0000, 0.7818, 0.1806, 0.0189, 1.0000, 0.7336, 0.2168, 0.0144, 1.0000, 0.6832, 0.2471, 0.0109, 1.0000, 0.6303, 0.2712, 0.0082, 1.0000, 0.5755, 0.2895, 0.0062, 1.0000, 0.5201, 0.3029, 0.0047, 1.0000, 0.4656, 0.3123, 0.0036, 1.0000,
|
|
129
|
+
1.0000, 0.0000, 0.0221, 1.0000, 1.0000, 0.0000, 0.0222, 1.0000, 0.9995, 0.0007, 0.0225, 1.0000, 0.9958, 0.0033, 0.0232, 1.0000, 0.9860, 0.0102, 0.0239, 1.0000, 0.9656, 0.0237, 0.0238, 1.0000, 0.9316, 0.0454, 0.0224, 1.0000, 0.8869, 0.0750, 0.0196, 1.0000, 0.8363, 0.1101, 0.0162, 1.0000, 0.7831, 0.1473, 0.0128, 1.0000, 0.7282, 0.1833, 0.0099, 1.0000, 0.6715, 0.2160, 0.0076, 1.0000, 0.6130, 0.2442, 0.0057, 1.0000, 0.5535, 0.2676, 0.0043, 1.0000, 0.4942, 0.2866, 0.0033, 1.0000, 0.4368, 0.3017, 0.0025, 1.0000,
|
|
130
|
+
1.0000, 0.0000, 0.0102, 1.0000, 1.0000, 0.0000, 0.0103, 1.0000, 0.9998, 0.0004, 0.0105, 1.0000, 0.9965, 0.0024, 0.0111, 1.0000, 0.9884, 0.0074, 0.0120, 1.0000, 0.9710, 0.0175, 0.0127, 1.0000, 0.9404, 0.0342, 0.0126, 1.0000, 0.8971, 0.0580, 0.0117, 1.0000, 0.8446, 0.0879, 0.0101, 1.0000, 0.7867, 0.1215, 0.0083, 1.0000, 0.7258, 0.1562, 0.0066, 1.0000, 0.6629, 0.1898, 0.0051, 1.0000, 0.5988, 0.2207, 0.0039, 1.0000, 0.5345, 0.2482, 0.0030, 1.0000, 0.4716, 0.2719, 0.0022, 1.0000, 0.4115, 0.2922, 0.0017, 1.0000,
|
|
131
|
+
1.0000, 0.0000, 0.0041, 1.0000, 1.0000, 0.0000, 0.0041, 1.0000, 0.9999, 0.0003, 0.0043, 1.0000, 0.9970, 0.0018, 0.0047, 1.0000, 0.9901, 0.0056, 0.0054, 1.0000, 0.9751, 0.0133, 0.0061, 1.0000, 0.9476, 0.0265, 0.0065, 1.0000, 0.9063, 0.0458, 0.0065, 1.0000, 0.8531, 0.0712, 0.0059, 1.0000, 0.7918, 0.1013, 0.0051, 1.0000, 0.7256, 0.1341, 0.0041, 1.0000, 0.6568, 0.1676, 0.0033, 1.0000, 0.5871, 0.2002, 0.0025, 1.0000, 0.5182, 0.2308, 0.0019, 1.0000, 0.4517, 0.2586, 0.0015, 1.0000, 0.3891, 0.2836, 0.0011, 1.0000,
|
|
132
|
+
1.0000, 0.0000, 0.0013, 1.0000, 1.0000, 0.0000, 0.0014, 1.0000, 0.9999, 0.0002, 0.0014, 1.0000, 0.9974, 0.0014, 0.0017, 1.0000, 0.9913, 0.0043, 0.0021, 1.0000, 0.9783, 0.0104, 0.0026, 1.0000, 0.9534, 0.0210, 0.0030, 1.0000, 0.9143, 0.0369, 0.0033, 1.0000, 0.8615, 0.0585, 0.0032, 1.0000, 0.7979, 0.0852, 0.0029, 1.0000, 0.7271, 0.1159, 0.0024, 1.0000, 0.6528, 0.1487, 0.0020, 1.0000, 0.5776, 0.1822, 0.0016, 1.0000, 0.5040, 0.2151, 0.0012, 1.0000, 0.4339, 0.2465, 0.0009, 1.0000, 0.3692, 0.2758, 0.0007, 1.0000,
|
|
133
|
+
1.0000, 0.0000, 0.0003, 1.0000, 1.0000, 0.0000, 0.0003, 1.0000, 1.0000, 0.0001, 0.0004, 1.0000, 0.9977, 0.0010, 0.0005, 1.0000, 0.9923, 0.0034, 0.0007, 1.0000, 0.9807, 0.0083, 0.0010, 1.0000, 0.9581, 0.0169, 0.0012, 1.0000, 0.9214, 0.0301, 0.0015, 1.0000, 0.8695, 0.0486, 0.0015, 1.0000, 0.8044, 0.0724, 0.0015, 1.0000, 0.7299, 0.1007, 0.0013, 1.0000, 0.6504, 0.1325, 0.0011, 1.0000, 0.5699, 0.1663, 0.0009, 1.0000, 0.4916, 0.2010, 0.0007, 1.0000, 0.4181, 0.2353, 0.0006, 1.0000, 0.3513, 0.2686, 0.0004, 1.0000,
|
|
134
|
+
1.0000, 0.0000, 0.0000, 1.0000, 1.0000, 0.0000, 0.0000, 1.0000, 1.0000, 0.0000, 0.0001, 1.0000, 0.9979, 0.0008, 0.0001, 1.0000, 0.9930, 0.0028, 0.0002, 1.0000, 0.9826, 0.0068, 0.0003, 1.0000, 0.9619, 0.0139, 0.0004, 1.0000, 0.9274, 0.0250, 0.0006, 1.0000, 0.8769, 0.0409, 0.0006, 1.0000, 0.8112, 0.0620, 0.0007, 1.0000, 0.7337, 0.0881, 0.0006, 1.0000, 0.6495, 0.1185, 0.0005, 1.0000, 0.5637, 0.1522, 0.0005, 1.0000, 0.4808, 0.1881, 0.0004, 1.0000, 0.4039, 0.2250, 0.0003, 1.0000, 0.3350, 0.2619, 0.0002, 1.0000,
|
|
135
|
+
1.0000, 0.0000, 0.0000, 1.0000, 1.0000, 0.0000, 0.0000, 1.0000, 1.0000, 0.0000, 0.0000, 1.0000, 0.9980, 0.0006, 0.0000, 1.0000, 0.9936, 0.0023, 0.0000, 1.0000, 0.9841, 0.0056, 0.0001, 1.0000, 0.9651, 0.0115, 0.0001, 1.0000, 0.9327, 0.0210, 0.0002, 1.0000, 0.8837, 0.0348, 0.0002, 1.0000, 0.8179, 0.0535, 0.0002, 1.0000, 0.7381, 0.0775, 0.0002, 1.0000, 0.6497, 0.1064, 0.0002, 1.0000, 0.5589, 0.1396, 0.0002, 1.0000, 0.4713, 0.1763, 0.0002, 1.0000, 0.3911, 0.2154, 0.0001, 1.0000, 0.3203, 0.2557, 0.0001, 1.0000,
|
|
136
|
+
1.0000, 0.0000, 0.0000, 1.0000, 1.0000, 0.0000, 0.0000, 1.0000, 1.0000, 0.0000, 0.0000, 1.0000, 0.9980, 0.0005, 0.0000, 1.0000, 0.9939, 0.0019, 0.0000, 1.0000, 0.9854, 0.0047, 0.0000, 1.0000, 0.9677, 0.0097, 0.0000, 1.0000, 0.9372, 0.0178, 0.0000, 1.0000, 0.8899, 0.0298, 0.0000, 1.0000, 0.8245, 0.0466, 0.0000, 1.0000, 0.7430, 0.0685, 0.0000, 1.0000, 0.6508, 0.0959, 0.0000, 1.0000, 0.5552, 0.1284, 0.0000, 1.0000, 0.4631, 0.1656, 0.0000, 1.0000, 0.3794, 0.2065, 0.0000, 1.0000, 0.3069, 0.2500, 0.0000, 1.0000,
|
|
137
|
+
] );
|
|
138
|
+
|
|
139
|
+
const _dfgLutTexture = new DataTexture( _dfgLutData, DFG_LUT_SIZE, DFG_LUT_SIZE, RGBAFormat, FloatType );
|
|
140
|
+
_dfgLutTexture.minFilter = LinearFilter;
|
|
141
|
+
_dfgLutTexture.magFilter = LinearFilter;
|
|
142
|
+
_dfgLutTexture.wrapS = ClampToEdgeWrapping;
|
|
143
|
+
_dfgLutTexture.wrapT = ClampToEdgeWrapping;
|
|
144
|
+
_dfgLutTexture.generateMipmaps = false;
|
|
145
|
+
_dfgLutTexture.needsUpdate = true;
|
|
146
|
+
|
|
147
|
+
const dfgLutNode = texture( _dfgLutTexture );
|
|
148
|
+
dfgLutNode.setUpdateMatrix( false );
|
|
149
|
+
|
|
150
|
+
// Remaps a (roughness, NoV) query onto the LUT's texel centres. Entry i holds the value at
|
|
151
|
+
// i/(N-1), so a raw fetch would read the endpoints half a texel early at both ends.
|
|
152
|
+
const dfgLutUV = ( roughness, NoV ) => vec2( roughness, NoV )
|
|
153
|
+
.mul( ( DFG_LUT_SIZE - 1 ) / DFG_LUT_SIZE )
|
|
154
|
+
.add( 0.5 / DFG_LUT_SIZE );
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Directional albedo of the sheen lobe — what fraction of incoming light it reflects, and so
|
|
158
|
+
* exactly what the base layer underneath must be attenuated by for the pair to conserve energy.
|
|
159
|
+
* Near zero head-on and rising toward grazing, because sheen is a rim lobe.
|
|
160
|
+
*/
|
|
161
|
+
export const sheenDirectionalAlbedo = Fn( ( [ NoV, sheenRoughness ] ) => {
|
|
162
|
+
|
|
163
|
+
return clamp( dfgLutNode.sample( dfgLutUV( sheenRoughness, NoV ) ).g, 0.0, 1.0 );
|
|
61
164
|
|
|
62
165
|
} );
|
|
63
166
|
|
|
@@ -76,26 +179,20 @@ export const GeometrySmith = Fn( ( [ NoV, NoL, roughness ] ) => {
|
|
|
76
179
|
// halves the polynomial work versus calling two separate functions.
|
|
77
180
|
export const evaluateDFG = Fn( ( [ F0, NoV, roughness ] ) => {
|
|
78
181
|
|
|
79
|
-
|
|
80
|
-
const r0 = float( 1.0 ).sub( roughness );
|
|
81
|
-
const r1 = roughness.mul( - 0.0275 ).add( 0.0425 );
|
|
82
|
-
const r2 = roughness.mul( - 0.572 ).add( 1.04 );
|
|
83
|
-
const r3 = roughness.mul( 0.022 ).sub( 0.04 );
|
|
84
|
-
|
|
85
|
-
// exp2(-9.28 * NoV) = exp(-6.4308 * NoV)
|
|
86
|
-
const a004 = min( r0.mul( r0 ), exp( float( - 6.4308 ).mul( NoV ) ) ).mul( r0 ).add( r1 );
|
|
182
|
+
const lut = dfgLutNode.sample( dfgLutUV( roughness, NoV ) ).toVar();
|
|
87
183
|
|
|
88
|
-
|
|
89
|
-
|
|
184
|
+
// Directional albedo at F0 = 1 and F0 = 0. Schlick is linear in F0, so these two pin the
|
|
185
|
+
// whole family exactly: E(F0) = F0·(Ew - bias) + bias. This used to be Karis's analytic
|
|
186
|
+
// split-sum polynomial, which fits a DIFFERENT integral — off by up to 0.31 absolute against
|
|
187
|
+
// this BSDF, and the source of metal's +11 %/-26 % white-furnace error.
|
|
188
|
+
const Ew = max( lut.r, 0.05 ).toVar();
|
|
189
|
+
const bias = clamp( lut.b, 0.0, 1.0 ).toVar();
|
|
190
|
+
const scale = max( Ew.sub( bias ), 0.0 );
|
|
90
191
|
|
|
91
|
-
//
|
|
92
|
-
const Ew = max( dfgScale.add( dfgBias ), 0.1 );
|
|
93
|
-
|
|
94
|
-
// Energy compensation: 1 + F0 * (1/Ew - 1)
|
|
192
|
+
// Kulla-Conty: 1 + F0·(1/Ew - 1).
|
|
95
193
|
const compensation = vec3( 1.0 ).add( F0.mul( float( 1.0 ).div( Ew ).sub( 1.0 ) ) );
|
|
96
194
|
|
|
97
|
-
|
|
98
|
-
const E_ss = max( F0.mul( dfgScale ).add( vec3( dfgBias ) ), vec3( 0.0 ) );
|
|
195
|
+
const E_ss = max( F0.mul( scale ).add( vec3( bias ) ), vec3( 0.0 ) );
|
|
99
196
|
const E_total = clamp( E_ss.mul( compensation ), vec3( 0.0 ), vec3( 1.0 ) );
|
|
100
197
|
|
|
101
198
|
return DFGResult( { compensation, E_total } );
|
|
@@ -106,14 +203,6 @@ export const evaluateDFG = Fn( ( [ F0, NoV, roughness ] ) => {
|
|
|
106
203
|
// PDF Calculation Helpers
|
|
107
204
|
// -----------------------------------------------------------------------------
|
|
108
205
|
|
|
109
|
-
// Calculate PDF for standard GGX importance sampling
|
|
110
|
-
// Formula: D(H) * NoH / (4 * VoH)
|
|
111
|
-
export const calculateGGXPDF = Fn( ( [ NoH, VoH, roughness ] ) => {
|
|
112
|
-
|
|
113
|
-
const D = DistributionGGX( NoH, roughness );
|
|
114
|
-
return D.mul( NoH ).div( max( float( 4.0 ).mul( VoH ), EPSILON ) );
|
|
115
|
-
|
|
116
|
-
} );
|
|
117
206
|
|
|
118
207
|
// Calculate PDF for VNDF sampling
|
|
119
208
|
// Formula: G1(V) * D(H) / (NoV * 4)
|
|
@@ -181,24 +270,6 @@ export const calculateVNDFPDFAniso = Fn( ( [ alphaT, alphaB, NoH, ToH, BoH, NoV,
|
|
|
181
270
|
|
|
182
271
|
} );
|
|
183
272
|
|
|
184
|
-
// Specular VNDF pdf (iso/aniso auto-select) for MIS sites that only have (N, V, L).
|
|
185
|
-
export const specularVNDFPDFForDir = Fn( ( [ material, N, V, L ] ) => {
|
|
186
|
-
|
|
187
|
-
const dots = DotProducts.wrap( computeDotProductsAniso( N, V, L, material ) );
|
|
188
|
-
const pdf = float( 0.0 ).toVar();
|
|
189
|
-
If( material.anisotropy.greaterThan( 0.0 ), () => {
|
|
190
|
-
|
|
191
|
-
const a = computeAnisoAlphas( material.roughness, material.anisotropy );
|
|
192
|
-
pdf.assign( calculateVNDFPDFAniso( a.x, a.y, dots.NoH, dots.ToH, dots.BoH, dots.NoV, dots.ToV, dots.BoV ) );
|
|
193
|
-
|
|
194
|
-
} ).Else( () => {
|
|
195
|
-
|
|
196
|
-
pdf.assign( calculateVNDFPDF( dots.NoH, dots.NoV, max( material.roughness, 0.02 ) ) );
|
|
197
|
-
|
|
198
|
-
} );
|
|
199
|
-
return pdf;
|
|
200
|
-
|
|
201
|
-
} );
|
|
202
273
|
|
|
203
274
|
// -----------------------------------------------------------------------------
|
|
204
275
|
// Iridescence Evaluation
|
|
@@ -323,7 +394,13 @@ export const calculateBRDFWeights = Fn( ( [ material, mc, cache ] ) => {
|
|
|
323
394
|
} );
|
|
324
395
|
|
|
325
396
|
const specular = baseSpecularWeight.mul( material.specularIntensity ).toVar();
|
|
326
|
-
|
|
397
|
+
// (1 - transmission) is load-bearing: a fully transmissive dielectric has no diffuse lobe, and
|
|
398
|
+
// without this glass spent ~23 % of its samples proposing cosine directions whose BRDF value is
|
|
399
|
+
// ~0 while refraction went under-sampled. The old inline MIS weights in LightsSampling had this
|
|
400
|
+
// factor; lobe selection never did.
|
|
401
|
+
const diffuse = float( 1.0 ).sub( baseSpecularWeight )
|
|
402
|
+
.mul( float( 1.0 ).sub( material.metalness ) )
|
|
403
|
+
.mul( float( 1.0 ).sub( material.transmission ) ).toVar();
|
|
327
404
|
const sheen = material.sheen.mul( cache.maxSheenColor ).toVar();
|
|
328
405
|
|
|
329
406
|
const clearcoat = float( 0.0 ).toVar();
|
|
@@ -382,143 +459,99 @@ export const calculateBRDFWeights = Fn( ( [ material, mc, cache ] ) => {
|
|
|
382
459
|
} );
|
|
383
460
|
|
|
384
461
|
// -----------------------------------------------------------------------------
|
|
385
|
-
//
|
|
462
|
+
// BSDF sampling density — the single source of truth for MIS
|
|
386
463
|
// -----------------------------------------------------------------------------
|
|
464
|
+
// The exact density of the direction generateSampledDirection produces, for the upper hemisphere.
|
|
465
|
+
//
|
|
466
|
+
// That sampler picks lobe j with probability weights.j then samples lobe j, so the density of a
|
|
467
|
+
// resulting direction is the sum over every lobe that could have produced it. Two separate
|
|
468
|
+
// requirements force every MIS site through this one function: the weights must sum to 1, which
|
|
469
|
+
// needs both strategies evaluating the same p_bsdf; and each estimator must divide by its own
|
|
470
|
+
// true density.
|
|
471
|
+
//
|
|
472
|
+
// Transmission is absent because it only produces NoL < 0. `weights` is normalised over all
|
|
473
|
+
// lobes including transmission, so omitting its term is exact, not an approximation.
|
|
474
|
+
export const calculateBSDFSamplingPDF = Fn( ( [ material, weights, dots ] ) => {
|
|
387
475
|
|
|
388
|
-
|
|
389
|
-
material, bounceIndex, mc,
|
|
390
|
-
] ) => {
|
|
391
|
-
|
|
392
|
-
// Base BRDF weights using temporary cache
|
|
393
|
-
const tempInvRoughness = float( 1.0 ).sub( material.roughness );
|
|
394
|
-
const tempMetalFactor = float( 0.5 ).add( float( 0.5 ).mul( material.metalness ) );
|
|
395
|
-
const tempIorFactor = min( float( 2.0 ).div( material.ior ), 1.0 );
|
|
396
|
-
const tempMaxSheenColor = max( material.sheenColor.r, max( material.sheenColor.g, material.sheenColor.b ) );
|
|
397
|
-
|
|
398
|
-
const tempCache = MaterialCache( {
|
|
399
|
-
F0: dielectricF0( material.ior ),
|
|
400
|
-
NoV: float( 0.5 ),
|
|
401
|
-
diffuseColor: material.color.rgb,
|
|
402
|
-
isPurelyDiffuse: false,
|
|
403
|
-
alpha: material.roughness.mul( material.roughness ),
|
|
404
|
-
k: material.roughness.add( 1.0 ).mul( material.roughness.add( 1.0 ) ).div( 8.0 ),
|
|
405
|
-
alpha2: material.roughness.mul( material.roughness ).mul( material.roughness ).mul( material.roughness ),
|
|
406
|
-
invRoughness: tempInvRoughness,
|
|
407
|
-
metalFactor: tempMetalFactor,
|
|
408
|
-
iorFactor: tempIorFactor,
|
|
409
|
-
maxSheenColor: tempMaxSheenColor,
|
|
410
|
-
} );
|
|
476
|
+
const pdf = float( 0.0 ).toVar();
|
|
411
477
|
|
|
412
|
-
|
|
478
|
+
If( dots.NoL.greaterThan( 0.0 ), () => {
|
|
413
479
|
|
|
414
|
-
|
|
415
|
-
const specularImportance = weights.specular.toVar();
|
|
416
|
-
const transmissionImportance = weights.transmission.toVar();
|
|
417
|
-
const clearcoatImportance = weights.clearcoat.toVar();
|
|
480
|
+
pdf.addAssign( weights.diffuse.mul( dots.NoL ).mul( PI_INV ) );
|
|
418
481
|
|
|
419
|
-
|
|
420
|
-
|
|
482
|
+
const specPdf = float( 0.0 ).toVar();
|
|
483
|
+
If( material.anisotropy.greaterThan( 0.0 ), () => {
|
|
421
484
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
clearcoatImportance.mulAssign( float( 0.7 ).add( depthFactor.mul( 0.3 ) ) );
|
|
425
|
-
diffuseImportance.mulAssign( float( 1.0 ).add( depthFactor.mul( 0.2 ) ) );
|
|
485
|
+
const a = computeAnisoAlphas( material.roughness, material.anisotropy );
|
|
486
|
+
specPdf.assign( calculateVNDFPDFAniso( a.x, a.y, dots.NoH, dots.ToH, dots.BoH, dots.NoV, dots.ToV, dots.BoV ) );
|
|
426
487
|
|
|
427
|
-
|
|
488
|
+
} ).Else( () => {
|
|
428
489
|
|
|
429
|
-
|
|
430
|
-
|
|
490
|
+
// Raw roughness, not clamped: sampleGGXVNDF is called with material.roughness, and a
|
|
491
|
+
// clamp here would report a density the sampler never drew from.
|
|
492
|
+
specPdf.assign( calculateVNDFPDF( dots.NoH, dots.NoV, material.roughness ) );
|
|
431
493
|
|
|
432
|
-
|
|
433
|
-
|
|
494
|
+
} );
|
|
495
|
+
pdf.addAssign( weights.specular.mul( specPdf ) );
|
|
434
496
|
|
|
435
|
-
|
|
497
|
+
If( weights.sheen.greaterThan( 0.0 ), () => {
|
|
436
498
|
|
|
437
|
-
|
|
499
|
+
const sheenPdf = SheenDistribution( dots.NoH, material.sheenRoughness )
|
|
500
|
+
.mul( dots.NoH ).div( max( float( 4.0 ).mul( dots.VoH ), EPSILON ) );
|
|
501
|
+
pdf.addAssign( weights.sheen.mul( sheenPdf ) );
|
|
438
502
|
|
|
439
|
-
|
|
440
|
-
diffuseImportance.mulAssign( 0.2 );
|
|
441
|
-
specularImportance.mulAssign( 0.6 );
|
|
503
|
+
} );
|
|
442
504
|
|
|
443
|
-
|
|
505
|
+
If( weights.clearcoat.greaterThan( 0.0 ), () => {
|
|
444
506
|
|
|
445
|
-
|
|
507
|
+
const ccRoughness = clamp( material.clearcoatRoughness, MIN_CLEARCOAT_ROUGHNESS, MAX_ROUGHNESS );
|
|
508
|
+
pdf.addAssign( weights.clearcoat.mul( calculateVNDFPDF( dots.NoH, dots.NoV, ccRoughness ) ) );
|
|
446
509
|
|
|
447
|
-
|
|
510
|
+
} );
|
|
448
511
|
|
|
449
512
|
} );
|
|
450
513
|
|
|
451
|
-
|
|
452
|
-
// (environment is handled via deterministic NEE in direct lighting, not an indirect strategy)
|
|
453
|
-
const sum = diffuseImportance.add( specularImportance ).add( transmissionImportance )
|
|
454
|
-
.add( clearcoatImportance ).toVar();
|
|
455
|
-
|
|
456
|
-
If( sum.greaterThan( 0.001 ), () => {
|
|
514
|
+
return pdf;
|
|
457
515
|
|
|
458
|
-
|
|
459
|
-
diffuseImportance.mulAssign( invSum );
|
|
460
|
-
specularImportance.mulAssign( invSum );
|
|
461
|
-
transmissionImportance.mulAssign( invSum );
|
|
462
|
-
clearcoatImportance.mulAssign( invSum );
|
|
516
|
+
} );
|
|
463
517
|
|
|
464
|
-
|
|
518
|
+
// Selection probabilities from `material` alone, for samplers outside the wavefront's
|
|
519
|
+
// classify-once path. Must be the same weights whose density calculateBSDFSamplingPDF reports.
|
|
520
|
+
export const calculateBRDFWeightsFromMaterial = Fn( ( [ material ] ) => {
|
|
465
521
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
522
|
+
const mc = MaterialClassification.wrap( classifyMaterial(
|
|
523
|
+
material.metalness, material.roughness, material.transmission,
|
|
524
|
+
material.clearcoat, material.emissive, material.subsurface,
|
|
525
|
+
) );
|
|
470
526
|
|
|
527
|
+
const cache = MaterialCache( {
|
|
528
|
+
invRoughness: float( 1.0 ).sub( material.roughness ),
|
|
529
|
+
metalFactor: float( 0.5 ).add( float( 0.5 ).mul( material.metalness ) ),
|
|
530
|
+
iorFactor: min( float( 2.0 ).div( material.ior ), 1.0 ),
|
|
531
|
+
maxSheenColor: max( material.sheenColor.x, max( material.sheenColor.y, material.sheenColor.z ) ),
|
|
471
532
|
} );
|
|
472
533
|
|
|
473
|
-
return
|
|
474
|
-
diffuseImportance,
|
|
475
|
-
specularImportance,
|
|
476
|
-
transmissionImportance,
|
|
477
|
-
clearcoatImportance,
|
|
478
|
-
} );
|
|
534
|
+
return calculateBRDFWeights( material, mc, cache );
|
|
479
535
|
|
|
480
536
|
} );
|
|
481
537
|
|
|
482
|
-
//
|
|
483
|
-
//
|
|
484
|
-
//
|
|
538
|
+
// calculateBSDFSamplingPDF for callers holding only `material` and `dots`. Rebuilds the
|
|
539
|
+
// classification and weight cache, so prefer the struct-taking form where those already exist
|
|
540
|
+
// (the wavefront classifies once per shade).
|
|
541
|
+
export const calculateBSDFSamplingPDFFromMaterial = Fn( ( [ material, dots ] ) => {
|
|
485
542
|
|
|
486
|
-
|
|
543
|
+
const weights = BRDFWeights.wrap( calculateBRDFWeightsFromMaterial( material ) );
|
|
544
|
+
return calculateBSDFSamplingPDF( material, weights, dots );
|
|
487
545
|
|
|
488
|
-
|
|
546
|
+
} );
|
|
489
547
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
.toVar();
|
|
548
|
+
// -----------------------------------------------------------------------------
|
|
549
|
+
// Importance Sampling Info
|
|
550
|
+
// -----------------------------------------------------------------------------
|
|
494
551
|
|
|
495
|
-
const alpha = samples.roughness.mul( samples.roughness ).toVar();
|
|
496
|
-
const alpha2 = alpha.mul( alpha ).toVar();
|
|
497
|
-
const r = samples.roughness.add( 1.0 );
|
|
498
|
-
const k = r.mul( r ).div( 8.0 ).toVar();
|
|
499
|
-
|
|
500
|
-
const baseF0 = dielectricF0( material.ior ).mul( material.specularColor );
|
|
501
|
-
const F0 = mix( baseF0, samples.albedo.rgb, samples.metalness ).mul( material.specularIntensity ).toVar();
|
|
502
|
-
const diffuseColor = samples.albedo.rgb.mul( float( 1.0 ).sub( samples.metalness ) ).toVar();
|
|
503
|
-
|
|
504
|
-
const invRoughness = float( 1.0 ).sub( samples.roughness ).toVar();
|
|
505
|
-
const metalFactor = float( 0.5 ).add( float( 0.5 ).mul( samples.metalness ) ).toVar();
|
|
506
|
-
const iorFactor = min( float( 2.0 ).div( material.ior ), 1.0 ).toVar();
|
|
507
|
-
const maxSheenColor = max( material.sheenColor.r, max( material.sheenColor.g, material.sheenColor.b ) ).toVar();
|
|
508
|
-
|
|
509
|
-
return MaterialCache( {
|
|
510
|
-
F0,
|
|
511
|
-
NoV,
|
|
512
|
-
diffuseColor,
|
|
513
|
-
isPurelyDiffuse,
|
|
514
|
-
alpha,
|
|
515
|
-
k,
|
|
516
|
-
alpha2,
|
|
517
|
-
invRoughness,
|
|
518
|
-
metalFactor,
|
|
519
|
-
iorFactor,
|
|
520
|
-
maxSheenColor,
|
|
521
|
-
} );
|
|
522
552
|
|
|
523
|
-
|
|
553
|
+
// -----------------------------------------------------------------------------
|
|
554
|
+
// Material Cache Creation
|
|
555
|
+
// -----------------------------------------------------------------------------
|
|
556
|
+
|
|
524
557
|
|
|
@@ -1,32 +1,14 @@
|
|
|
1
1
|
// Material Transmission & Refraction - Ported from material_transmission.fs
|
|
2
2
|
// Handles both volumetric transmission AND opacity-based transparency
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
Fn,
|
|
6
|
-
wgslFn,
|
|
7
|
-
vec2,
|
|
8
|
-
vec3,
|
|
9
|
-
float,
|
|
10
|
-
int,
|
|
11
|
-
bool as tslBool,
|
|
12
|
-
If,
|
|
13
|
-
select,
|
|
14
|
-
abs,
|
|
15
|
-
dot,
|
|
16
|
-
reflect,
|
|
17
|
-
refract,
|
|
18
|
-
max,
|
|
19
|
-
mix,
|
|
20
|
-
clamp,
|
|
21
|
-
exp,
|
|
22
|
-
} from 'three/tsl';
|
|
4
|
+
import { Fn, wgslFn, vec3, float, int, bool as tslBool, If, select, abs, dot, reflect, refract, max, mix, clamp, exp } from 'three/tsl';
|
|
23
5
|
|
|
24
6
|
import { struct } from './patches.js';
|
|
25
7
|
import { EPSILON, MIN_ROUGHNESS, MIN_PDF } from './Common.js';
|
|
26
8
|
import { iorToFresnel0, fresnelSchlickFloat } from './Fresnel.js';
|
|
27
9
|
import { DistributionGGX } from './MaterialProperties.js';
|
|
28
10
|
import { ImportanceSampleGGX } from './MaterialSampling.js';
|
|
29
|
-
import {
|
|
11
|
+
import { getRandomSample1D, getRandomSample2D, pcgHash } from './Random.js';
|
|
30
12
|
import { handleSubsurfaceEntry, SubsurfaceEntryResult } from './Subsurface.js';
|
|
31
13
|
|
|
32
14
|
// ================================================================================
|
|
@@ -215,7 +197,8 @@ export const calculateShadowTransmittance = Fn( ( [ rayDir, normal, material, en
|
|
|
215
197
|
// ================================================================================
|
|
216
198
|
|
|
217
199
|
export const sampleMicrofacetTransmission = Fn( ( [
|
|
218
|
-
V, N, ior, roughness, entering, dispersion, xi, rngState, pathWavelength
|
|
200
|
+
V, N, ior, roughness, entering, dispersion, xi, rngState, pathWavelength,
|
|
201
|
+
pixelCoord, resolution, frame, dimBase,
|
|
219
202
|
] ) => {
|
|
220
203
|
|
|
221
204
|
const result = MicrofacetTransmissionResult( {
|
|
@@ -244,7 +227,7 @@ export const sampleMicrofacetTransmission = Fn( ( [
|
|
|
244
227
|
|
|
245
228
|
} ).Else( () => {
|
|
246
229
|
|
|
247
|
-
const spectralSample = SpectralSample.wrap( sampleWavelengthForDispersion( ior, dispersion,
|
|
230
|
+
const spectralSample = SpectralSample.wrap( sampleWavelengthForDispersion( ior, dispersion, getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 5 ) ), rngState, resolution, frame ) ) );
|
|
248
231
|
etaRatio.assign( select( entering, float( 1.0 ).div( spectralSample.ior ), spectralSample.ior ) );
|
|
249
232
|
result.colorWeight.assign( spectralSample.colorWeight );
|
|
250
233
|
result.pathWavelength.assign( spectralSample.wavelength );
|
|
@@ -290,7 +273,7 @@ export const sampleMicrofacetTransmission = Fn( ( [
|
|
|
290
273
|
|
|
291
274
|
} ).Else( () => {
|
|
292
275
|
|
|
293
|
-
const spectralSample = SpectralSample.wrap( sampleWavelengthForDispersion( ior, dispersion,
|
|
276
|
+
const spectralSample = SpectralSample.wrap( sampleWavelengthForDispersion( ior, dispersion, getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 5 ) ), rngState, resolution, frame ) ) );
|
|
294
277
|
etaRatio.assign( select( entering, float( 1.0 ).div( spectralSample.ior ), spectralSample.ior ) );
|
|
295
278
|
result.colorWeight.assign( spectralSample.colorWeight );
|
|
296
279
|
result.pathWavelength.assign( spectralSample.wavelength );
|
|
@@ -348,6 +331,7 @@ export const sampleMicrofacetTransmission = Fn( ( [
|
|
|
348
331
|
|
|
349
332
|
export const handleTransmission = Fn( ( [
|
|
350
333
|
rayDir, normal, material, entering, rngState,
|
|
334
|
+
pixelCoord, resolution, frame, dimBase,
|
|
351
335
|
currentMediumIOR, previousMediumIOR, pathWavelength,
|
|
352
336
|
] ) => {
|
|
353
337
|
|
|
@@ -400,20 +384,18 @@ export const handleTransmission = Fn( ( [
|
|
|
400
384
|
reflectProb.assign( clamp( reflectProb, 0.05, 0.95 ) );
|
|
401
385
|
|
|
402
386
|
// Force reflection if TIR, otherwise probabilistically choose
|
|
403
|
-
const doReflect = totalInternalReflection.or(
|
|
387
|
+
const doReflect = totalInternalReflection.or( getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 4 ) ), rngState, resolution, frame ).lessThan( reflectProb ) ).toVar();
|
|
404
388
|
result.didReflect.assign( doReflect );
|
|
405
389
|
|
|
406
390
|
// Choose random sample for microfacet sampling
|
|
407
|
-
const
|
|
408
|
-
const xi_r2 = RandomValue( rngState ).toVar();
|
|
409
|
-
const xi = vec2( xi_r1, xi_r2 );
|
|
391
|
+
const xi = getRandomSample2D( pixelCoord, int( 0 ), dimBase.add( int( 4 ) ), rngState, resolution, frame ).toVar();
|
|
410
392
|
|
|
411
393
|
If( doReflect, () => {
|
|
412
394
|
|
|
413
395
|
// Reflection at a transmissive surface — no wavelength locking
|
|
414
396
|
If( material.roughness.greaterThan( 0.05 ), () => {
|
|
415
397
|
|
|
416
|
-
const mtResult = MicrofacetTransmissionResult.wrap( sampleMicrofacetTransmission( V, N, material.ior, material.roughness, entering, float( 0.0 ), xi, rngState, float( 0.0 ) ) );
|
|
398
|
+
const mtResult = MicrofacetTransmissionResult.wrap( sampleMicrofacetTransmission( V, N, material.ior, material.roughness, entering, float( 0.0 ), xi, rngState, float( 0.0 ), pixelCoord, resolution, frame, dimBase ) );
|
|
417
399
|
result.direction.assign( mtResult.direction );
|
|
418
400
|
|
|
419
401
|
} ).Else( () => {
|
|
@@ -430,7 +412,7 @@ export const handleTransmission = Fn( ( [
|
|
|
430
412
|
// Transmission/refraction path
|
|
431
413
|
If( material.roughness.greaterThan( 0.05 ).or( material.dispersion.greaterThan( 0.0 ) ), () => {
|
|
432
414
|
|
|
433
|
-
const mtResult = MicrofacetTransmissionResult.wrap( sampleMicrofacetTransmission( V, N, material.ior, material.roughness, entering, material.dispersion, xi, rngState, pathWavelength ) );
|
|
415
|
+
const mtResult = MicrofacetTransmissionResult.wrap( sampleMicrofacetTransmission( V, N, material.ior, material.roughness, entering, material.dispersion, xi, rngState, pathWavelength, pixelCoord, resolution, frame, dimBase ) );
|
|
434
416
|
result.pathWavelength.assign( mtResult.pathWavelength );
|
|
435
417
|
|
|
436
418
|
If( mtResult.didReflect, () => {
|
|
@@ -486,6 +468,7 @@ export const handleTransmission = Fn( ( [
|
|
|
486
468
|
|
|
487
469
|
export const handleMaterialTransparency = Fn( ( [
|
|
488
470
|
ray, normal, material, rngState,
|
|
471
|
+
pixelCoord, resolution, frame, dimBase,
|
|
489
472
|
transmissiveTraversals,
|
|
490
473
|
currentMediumIOR, previousMediumIOR,
|
|
491
474
|
pathWavelength,
|
|
@@ -512,8 +495,8 @@ export const handleMaterialTransparency = Fn( ( [
|
|
|
512
495
|
|
|
513
496
|
} ).Else( () => {
|
|
514
497
|
|
|
515
|
-
const alphaRand =
|
|
516
|
-
const transmissionRand =
|
|
498
|
+
const alphaRand = getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 6 ) ), rngState, resolution, frame );
|
|
499
|
+
const transmissionRand = getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 7 ) ), rngState, resolution, frame );
|
|
517
500
|
const transmissionSeed = pcgHash( { state: rngState } );
|
|
518
501
|
|
|
519
502
|
const handled = tslBool( false ).toVar();
|
|
@@ -574,6 +557,7 @@ export const handleMaterialTransparency = Fn( ( [
|
|
|
574
557
|
|
|
575
558
|
const transResult = TransmissionResult.wrap( handleTransmission(
|
|
576
559
|
ray.direction, normal, material, entering, transmissionSeed,
|
|
560
|
+
pixelCoord, resolution, frame, dimBase,
|
|
577
561
|
currentMediumIOR, previousMediumIOR, pathWavelength,
|
|
578
562
|
) );
|
|
579
563
|
|
|
@@ -595,12 +579,13 @@ export const handleMaterialTransparency = Fn( ( [
|
|
|
595
579
|
If( handled.not().and( material.subsurface.greaterThan( 0.0 ) ), () => {
|
|
596
580
|
|
|
597
581
|
const entering = dot( ray.direction, normal ).lessThan( 0.0 );
|
|
598
|
-
const doEnter = entering.not().or(
|
|
582
|
+
const doEnter = entering.not().or( getRandomSample1D( pixelCoord, int( 0 ), dimBase.add( int( 8 ) ), rngState, resolution, frame ).lessThan( material.subsurface ) );
|
|
599
583
|
|
|
600
584
|
If( doEnter, () => {
|
|
601
585
|
|
|
602
586
|
const ssResult = SubsurfaceEntryResult.wrap( handleSubsurfaceEntry(
|
|
603
587
|
ray.direction, normal, material, entering, rngState,
|
|
588
|
+
pixelCoord, resolution, frame, dimBase,
|
|
604
589
|
currentMediumIOR, previousMediumIOR,
|
|
605
590
|
) ).toVar();
|
|
606
591
|
|