@forgeax/engine-shader 0.1.20 → 0.1.23
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 +10 -0
- package/dist/ShaderRegistry.d.ts.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +33070 -8
- package/dist/index.mjs.map +1 -1
- package/dist/ltc/provenance.d.ts +17 -0
- package/dist/ltc/provenance.d.ts.map +1 -0
- package/dist/ltc/tables.d.ts +22 -0
- package/dist/ltc/tables.d.ts.map +1 -0
- package/dist/material/artifact-types.d.ts +52 -0
- package/dist/material/artifact-types.d.ts.map +1 -1
- package/dist/material-schemas.d.ts +5 -0
- package/dist/material-schemas.d.ts.map +1 -1
- package/dist/register-default-sprite-lit.d.ts +3 -6
- package/dist/register-default-sprite-lit.d.ts.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/ShaderRegistry.ts +65 -0
- package/src/__tests__/bloom-fxaa-tonemap.unit.test.ts +30 -4
- package/src/__tests__/default-standard-pbr-alpha.unit.test.ts +7 -0
- package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +138 -30
- package/src/__tests__/direct-light-layout.unit.test.ts +40 -0
- package/src/__tests__/ibl-irradiance.unit.test.ts +7 -4
- package/src/__tests__/ibl-sampling.unit.test.ts +3 -4
- package/src/__tests__/lighting-point-spot-volume.unit.test.ts +16 -2
- package/src/__tests__/lighting-punctual.unit.test.ts +61 -8
- package/src/__tests__/lighting-spot-volume-attenuation.unit.test.ts +1 -1
- package/src/__tests__/ltc-provenance.unit.test.ts +33 -0
- package/src/__tests__/manifest.fixture.json +33 -0
- package/src/__tests__/material-contract.unit.test.ts +61 -1
- package/src/__tests__/material-derived-builtins.integration.test.ts +27 -0
- package/src/__tests__/probe-lighting-composition.unit.test.ts +38 -0
- package/src/__tests__/rect-area-brdf.unit.test.ts +36 -0
- package/src/__tests__/reflection-probe-sampling.unit.test.ts +45 -0
- package/src/__tests__/scene-temporal.unit.test.ts +20 -0
- package/src/__tests__/shader.unit.test.ts +40 -15
- package/src/__tests__/shadow-pcss.unit.test.ts +131 -0
- package/src/__tests__/spot-cookie-composition.unit.test.ts +57 -0
- package/src/__tests__/spot-modifier-composition.unit.test.ts +19 -0
- package/src/__tests__/sprite-lit-shader.test.ts +24 -11
- package/src/__tests__/sprite-variants.unit.test.ts +5 -1
- package/src/__tests__/transmission-thickness-scale.unit.test.ts +7 -1
- package/src/__tests__/transparent-pbr.unit.test.ts +7 -0
- package/src/brdf.wgsl +115 -1
- package/src/common.wgsl +95 -91
- package/src/default-standard-pbr-skin.wgsl +108 -92
- package/src/default-standard-pbr.wgsl +189 -191
- package/src/fxaa.wgsl +27 -6
- package/src/ibl-irradiance.wgsl +4 -1
- package/src/ibl-sampling.wgsl +56 -2
- package/src/index.ts +23 -0
- package/src/lighting-directional.wgsl +161 -132
- package/src/lighting-probe.wgsl +51 -0
- package/src/lighting-punctual.wgsl +69 -29
- package/src/lighting-rect-area.wgsl +162 -0
- package/src/lighting-spot-modifiers.wgsl +93 -0
- package/src/ltc/provenance.ts +19 -0
- package/src/ltc/tables.ts +2831 -0
- package/src/material/artifact-types.ts +137 -0
- package/src/material-schemas.ts +12 -0
- package/src/register-default-sprite-lit.ts +3 -6
- package/src/scene-temporal.wgsl +14 -8
- package/src/shadow-pcf.wgsl +44 -1
- package/src/sprite-lit.wgsl +37 -53
- package/src/sprite.wgsl +1 -1
- package/src/standard-cluster.wgsl +274 -0
- package/src/tonemap.wgsl +8 -3
- package/src/types.ts +2 -1
- package/src/volume/volume-inject.wgsl +48 -38
- package/src/volume/volume-integrate.wgsl +47 -42
- package/dist/.tsbuildinfo +0 -1
- package/src/hdrp-cluster-forward.wgsl +0 -224
|
@@ -164,6 +164,7 @@ import {
|
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
let fxaaSource!: string;
|
|
167
|
+
let commonSource!: string;
|
|
167
168
|
|
|
168
169
|
beforeAll(async () => {
|
|
169
170
|
const fsId = 'node:fs';
|
|
@@ -174,7 +175,9 @@ import {
|
|
|
174
175
|
const url = (await import(/* @vite-ignore */ urlId)) as NodeUrl;
|
|
175
176
|
const here = url.fileURLToPath(import.meta.url);
|
|
176
177
|
const fxaaPath = path.resolve(path.dirname(here), '..', 'fxaa.wgsl');
|
|
178
|
+
const commonPath = path.resolve(path.dirname(here), '..', 'common.wgsl');
|
|
177
179
|
fxaaSource = fs.readFileSync(fxaaPath, 'utf8');
|
|
180
|
+
commonSource = fs.readFileSync(commonPath, 'utf8');
|
|
178
181
|
});
|
|
179
182
|
|
|
180
183
|
describe('fxaa.wgsl content markers', () => {
|
|
@@ -203,7 +206,7 @@ import {
|
|
|
203
206
|
});
|
|
204
207
|
|
|
205
208
|
it('imports forgeax_view::common::FullscreenOutput', () => {
|
|
206
|
-
expect(fxaaSource).toContain('#import forgeax_view::common::FullscreenOutput');
|
|
209
|
+
expect(fxaaSource).toContain('#import forgeax_view::common::{FullscreenOutput');
|
|
207
210
|
});
|
|
208
211
|
|
|
209
212
|
it('declares display-encoded input/output without an OETF owner', () => {
|
|
@@ -212,9 +215,24 @@ import {
|
|
|
212
215
|
expect(fxaaSource).not.toContain('linearToSrgbOetf');
|
|
213
216
|
});
|
|
214
217
|
|
|
215
|
-
it('
|
|
216
|
-
expect(fxaaSource).
|
|
217
|
-
expect(fxaaSource).toMatch(
|
|
218
|
+
it('gates dither at both early and final paths with the per-pass policy UBO', () => {
|
|
219
|
+
expect(fxaaSource).toContain('struct FxaaParams');
|
|
220
|
+
expect(fxaaSource).toMatch(
|
|
221
|
+
/@group\(0\)\s+@binding\(2\)\s+var<uniform> params\s*:\s*FxaaParams/,
|
|
222
|
+
);
|
|
223
|
+
expect(fxaaSource).toContain(
|
|
224
|
+
'select(centerColor, ditherUnorm8(centerColor, in.position.xy), params.ditherEnabled > 0.5)',
|
|
225
|
+
);
|
|
226
|
+
expect(fxaaSource).toContain(
|
|
227
|
+
'select(finalColor, ditherUnorm8(finalColor, in.position.xy), params.ditherEnabled > 0.5)',
|
|
228
|
+
);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('uses the shared output-boundary dither implementation', () => {
|
|
232
|
+
expect(commonSource).toContain('fn ditherUnorm8');
|
|
233
|
+
expect(commonSource).toContain('fn ditherNoise');
|
|
234
|
+
expect(fxaaSource).not.toContain('fn ditherUnorm8');
|
|
235
|
+
expect(fxaaSource).not.toContain('fn ditherNoise');
|
|
218
236
|
});
|
|
219
237
|
});
|
|
220
238
|
|
|
@@ -227,8 +245,8 @@ import {
|
|
|
227
245
|
expect(fxaaSource).toMatch(/@binding\(1\)\s+var\s+samp.*sampler/);
|
|
228
246
|
});
|
|
229
247
|
|
|
230
|
-
it('
|
|
231
|
-
expect(fxaaSource).
|
|
248
|
+
it('declares @group(0) @binding(2) FxaaParams uniform', () => {
|
|
249
|
+
expect(fxaaSource).toMatch(/@binding\(2\)\s+var<uniform>\s+params\s*:\s*FxaaParams/);
|
|
232
250
|
});
|
|
233
251
|
});
|
|
234
252
|
}
|
|
@@ -736,7 +754,8 @@ import {
|
|
|
736
754
|
// Structural source-level assertions (same pattern as tbn-lighting-helpers.test.ts):
|
|
737
755
|
// (a) common.wgsl declares @group(3) instances binding under STORAGE_BUFFER_AVAILABLE
|
|
738
756
|
// (b) default-standard-pbr.wgsl and unlit.wgsl each carry exactly one
|
|
739
|
-
// #pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
757
|
+
// #pragma variant_axis STORAGE_BUFFER_AVAILABLE plus the
|
|
758
|
+
// reflection-fallback axis owned by the Standard PBR shader
|
|
740
759
|
// (c) No shader introduces a new variant axis like INSTANCE_STORAGE_AVAILABLE (OOS-6)
|
|
741
760
|
//
|
|
742
761
|
// RED before w3 (common.wgsl has no @group(3) yet); GREEN after w3 adds it.
|
|
@@ -794,7 +813,7 @@ import {
|
|
|
794
813
|
expect(src).toMatch(/#if\s+STORAGE_BUFFER_AVAILABLE\s*==\s*true[\s\S]*@group\(3\)/);
|
|
795
814
|
});
|
|
796
815
|
|
|
797
|
-
it('(b) default-standard-pbr.wgsl declares
|
|
816
|
+
it('(b) default-standard-pbr.wgsl declares its complete variant-axis contract', () => {
|
|
798
817
|
// feat-20260609-hdrp-cluster-fragment-ggx M1: added CLUSTER_FORWARD_AVAILABLE as a
|
|
799
818
|
// second variant axis so the standard-pbr fragment can switch between URP-style
|
|
800
819
|
// (single dirlight + ambient) and HDRP-style (cluster forward 256 punctual lights).
|
|
@@ -804,8 +823,12 @@ import {
|
|
|
804
823
|
'#pragma variant_axis STORAGE_BUFFER_AVAILABLE',
|
|
805
824
|
'#pragma variant_axis CLUSTER_FORWARD_AVAILABLE',
|
|
806
825
|
'#pragma variant_axis VERTEX_COLOR_AVAILABLE',
|
|
826
|
+
'#pragma variant_axis PROBE_BLEND_AVAILABLE',
|
|
827
|
+
'#pragma variant_axis EXTENDED_LIGHTING_AVAILABLE',
|
|
807
828
|
'#pragma variant_axis TRANSMISSION_AVAILABLE',
|
|
829
|
+
'#pragma variant_axis DIRECTIONAL_PCSS_AVAILABLE',
|
|
808
830
|
'#pragma variant_axis PROJECTOR_AVAILABLE',
|
|
831
|
+
'#pragma variant_axis REFLECTION_FALLBACK_AVAILABLE',
|
|
809
832
|
]);
|
|
810
833
|
});
|
|
811
834
|
|
|
@@ -1741,10 +1764,11 @@ import {
|
|
|
1741
1764
|
expect(src).toMatch(/applyTBN/);
|
|
1742
1765
|
});
|
|
1743
1766
|
|
|
1744
|
-
it('default-standard-pbr.wgsl imports
|
|
1767
|
+
it('default-standard-pbr.wgsl imports directional lighting and the shared Standard Cluster owner', () => {
|
|
1745
1768
|
const src = readSource('default-standard-pbr.wgsl');
|
|
1746
1769
|
expect(src).toMatch(/#import\s+forgeax_pbr::lighting_directional::/);
|
|
1747
|
-
expect(src).toMatch(/#import\s+
|
|
1770
|
+
expect(src).toMatch(/#import\s+forgeax_standard::cluster::/);
|
|
1771
|
+
expect(src).toMatch(/evaluateStandardClusterLights/);
|
|
1748
1772
|
});
|
|
1749
1773
|
|
|
1750
1774
|
it('default-standard-pbr.wgsl no longer defines fn evalDirectional / evalPoint / evalSpot inline', () => {
|
|
@@ -1843,9 +1867,10 @@ import {
|
|
|
1843
1867
|
// ground-truth table (research F1 section 6 - "extended Reinhard
|
|
1844
1868
|
// matches Reinhard 2002 luminance variant"). The TS port shadowed
|
|
1845
1869
|
// below is the formula manifest.
|
|
1846
|
-
// 2. TonemapParams std140 layout is 16 B (exposure + whitePoint
|
|
1847
|
-
// +
|
|
1848
|
-
// keeps the same byte layout
|
|
1870
|
+
// 2. TonemapParams std140 layout is 16 B (exposure + whitePoint + mode
|
|
1871
|
+
// + ditherEnabled = 16 B). The shader-side struct in tonemap.wgsl
|
|
1872
|
+
// keeps the same byte layout; the final output pass toggles only the
|
|
1873
|
+
// ditherEnabled slot in a per-pass copy.
|
|
1849
1874
|
// 3. TONEMAP_LUMINANCE_EPSILON is the shared TS / WGSL const
|
|
1850
1875
|
// (D-O3, 1e-5).
|
|
1851
1876
|
//
|
|
@@ -1939,10 +1964,10 @@ import {
|
|
|
1939
1964
|
});
|
|
1940
1965
|
|
|
1941
1966
|
describe('TonemapParams std140 layout', () => {
|
|
1942
|
-
it('TonemapParams stride is 16 B (
|
|
1967
|
+
it('TonemapParams stride is 16 B (exposure + whitePoint + mode + ditherEnabled)', () => {
|
|
1943
1968
|
// The host writes `Float32Array` of length 4 to the UBO; the shader
|
|
1944
1969
|
// declares `struct TonemapParams { exposure: f32, whitePoint: f32,
|
|
1945
|
-
//
|
|
1970
|
+
// mode: u32, ditherEnabled: f32 }` — total = 16 B.
|
|
1946
1971
|
const stride = 4 * 4;
|
|
1947
1972
|
expect(stride).toBe(16);
|
|
1948
1973
|
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
|
|
5
|
+
const shaderRoot = fileURLToPath(new URL('..', import.meta.url));
|
|
6
|
+
const directional = readFileSync(`${shaderRoot}/lighting-directional.wgsl`, 'utf8');
|
|
7
|
+
const shadowPcf = readFileSync(`${shaderRoot}/shadow-pcf.wgsl`, 'utf8');
|
|
8
|
+
|
|
9
|
+
const PCSS_MEDIUM_RAW_TAPS = 8;
|
|
10
|
+
const PCSS_MEDIUM_COMPARE_TAPS = 16;
|
|
11
|
+
const PCSS_HIGH_RAW_TAPS = 16;
|
|
12
|
+
const PCSS_HIGH_COMPARE_TAPS = 32;
|
|
13
|
+
|
|
14
|
+
function biasedReceiverDepth(
|
|
15
|
+
receiverDepth: number,
|
|
16
|
+
normalBias: number,
|
|
17
|
+
depthBias: number,
|
|
18
|
+
nDotL: number,
|
|
19
|
+
): number {
|
|
20
|
+
return receiverDepth - Math.max(normalBias * (1 - nDotL), depthBias);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function averageBlockerDepth(
|
|
24
|
+
rawDepths: readonly number[],
|
|
25
|
+
receiverDepth: number,
|
|
26
|
+
): number | undefined {
|
|
27
|
+
const blockers = rawDepths.filter((depth) => Number.isFinite(depth) && depth < receiverDepth);
|
|
28
|
+
if (blockers.length === 0) return undefined;
|
|
29
|
+
return blockers.reduce((sum, depth) => sum + depth, 0) / blockers.length;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function penumbraTexels(
|
|
33
|
+
biasedDepth: number,
|
|
34
|
+
blockerDepth: number,
|
|
35
|
+
lightDepthWorldSpan: number,
|
|
36
|
+
angularRadiusRadians: number,
|
|
37
|
+
worldUnitsPerTexel: number,
|
|
38
|
+
maxPenumbraTexels: number,
|
|
39
|
+
): number {
|
|
40
|
+
const worldDistance = Math.max(0, biasedDepth - blockerDepth) * lightDepthWorldSpan;
|
|
41
|
+
return Math.min(
|
|
42
|
+
maxPenumbraTexels,
|
|
43
|
+
Math.max(0, (worldDistance * Math.tan(angularRadiusRadians)) / worldUnitsPerTexel),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function clampShadowTexel(
|
|
48
|
+
texel: readonly [number, number],
|
|
49
|
+
tileOrigin: readonly [number, number],
|
|
50
|
+
tileSize: readonly [number, number],
|
|
51
|
+
inset: number,
|
|
52
|
+
): [number, number] {
|
|
53
|
+
const minX = tileOrigin[0] + inset;
|
|
54
|
+
const minY = tileOrigin[1] + inset;
|
|
55
|
+
const maxX = tileOrigin[0] + Math.max(inset, tileSize[0] - 1 - inset);
|
|
56
|
+
const maxY = tileOrigin[1] + Math.max(inset, tileSize[1] - 1 - inset);
|
|
57
|
+
return [
|
|
58
|
+
Math.min(maxX, Math.max(minX, Math.trunc(texel[0]))),
|
|
59
|
+
Math.min(maxY, Math.max(minY, Math.trunc(texel[1]))),
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function stableDiskRotation(texelX: number, texelY: number, cascade: number): number {
|
|
64
|
+
const hash = Math.imul(Math.imul(texelX ^ 0x9e3779b9, 31) ^ texelY, 17) ^ cascade;
|
|
65
|
+
return (hash >>> 0) / 0x100000000;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
describe('Directional PCSS numeric oracle', () => {
|
|
69
|
+
it('freezes normal and blend tap budgets for medium and high profiles', () => {
|
|
70
|
+
expect([PCSS_MEDIUM_RAW_TAPS, PCSS_MEDIUM_COMPARE_TAPS]).toEqual([8, 16]);
|
|
71
|
+
expect([PCSS_HIGH_RAW_TAPS, PCSS_HIGH_COMPARE_TAPS]).toEqual([16, 32]);
|
|
72
|
+
expect(PCSS_MEDIUM_RAW_TAPS + PCSS_MEDIUM_COMPARE_TAPS).toBe(24);
|
|
73
|
+
expect(PCSS_HIGH_RAW_TAPS + PCSS_HIGH_COMPARE_TAPS).toBe(48);
|
|
74
|
+
expect((PCSS_MEDIUM_RAW_TAPS + PCSS_MEDIUM_COMPARE_TAPS) * 2).toBe(48);
|
|
75
|
+
expect((PCSS_HIGH_RAW_TAPS + PCSS_HIGH_COMPARE_TAPS) * 2).toBe(96);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('uses one biased receiver depth for blocker classification and compare', () => {
|
|
79
|
+
const receiver = biasedReceiverDepth(0.7, 0.02, 0.003, 0.5);
|
|
80
|
+
expect(receiver).toBeCloseTo(0.69, 7);
|
|
81
|
+
expect(averageBlockerDepth([0.68, 0.69, 0.71, Number.NaN], receiver)).toBeCloseTo(0.68, 7);
|
|
82
|
+
expect(averageBlockerDepth([0.7, 0.8, Number.POSITIVE_INFINITY], receiver)).toBeUndefined();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('grows world-scale penumbra with blocker distance and clamps the texel radius', () => {
|
|
86
|
+
const near = penumbraTexels(0.7, 0.69, 20, 0.01, 0.1, 64);
|
|
87
|
+
const far = penumbraTexels(0.7, 0.4, 20, 0.01, 0.1, 64);
|
|
88
|
+
expect(near).toBeGreaterThan(0);
|
|
89
|
+
expect(far).toBeGreaterThan(near);
|
|
90
|
+
expect(penumbraTexels(0.7, 0.0, 20, 0.05, 0.001, 12)).toBe(12);
|
|
91
|
+
expect(penumbraTexels(0.7, 0.8, 20, 0.01, 0.1, 64)).toBe(0);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('keeps raw and compare texels inside an integer one-texel tile inset', () => {
|
|
95
|
+
expect(clampShadowTexel([-100.8, 999.2], [32, 48], [64, 32], 1)).toEqual([33, 78]);
|
|
96
|
+
expect(clampShadowTexel([48.9, 60.1], [32, 48], [64, 32], 1)).toEqual([48, 60]);
|
|
97
|
+
expect(clampShadowTexel([32, 48], [32, 48], [1, 1], 1)).toEqual([33, 49]);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('is stable for the same cascade-local integer texel and independent of frame jitter', () => {
|
|
101
|
+
const first = stableDiskRotation(107, 203, 2);
|
|
102
|
+
const second = stableDiskRotation(107, 203, 2);
|
|
103
|
+
expect(first).toBe(second);
|
|
104
|
+
expect(stableDiskRotation(108, 203, 2)).not.toBe(first);
|
|
105
|
+
expect(directional).not.toMatch(/frameIndex|frame_index|taaJitter|taa_jitter|jitter/);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('requires the production three-stage receiver and shared sampling primitives', () => {
|
|
109
|
+
expect(directional).toContain('PCSS_MEDIUM_RAW_TAPS');
|
|
110
|
+
expect(directional).toContain('PCSS_MEDIUM_COMPARE_TAPS');
|
|
111
|
+
expect(directional).toContain('PCSS_HIGH_RAW_TAPS');
|
|
112
|
+
expect(directional).toContain('PCSS_HIGH_COMPARE_TAPS');
|
|
113
|
+
expect(directional).toMatch(/blocker/i);
|
|
114
|
+
expect(directional).toMatch(/penumbra/i);
|
|
115
|
+
expect(shadowPcf).toContain('fn shadow_load_raw_depth');
|
|
116
|
+
expect(shadowPcf).toContain('fn shadow_sample_compare');
|
|
117
|
+
expect(shadowPcf).toContain('fn shadow_biased_receiver_depth');
|
|
118
|
+
expect(shadowPcf).toContain('fn shadow_clamp_texel_to_tile');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('does not accept fixed-radius or PCF5-as-PCSS receiver structure', () => {
|
|
122
|
+
expect(directional).not.toMatch(/fixed.?radius|fixedUvRadius|pcf5.*pcss/i);
|
|
123
|
+
expect(directional).not.toContain('M2 supplies the PCSS arithmetic');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('maps the serialized PCF labels to distinct production kernel widths', () => {
|
|
127
|
+
expect(directional).toMatch(
|
|
128
|
+
/let kernel = select\(select\(3u, 5u, filterProfile == 3u\), 1u, filterProfile == 1u\)/,
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
async function readShader(name: string): Promise<string> {
|
|
5
|
+
return readFile(new URL(`../${name}`, import.meta.url), 'utf8');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function extractSpotModifierFactors(source: string): string {
|
|
9
|
+
const start = source.indexOf('fn spotModifierFactors(');
|
|
10
|
+
const end = source.indexOf('\nfn evalSpotWithModifiers', start);
|
|
11
|
+
return source.slice(start, end).replace(/\s+/g, '');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('Cookie RGBA Spot modifier composition', () => {
|
|
15
|
+
it('rejects scalar Cookie sampling and applies linear RGB times alpha', async () => {
|
|
16
|
+
const [standard, skin, modifiers, clustered, common] = await Promise.all([
|
|
17
|
+
readShader('default-standard-pbr.wgsl'),
|
|
18
|
+
readShader('default-standard-pbr-skin.wgsl'),
|
|
19
|
+
readShader('lighting-spot-modifiers.wgsl'),
|
|
20
|
+
readShader('standard-cluster.wgsl'),
|
|
21
|
+
readShader('common.wgsl'),
|
|
22
|
+
]);
|
|
23
|
+
expect(standard).not.toContain('lighting_spot_modifiers::{spotModifierFactors}');
|
|
24
|
+
expect(skin).not.toContain('lighting_spot_modifiers::{spotModifierFactors}');
|
|
25
|
+
expect(clustered).toContain('lighting_spot_modifiers::{spotModifierFactors}');
|
|
26
|
+
expect(standard).not.toContain('fn spotModifierFactors(');
|
|
27
|
+
expect(skin).not.toContain('fn spotModifierFactors(');
|
|
28
|
+
expect(modifiers).toContain(
|
|
29
|
+
'textureSampleLevel(cookieTexture, spotModifierSampler, cookieUv, metadata.w, 0.0)',
|
|
30
|
+
);
|
|
31
|
+
expect(modifiers).toContain('cookieSample.rgb * cookieSample.a');
|
|
32
|
+
expect(modifiers).not.toMatch(/textureSampleLevel\(cookieTexture[^\n]*\)\.r/);
|
|
33
|
+
expect(modifiers).toContain('cookieMatrices[metadata.w]');
|
|
34
|
+
expect(modifiers).toContain('metadata.w != 0xffffffffu && (metadata.y & PROJECTOR_FLAG) == 0u');
|
|
35
|
+
expect(modifiers).toContain('let right = normalize(cross(forward, referenceUp));');
|
|
36
|
+
expect(modifiers).toContain('let depth = dot(outgoing, forward);');
|
|
37
|
+
expect(modifiers).toContain(
|
|
38
|
+
'iesProfileTexture, spotModifierSampler, iesUv, metadata.z, 0.0).r',
|
|
39
|
+
);
|
|
40
|
+
expect(common).toContain('@group(0) @binding(15) var<uniform> cookieMatrices');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('keeps skin and non-skin sampling and missing-resource identity aligned', async () => {
|
|
44
|
+
const [standard, skin, modifiers, clustered] = await Promise.all([
|
|
45
|
+
readShader('default-standard-pbr.wgsl'),
|
|
46
|
+
readShader('default-standard-pbr-skin.wgsl'),
|
|
47
|
+
readShader('lighting-spot-modifiers.wgsl'),
|
|
48
|
+
readShader('standard-cluster.wgsl'),
|
|
49
|
+
]);
|
|
50
|
+
expect(extractSpotModifierFactors(standard)).toBe('');
|
|
51
|
+
expect(extractSpotModifierFactors(skin)).toBe('');
|
|
52
|
+
expect(modifiers).toContain('cookie : f32');
|
|
53
|
+
expect(modifiers).toContain('brdf * range * cone * ies * cookie * shadow');
|
|
54
|
+
expect(clustered).toContain('spotModifierFactors');
|
|
55
|
+
expect(clustered).toContain(') * modifier;');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
|
|
4
|
+
describe('shared Spot modifier shader composition', () => {
|
|
5
|
+
it('names one shared evaluator with the frozen multiplication order', async () => {
|
|
6
|
+
const source = await readFile(
|
|
7
|
+
new URL('../lighting-spot-modifiers.wgsl', import.meta.url),
|
|
8
|
+
'utf8',
|
|
9
|
+
);
|
|
10
|
+
expect(source).toContain('spotModifierProduct');
|
|
11
|
+
expect(source).toMatch(/brdf[\s\S]*range[\s\S]*cone[\s\S]*ies[\s\S]*cookie[\s\S]*shadow/);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('keeps modifier sampling behind the extended-lighting topology', async () => {
|
|
15
|
+
const punctual = await readFile(new URL('../lighting-punctual.wgsl', import.meta.url), 'utf8');
|
|
16
|
+
expect(punctual).toContain('extendedLighting');
|
|
17
|
+
expect(punctual).not.toContain('modifierLight');
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -181,34 +181,47 @@ describe('sprite-lit shader (flat 2D lighting, tweak-20260701 M1)', () => {
|
|
|
181
181
|
// At minimum: one consumer in each of fs_main and fs_main_hdr.
|
|
182
182
|
expect(consumers.length).toBeGreaterThanOrEqual(2);
|
|
183
183
|
});
|
|
184
|
+
|
|
185
|
+
it('VsOut carries vertex-produced ndc and viewZ for Standard cluster lookup', async () => {
|
|
186
|
+
const src = await readSpriteLitSource();
|
|
187
|
+
expect(src).toMatch(/@location\(\s*2\s*\)\s+ndc\s*:\s*vec3<f32>/);
|
|
188
|
+
expect(src).toMatch(/@location\(\s*3\s*\)\s+viewZ\s*:\s*f32/);
|
|
189
|
+
expect(src).toMatch(/out\.ndc\s*=\s*clipPos\.xyz\s*\/\s*clipPos\.w\s*;/);
|
|
190
|
+
expect(src).toMatch(/out\.viewZ\s*=\s*sceneViewZ\(/);
|
|
191
|
+
expect(src).toMatch(/evaluateStandardClusterLights\(\s*ndc,\s*viewZ,\s*worldPos/);
|
|
192
|
+
});
|
|
184
193
|
});
|
|
185
194
|
|
|
186
|
-
describe('
|
|
195
|
+
describe('Standard local lights use the shared Cluster path (D-P2)', () => {
|
|
187
196
|
it('spriteLitDirectional signature is `(albedo)` only', async () => {
|
|
188
197
|
const src = await readSpriteLitSource();
|
|
189
198
|
const re = /fn\s+spriteLitDirectional\s*\(\s*albedo\s*:\s*vec3<f32>\s*\)/;
|
|
190
199
|
expect(re.test(src)).toBe(true);
|
|
191
200
|
});
|
|
192
201
|
|
|
193
|
-
it('
|
|
202
|
+
it('does not retain a direct point-light branch', async () => {
|
|
194
203
|
const src = await readSpriteLitSource();
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
expect(re.test(src)).toBe(true);
|
|
204
|
+
expect(src).not.toContain('pointLightsBuffer');
|
|
205
|
+
expect(src).not.toContain('evalPointFlat(');
|
|
198
206
|
});
|
|
199
207
|
|
|
200
|
-
it('
|
|
208
|
+
it('does not retain a direct spot-light branch', async () => {
|
|
201
209
|
const src = await readSpriteLitSource();
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
expect(re.test(src)).toBe(true);
|
|
210
|
+
expect(src).not.toContain('spotLightsBuffer');
|
|
211
|
+
expect(src).not.toContain('evalSpotFlat(');
|
|
205
212
|
});
|
|
206
213
|
|
|
207
|
-
it('spriteLitShadeAccum
|
|
214
|
+
it('spriteLitShadeAccum consumes vertex-produced ndc/viewZ without a normal', async () => {
|
|
208
215
|
const src = await readSpriteLitSource();
|
|
209
216
|
const re =
|
|
210
|
-
/fn\s+spriteLitShadeAccum\s*\(\s*albedo\s*:\s*vec3<f32>\s*,\s*worldPos\s*:\s*vec3<f32>\s*\)/;
|
|
217
|
+
/fn\s+spriteLitShadeAccum\s*\(\s*albedo\s*:\s*vec3<f32>\s*,\s*worldPos\s*:\s*vec3<f32>\s*,\s*ndc\s*:\s*vec3<f32>\s*,\s*viewZ\s*:\s*f32\s*,?\s*\)/;
|
|
211
218
|
expect(re.test(src)).toBe(true);
|
|
219
|
+
const bodyStart = src.indexOf('fn spriteLitShadeAccum');
|
|
220
|
+
const bodyEnd = src.indexOf('fn fs_main(', bodyStart);
|
|
221
|
+
expect(bodyStart).toBeGreaterThanOrEqual(0);
|
|
222
|
+
expect(src.slice(bodyStart, bodyEnd > bodyStart ? bodyEnd : src.length)).not.toContain(
|
|
223
|
+
'@builtin(position)',
|
|
224
|
+
);
|
|
212
225
|
});
|
|
213
226
|
});
|
|
214
227
|
|
|
@@ -111,14 +111,18 @@ describe('w6 (b) -- pbr / unlit / sprite-adjacent shaders DO NOT pick up PER_INS
|
|
|
111
111
|
});
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
it('default-standard-pbr.wgsl keeps its
|
|
114
|
+
it('default-standard-pbr.wgsl keeps its material axes (none touch PER_INSTANCE_REGION)', () => {
|
|
115
115
|
const axes = variantAxes(readWgsl('default-standard-pbr.wgsl'));
|
|
116
116
|
expect(axes).toEqual([
|
|
117
117
|
'#pragma variant_axis STORAGE_BUFFER_AVAILABLE',
|
|
118
118
|
'#pragma variant_axis CLUSTER_FORWARD_AVAILABLE',
|
|
119
119
|
'#pragma variant_axis VERTEX_COLOR_AVAILABLE',
|
|
120
|
+
'#pragma variant_axis PROBE_BLEND_AVAILABLE',
|
|
121
|
+
'#pragma variant_axis EXTENDED_LIGHTING_AVAILABLE',
|
|
120
122
|
'#pragma variant_axis TRANSMISSION_AVAILABLE',
|
|
123
|
+
'#pragma variant_axis DIRECTIONAL_PCSS_AVAILABLE',
|
|
121
124
|
'#pragma variant_axis PROJECTOR_AVAILABLE',
|
|
125
|
+
'#pragma variant_axis REFLECTION_FALLBACK_AVAILABLE',
|
|
122
126
|
]);
|
|
123
127
|
});
|
|
124
128
|
|
|
@@ -7,7 +7,13 @@ const shader = readFileSync(resolve(import.meta.dirname, '../default-standard-pb
|
|
|
7
7
|
describe('standard transmission thickness scale contract', () => {
|
|
8
8
|
it('converts local authored thickness through the combined local-to-world basis', () => {
|
|
9
9
|
expect(shader).toContain('let localToWorld = entityWorld * instanceLocal;');
|
|
10
|
-
expect(shader).toContain(
|
|
10
|
+
expect(shader).toContain('@location(4) @interpolate(flat) transmissionBasis0 : vec4<f32>');
|
|
11
|
+
expect(shader).toContain('@location(15) @interpolate(flat) transmissionBasis1 : vec4<f32>');
|
|
12
|
+
expect(shader).toContain('let localToWorld0 = in.transmissionBasis0.xyz;');
|
|
13
|
+
expect(shader).toContain('let localToWorld1 = vec3<f32>(');
|
|
14
|
+
expect(shader).toContain('let localToWorld2 = vec3<f32>(');
|
|
15
|
+
expect(shader).toContain('in.ndc.w,');
|
|
16
|
+
expect(shader).not.toContain(
|
|
11
17
|
'let localToWorld = meshes[0].worldFromLocal * instances[in.instanceIdx].localFromInstance;',
|
|
12
18
|
);
|
|
13
19
|
expect(shader).toContain('let worldToLocal0 = vec3<f32>(');
|
|
@@ -18,4 +18,11 @@ describe('transparent PBR color domain contract', () => {
|
|
|
18
18
|
expect(shader).toContain('linearHdrColorDomain');
|
|
19
19
|
expect(shader).toContain('toneStageInput');
|
|
20
20
|
});
|
|
21
|
+
|
|
22
|
+
it('keeps transparent PBR on the shared Directional factor path', () => {
|
|
23
|
+
expect(shader).toContain('lighting_directional');
|
|
24
|
+
expect(shader.match(/evalDirectionalShadowFactor\(/g)).toHaveLength(1);
|
|
25
|
+
expect(shader).toContain('directionalBase');
|
|
26
|
+
expect(shader).toContain('directionalClearcoat');
|
|
27
|
+
});
|
|
21
28
|
});
|
package/src/brdf.wgsl
CHANGED
|
@@ -21,5 +21,119 @@ fn v_smith(nDotV : f32, nDotL : f32, a : f32) -> f32 {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
fn f_schlick(vDotH : f32, f0 : vec3<f32>) -> vec3<f32> {
|
|
24
|
-
|
|
24
|
+
let fresnel = exp2((-5.55473 * vDotH - 6.98316) * vDotH);
|
|
25
|
+
return f0 * (vec3<f32>(1.0) - fresnel) + vec3<f32>(fresnel);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
// Source: Three.js r184 src/nodes/functions/BSDF/DFGLUT.js. The full 16x16
|
|
30
|
+
// RG16F table is decoded to exact f32 constants here; the lookup below mirrors
|
|
31
|
+
// the source DataTexture's linear filtering and clamp-to-edge sampling without
|
|
32
|
+
// adding a host binding or coupling direct light to the engine IBL LUT.
|
|
33
|
+
const THREE_R184_DFG_LUT_SIZE : u32 = 16u;
|
|
34
|
+
const THREE_R184_DFG_LUT : array<vec2<f32>, 256> = array<vec2<f32>, 256>(
|
|
35
|
+
vec2<f32>(0.1470947265625, 0.85205078125), vec2<f32>(0.16552734375, 0.78759765625), vec2<f32>(0.244384765625, 0.638671875), vec2<f32>(0.370849609375, 0.51953125),
|
|
36
|
+
vec2<f32>(0.496826171875, 0.41552734375), vec2<f32>(0.60205078125, 0.326416015625), vec2<f32>(0.68408203125, 0.25390625), vec2<f32>(0.74609375, 0.197509765625),
|
|
37
|
+
vec2<f32>(0.79052734375, 0.154296875), vec2<f32>(0.822265625, 0.12164306640625), vec2<f32>(0.84326171875, 0.09698486328125), vec2<f32>(0.8564453125, 0.07843017578125),
|
|
38
|
+
vec2<f32>(0.86328125, 0.06439208984375), vec2<f32>(0.86572265625, 0.0537109375), vec2<f32>(0.8642578125, 0.045440673828125), vec2<f32>(0.85986328125, 0.039031982421875),
|
|
39
|
+
vec2<f32>(0.388671875, 0.611328125), vec2<f32>(0.39306640625, 0.6005859375), vec2<f32>(0.412353515625, 0.5458984375), vec2<f32>(0.45654296875, 0.4482421875),
|
|
40
|
+
vec2<f32>(0.52783203125, 0.3525390625), vec2<f32>(0.607421875, 0.27392578125), vec2<f32>(0.6787109375, 0.21142578125), vec2<f32>(0.7333984375, 0.16259765625),
|
|
41
|
+
vec2<f32>(0.77099609375, 0.1253662109375), vec2<f32>(0.79345703125, 0.0972900390625), vec2<f32>(0.80322265625, 0.07623291015625), vec2<f32>(0.8037109375, 0.06036376953125),
|
|
42
|
+
vec2<f32>(0.79638671875, 0.048431396484375), vec2<f32>(0.78564453125, 0.03936767578125), vec2<f32>(0.77197265625, 0.03240966796875), vec2<f32>(0.7548828125, 0.0269775390625),
|
|
43
|
+
vec2<f32>(0.572265625, 0.427490234375), vec2<f32>(0.57373046875, 0.424072265625), vec2<f32>(0.57958984375, 0.403564453125), vec2<f32>(0.591796875, 0.3544921875),
|
|
44
|
+
vec2<f32>(0.6162109375, 0.2880859375), vec2<f32>(0.6552734375, 0.224853515625), vec2<f32>(0.69873046875, 0.172607421875), vec2<f32>(0.7353515625, 0.1318359375),
|
|
45
|
+
vec2<f32>(0.75927734375, 0.10089111328125), vec2<f32>(0.77001953125, 0.07745361328125), vec2<f32>(0.77197265625, 0.0599365234375), vec2<f32>(0.76611328125, 0.046844482421875),
|
|
46
|
+
vec2<f32>(0.751953125, 0.0369873046875), vec2<f32>(0.732421875, 0.029541015625), vec2<f32>(0.70947265625, 0.023834228515625), vec2<f32>(0.68359375, 0.019439697265625),
|
|
47
|
+
vec2<f32>(0.708984375, 0.291015625), vec2<f32>(0.708984375, 0.289794921875), vec2<f32>(0.7099609375, 0.28125), vec2<f32>(0.7099609375, 0.258544921875),
|
|
48
|
+
vec2<f32>(0.71142578125, 0.220458984375), vec2<f32>(0.7197265625, 0.1768798828125), vec2<f32>(0.734375, 0.1370849609375), vec2<f32>(0.748046875, 0.10479736328125),
|
|
49
|
+
vec2<f32>(0.755859375, 0.07989501953125), vec2<f32>(0.759765625, 0.061004638671875), vec2<f32>(0.75341796875, 0.046844482421875), vec2<f32>(0.73876953125, 0.036224365234375),
|
|
50
|
+
vec2<f32>(0.7177734375, 0.02825927734375), vec2<f32>(0.69189453125, 0.0222625732421875), vec2<f32>(0.6611328125, 0.0177001953125), vec2<f32>(0.62841796875, 0.01419830322265625),
|
|
51
|
+
vec2<f32>(0.80810546875, 0.1917724609375), vec2<f32>(0.8076171875, 0.1912841796875), vec2<f32>(0.80615234375, 0.18798828125), vec2<f32>(0.8017578125, 0.1781005859375),
|
|
52
|
+
vec2<f32>(0.79296875, 0.15869140625), vec2<f32>(0.78369140625, 0.1322021484375), vec2<f32>(0.775390625, 0.1048583984375), vec2<f32>(0.7685546875, 0.08111572265625),
|
|
53
|
+
vec2<f32>(0.7646484375, 0.061981201171875), vec2<f32>(0.75537109375, 0.047271728515625), vec2<f32>(0.740234375, 0.036163330078125), vec2<f32>(0.71875, 0.0277557373046875),
|
|
54
|
+
vec2<f32>(0.69091796875, 0.021484375), vec2<f32>(0.658203125, 0.0167388916015625), vec2<f32>(0.6220703125, 0.01316070556640625), vec2<f32>(0.583984375, 0.01041412353515625),
|
|
55
|
+
vec2<f32>(0.87841796875, 0.1217041015625), vec2<f32>(0.8779296875, 0.1217041015625), vec2<f32>(0.875, 0.12060546875), vec2<f32>(0.869140625, 0.116943359375),
|
|
56
|
+
vec2<f32>(0.85693359375, 0.1080322265625), vec2<f32>(0.837890625, 0.09375), vec2<f32>(0.81494140625, 0.076904296875), vec2<f32>(0.7958984375, 0.060699462890625),
|
|
57
|
+
vec2<f32>(0.7763671875, 0.046875), vec2<f32>(0.75634765625, 0.035919189453125), vec2<f32>(0.732421875, 0.0274505615234375), vec2<f32>(0.703125, 0.021026611328125),
|
|
58
|
+
vec2<f32>(0.6689453125, 0.01617431640625), vec2<f32>(0.63037109375, 0.01251220703125), vec2<f32>(0.58935546875, 0.00974273681640625), vec2<f32>(0.54638671875, 0.007633209228515625),
|
|
59
|
+
vec2<f32>(0.92626953125, 0.07379150390625), vec2<f32>(0.92578125, 0.07391357421875), vec2<f32>(0.9228515625, 0.07391357421875), vec2<f32>(0.9169921875, 0.0731201171875),
|
|
60
|
+
vec2<f32>(0.90380859375, 0.06982421875), vec2<f32>(0.88134765625, 0.0631103515625), vec2<f32>(0.85107421875, 0.053802490234375), vec2<f32>(0.8212890625, 0.043701171875),
|
|
61
|
+
vec2<f32>(0.791015625, 0.034393310546875), vec2<f32>(0.76123046875, 0.026611328125), vec2<f32>(0.72802734375, 0.02044677734375), vec2<f32>(0.69189453125, 0.015655517578125),
|
|
62
|
+
vec2<f32>(0.65087890625, 0.0120086669921875), vec2<f32>(0.607421875, 0.00925445556640625), vec2<f32>(0.56103515625, 0.0071563720703125), vec2<f32>(0.51416015625, 0.005565643310546875),
|
|
63
|
+
vec2<f32>(0.95751953125, 0.042327880859375), vec2<f32>(0.95703125, 0.042449951171875), vec2<f32>(0.95458984375, 0.042816162109375), vec2<f32>(0.94921875, 0.043182373046875),
|
|
64
|
+
vec2<f32>(0.93701171875, 0.04266357421875), vec2<f32>(0.9130859375, 0.040252685546875), vec2<f32>(0.8818359375, 0.035797119140625), vec2<f32>(0.8447265625, 0.0301361083984375),
|
|
65
|
+
vec2<f32>(0.80615234375, 0.0243377685546875), vec2<f32>(0.76708984375, 0.0191497802734375), vec2<f32>(0.7265625, 0.01485443115234375), vec2<f32>(0.6826171875, 0.01142120361328125),
|
|
66
|
+
vec2<f32>(0.63623046875, 0.0087738037109375), vec2<f32>(0.5869140625, 0.006740570068359375), vec2<f32>(0.53662109375, 0.005199432373046875), vec2<f32>(0.486083984375, 0.00402069091796875),
|
|
67
|
+
vec2<f32>(0.9775390625, 0.0226287841796875), vec2<f32>(0.97705078125, 0.0227508544921875), vec2<f32>(0.97509765625, 0.023193359375), vec2<f32>(0.9697265625, 0.0239105224609375),
|
|
68
|
+
vec2<f32>(0.95947265625, 0.0244903564453125), vec2<f32>(0.93603515625, 0.0241241455078125), vec2<f32>(0.9052734375, 0.02252197265625), vec2<f32>(0.865234375, 0.0197601318359375),
|
|
69
|
+
vec2<f32>(0.82177734375, 0.016510009765625), vec2<f32>(0.7744140625, 0.0132904052734375), vec2<f32>(0.7265625, 0.01045989990234375), vec2<f32>(0.67626953125, 0.0081329345703125),
|
|
70
|
+
vec2<f32>(0.6240234375, 0.00627899169921875), vec2<f32>(0.56982421875, 0.004825592041015625), vec2<f32>(0.51513671875, 0.0037136077880859375), vec2<f32>(0.46142578125, 0.0028629302978515625),
|
|
71
|
+
vec2<f32>(0.98876953125, 0.01107025146484375), vec2<f32>(0.98876953125, 0.01116180419921875), vec2<f32>(0.98681640625, 0.01151275634765625), vec2<f32>(0.98291015625, 0.01221466064453125),
|
|
72
|
+
vec2<f32>(0.97314453125, 0.01299285888671875), vec2<f32>(0.953125, 0.013519287109375), vec2<f32>(0.9228515625, 0.01328277587890625), vec2<f32>(0.88232421875, 0.01226043701171875),
|
|
73
|
+
vec2<f32>(0.8349609375, 0.01065826416015625), vec2<f32>(0.783203125, 0.00885009765625), vec2<f32>(0.728515625, 0.007114410400390625), vec2<f32>(0.671875, 0.00560760498046875),
|
|
74
|
+
vec2<f32>(0.61376953125, 0.004360198974609375), vec2<f32>(0.5546875, 0.0033721923828125), vec2<f32>(0.496337890625, 0.0025997161865234375), vec2<f32>(0.439453125, 0.0020046234130859375),
|
|
75
|
+
vec2<f32>(0.9951171875, 0.00479888916015625), vec2<f32>(0.9951171875, 0.004863739013671875), vec2<f32>(0.99365234375, 0.005096435546875), vec2<f32>(0.990234375, 0.005603790283203125),
|
|
76
|
+
vec2<f32>(0.9814453125, 0.0063323974609375), vec2<f32>(0.96435546875, 0.006977081298828125), vec2<f32>(0.93603515625, 0.007297515869140625), vec2<f32>(0.896484375, 0.0071258544921875),
|
|
77
|
+
vec2<f32>(0.8466796875, 0.00649261474609375), vec2<f32>(0.79150390625, 0.005596160888671875), vec2<f32>(0.7314453125, 0.004627227783203125), vec2<f32>(0.6689453125, 0.0037174224853515625),
|
|
78
|
+
vec2<f32>(0.60546875, 0.0029296875), vec2<f32>(0.54150390625, 0.00228118896484375), vec2<f32>(0.479248046875, 0.001766204833984375), vec2<f32>(0.419677734375, 0.00136566162109375),
|
|
79
|
+
vec2<f32>(0.998046875, 0.0017604827880859375), vec2<f32>(0.998046875, 0.0017938613891601562), vec2<f32>(0.99658203125, 0.0019292831420898438), vec2<f32>(0.994140625, 0.002239227294921875),
|
|
80
|
+
vec2<f32>(0.986328125, 0.0027294158935546875), vec2<f32>(0.9716796875, 0.0032501220703125), vec2<f32>(0.94580078125, 0.00366973876953125), vec2<f32>(0.90771484375, 0.003818511962890625),
|
|
81
|
+
vec2<f32>(0.8583984375, 0.003681182861328125), vec2<f32>(0.79931640625, 0.0033206939697265625), vec2<f32>(0.7353515625, 0.0028438568115234375), vec2<f32>(0.66748046875, 0.0023441314697265625),
|
|
82
|
+
vec2<f32>(0.5986328125, 0.0018796920776367188), vec2<f32>(0.5302734375, 0.0014829635620117188), vec2<f32>(0.464111328125, 0.0011587142944335938), vec2<f32>(0.402099609375, 0.0008993148803710938),
|
|
83
|
+
vec2<f32>(0.99951171875, 0.0005011558532714844), vec2<f32>(0.99951171875, 0.0005154609680175781), vec2<f32>(0.998046875, 0.000583648681640625), vec2<f32>(0.99658203125, 0.0007505416870117188),
|
|
84
|
+
vec2<f32>(0.9892578125, 0.00101470947265625), vec2<f32>(0.97607421875, 0.001346588134765625), vec2<f32>(0.95263671875, 0.0016527175903320312), vec2<f32>(0.916015625, 0.0018558502197265625),
|
|
85
|
+
vec2<f32>(0.8671875, 0.0019054412841796875), vec2<f32>(0.8076171875, 0.0018167495727539062), vec2<f32>(0.7392578125, 0.001621246337890625), vec2<f32>(0.6669921875, 0.0013799667358398438),
|
|
86
|
+
vec2<f32>(0.59326171875, 0.0011358261108398438), vec2<f32>(0.52001953125, 0.0009121894836425781), vec2<f32>(0.450439453125, 0.0007219314575195312), vec2<f32>(0.385986328125, 0.0005650520324707031),
|
|
87
|
+
vec2<f32>(1.0, 0.00009316205978393555), vec2<f32>(1.0, 0.0000978708267211914), vec2<f32>(0.9990234375, 0.00012540817260742188), vec2<f32>(0.9970703125, 0.00019216537475585938),
|
|
88
|
+
vec2<f32>(0.99072265625, 0.0003104209899902344), vec2<f32>(0.97900390625, 0.0004699230194091797), vec2<f32>(0.95751953125, 0.0006470680236816406), vec2<f32>(0.92333984375, 0.0007915496826171875),
|
|
89
|
+
vec2<f32>(0.87548828125, 0.0008769035339355469), vec2<f32>(0.81494140625, 0.0008869171142578125), vec2<f32>(0.744140625, 0.0008325576782226562), vec2<f32>(0.66748046875, 0.0007386207580566406),
|
|
90
|
+
vec2<f32>(0.58837890625, 0.0006265640258789062), vec2<f32>(0.51123046875, 0.0005168914794921875), vec2<f32>(0.438232421875, 0.0004165172576904297), vec2<f32>(0.37109375, 0.0003311634063720703),
|
|
91
|
+
vec2<f32>(1.0, 0.000007271766662597656), vec2<f32>(1.0, 0.000008165836334228516), vec2<f32>(0.9990234375, 0.000016987323760986328), vec2<f32>(0.99755859375, 0.00003790855407714844),
|
|
92
|
+
vec2<f32>(0.9921875, 0.00007593631744384766), vec2<f32>(0.9814453125, 0.00013744831085205078), vec2<f32>(0.96142578125, 0.00020754337310791016), vec2<f32>(0.92919921875, 0.00028014183044433594),
|
|
93
|
+
vec2<f32>(0.8828125, 0.0003345012664794922), vec2<f32>(0.82177734375, 0.00036263465881347656), vec2<f32>(0.7490234375, 0.00036215782165527344), vec2<f32>(0.66845703125, 0.0003380775451660156),
|
|
94
|
+
vec2<f32>(0.58544921875, 0.00029969215393066406), vec2<f32>(0.50390625, 0.00025582313537597656), vec2<f32>(0.427001953125, 0.0002117156982421875), vec2<f32>(0.357666015625, 0.00017201900482177734),
|
|
95
|
+
vec2<f32>(1.0, 0.0), vec2<f32>(1.0, 5.960464477539063e-8), vec2<f32>(0.99951171875, 0.0000012516975402832031), vec2<f32>(0.99755859375, 0.000005304813385009766),
|
|
96
|
+
vec2<f32>(0.9931640625, 0.000015079975128173828), vec2<f32>(0.98291015625, 0.00002855062484741211), vec2<f32>(0.96435546875, 0.00004744529724121094), vec2<f32>(0.93408203125, 0.00006842613220214844),
|
|
97
|
+
vec2<f32>(0.88916015625, 0.00008893013000488281), vec2<f32>(0.828125, 0.0001042485237121582), vec2<f32>(0.75390625, 0.00011217594146728516), vec2<f32>(0.67041015625, 0.00011241436004638672),
|
|
98
|
+
vec2<f32>(0.5830078125, 0.00010627508163452148), vec2<f32>(0.4970703125, 0.00009584426879882812), vec2<f32>(0.4169921875, 0.0000833272933959961), vec2<f32>(0.34521484375, 0.00007051229476928711),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
fn sampleThreeR184DfgLut(roughness : f32, dotNV : f32) -> vec2<f32> {
|
|
102
|
+
let uv = clamp(vec2<f32>(roughness, dotNV), vec2<f32>(0.0), vec2<f32>(1.0));
|
|
103
|
+
let samplePosition = uv * f32(THREE_R184_DFG_LUT_SIZE) - vec2<f32>(0.5);
|
|
104
|
+
let base = vec2<i32>(floor(samplePosition));
|
|
105
|
+
let weight = fract(samplePosition);
|
|
106
|
+
let last = i32(THREE_R184_DFG_LUT_SIZE - 1u);
|
|
107
|
+
let lo = clamp(base, vec2<i32>(0), vec2<i32>(last));
|
|
108
|
+
let hi = clamp(base + vec2<i32>(1), vec2<i32>(0), vec2<i32>(last));
|
|
109
|
+
let rowLo = mix(
|
|
110
|
+
THREE_R184_DFG_LUT[u32(lo.y) * THREE_R184_DFG_LUT_SIZE + u32(lo.x)],
|
|
111
|
+
THREE_R184_DFG_LUT[u32(lo.y) * THREE_R184_DFG_LUT_SIZE + u32(hi.x)],
|
|
112
|
+
weight.x,
|
|
113
|
+
);
|
|
114
|
+
let rowHi = mix(
|
|
115
|
+
THREE_R184_DFG_LUT[u32(hi.y) * THREE_R184_DFG_LUT_SIZE + u32(lo.x)],
|
|
116
|
+
THREE_R184_DFG_LUT[u32(hi.y) * THREE_R184_DFG_LUT_SIZE + u32(hi.x)],
|
|
117
|
+
weight.x,
|
|
118
|
+
);
|
|
119
|
+
return mix(rowLo, rowHi, weight.y);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
fn threeR184DirectMultiScatter(
|
|
123
|
+
roughness : f32,
|
|
124
|
+
nDotV : f32,
|
|
125
|
+
nDotL : f32,
|
|
126
|
+
F0 : vec3<f32>,
|
|
127
|
+
) -> vec3<f32> {
|
|
128
|
+
let dfgV = sampleThreeR184DfgLut(roughness, nDotV);
|
|
129
|
+
let dfgL = sampleThreeR184DfgLut(roughness, nDotL);
|
|
130
|
+
let fssEssV = F0 * dfgV.x + vec3<f32>(dfgV.y);
|
|
131
|
+
let fssEssL = F0 * dfgL.x + vec3<f32>(dfgL.y);
|
|
132
|
+
let emsV = 1.0 - dfgV.x - dfgV.y;
|
|
133
|
+
let emsL = 1.0 - dfgL.x - dfgL.y;
|
|
134
|
+
let favg = F0 + (vec3<f32>(1.0) - F0) * 0.047619;
|
|
135
|
+
let energyLoss = emsV * emsL;
|
|
136
|
+
let fms = fssEssV * fssEssL * favg
|
|
137
|
+
/ (vec3<f32>(1.0) - energyLoss * favg * favg + vec3<f32>(1e-6));
|
|
138
|
+
return fms * energyLoss;
|
|
25
139
|
}
|