@forgeax/engine-shader 0.1.6 → 0.1.19
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 +61 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/ShaderRegistry.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +53 -2
- package/dist/index.mjs.map +1 -1
- package/dist/material-schemas.d.ts.map +1 -1
- package/package.json +6 -3
- package/src/ShaderRegistry.ts +50 -0
- package/src/__tests__/bloom-fxaa-tonemap.unit.test.ts +86 -12
- package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +181 -0
- package/src/__tests__/lighting-punctual.unit.test.ts +1 -1
- package/src/__tests__/material-builtins.unit.test.ts +38 -1
- package/src/__tests__/material-contract.unit.test.ts +123 -0
- package/src/__tests__/motion-blur.unit.test.ts +44 -0
- package/src/__tests__/points-lines-shader.unit.test.ts +43 -0
- package/src/__tests__/scene-temporal.unit.test.ts +23 -5
- package/src/__tests__/shader-manifest-transmission.unit.test.ts +75 -0
- package/src/__tests__/shader.unit.test.ts +43 -3
- package/src/__tests__/sprite-variants.unit.test.ts +19 -0
- package/src/__tests__/taa-resolve.unit.test.ts +75 -3
- package/src/__tests__/transmission-thickness-scale.unit.test.ts +19 -0
- package/src/common.wgsl +30 -30
- package/src/default-standard-pbr-skin.wgsl +27 -9
- package/src/default-standard-pbr.wgsl +209 -28
- package/src/fxaa.wgsl +5 -6
- package/src/index.ts +3 -0
- package/src/material-schemas.ts +7 -0
- package/src/motion-blur.wgsl +77 -0
- package/src/pbr-temporal.wgsl +15 -2
- package/src/points-lines.wgsl +103 -0
- package/src/scene-temporal.wgsl +54 -0
- package/src/shadow_caster.wgsl +27 -2
- package/src/sprite-lit.wgsl +5 -4
- package/src/sprite.wgsl +5 -4
- package/src/taa-resolve.wgsl +76 -81
- package/src/tonemap.wgsl +7 -9
- package/src/unlit.wgsl +6 -5
- package/dist/__tests__/atmosphere-background.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-background.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-cubemap-orientation.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-cubemap-orientation.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-preetham-numeric.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-preetham-numeric.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-preetham-red.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-preetham-red.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-preetham.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-preetham.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-three-r184-reference.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-three-r184-reference.test.d.ts.map +0 -1
- package/dist/__tests__/bloom-fxaa-tonemap.unit.test.d.ts +0 -2
- package/dist/__tests__/bloom-fxaa-tonemap.unit.test.d.ts.map +0 -1
- package/dist/__tests__/builtin-texture-sampling-contract.test.d.ts +0 -2
- package/dist/__tests__/builtin-texture-sampling-contract.test.d.ts.map +0 -1
- package/dist/__tests__/default-standard-pbr-alpha.unit.test.d.ts +0 -2
- package/dist/__tests__/default-standard-pbr-alpha.unit.test.d.ts.map +0 -1
- package/dist/__tests__/deferred-lighting-ssao.test.d.ts +0 -2
- package/dist/__tests__/deferred-lighting-ssao.test.d.ts.map +0 -1
- package/dist/__tests__/fog-oracle.unit.test.d.ts +0 -2
- package/dist/__tests__/fog-oracle.unit.test.d.ts.map +0 -1
- package/dist/__tests__/fog-orthographic-ray.unit.test.d.ts +0 -2
- package/dist/__tests__/fog-orthographic-ray.unit.test.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-matrix.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-producer-matrix.integration.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-ssao-compile.test.d.ts +0 -2
- package/dist/__tests__/hdrp-ssao-compile.test.d.ts.map +0 -1
- package/dist/__tests__/ibl-irradiance.unit.test.d.ts +0 -2
- package/dist/__tests__/ibl-irradiance.unit.test.d.ts.map +0 -1
- package/dist/__tests__/ibl-sampling.unit.test.d.ts +0 -2
- package/dist/__tests__/ibl-sampling.unit.test.d.ts.map +0 -1
- package/dist/__tests__/lighting-punctual.unit.test.d.ts +0 -2
- package/dist/__tests__/lighting-punctual.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-artifact-dedup.unit.test.d.ts +0 -2
- package/dist/__tests__/material-artifact-dedup.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-builtins.unit.test.d.ts +0 -2
- package/dist/__tests__/material-builtins.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-contract.unit.test.d.ts +0 -2
- package/dist/__tests__/material-contract.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-derived-builtins.integration.test.d.ts +0 -2
- package/dist/__tests__/material-derived-builtins.integration.test.d.ts.map +0 -1
- package/dist/__tests__/public-surface.unit.test.d.ts +0 -2
- package/dist/__tests__/public-surface.unit.test.d.ts.map +0 -1
- package/dist/__tests__/scene-temporal.unit.test.d.ts +0 -2
- package/dist/__tests__/scene-temporal.unit.test.d.ts.map +0 -1
- package/dist/__tests__/shader-registry-transaction.unit.test.d.ts +0 -2
- package/dist/__tests__/shader-registry-transaction.unit.test.d.ts.map +0 -1
- package/dist/__tests__/shader.unit.test.d.ts +0 -2
- package/dist/__tests__/shader.unit.test.d.ts.map +0 -1
- package/dist/__tests__/sprite-lit-shader.test.d.ts +0 -2
- package/dist/__tests__/sprite-lit-shader.test.d.ts.map +0 -1
- package/dist/__tests__/sprite-variants.unit.test.d.ts +0 -2
- package/dist/__tests__/sprite-variants.unit.test.d.ts.map +0 -1
- package/dist/__tests__/taa-resolve.unit.test.d.ts +0 -2
- package/dist/__tests__/taa-resolve.unit.test.d.ts.map +0 -1
- package/dist/__tests__/transparent-pbr.unit.test.d.ts +0 -2
- package/dist/__tests__/transparent-pbr.unit.test.d.ts.map +0 -1
- package/dist/__tests__/vertex-color-variant.unit.test.d.ts +0 -2
- package/dist/__tests__/vertex-color-variant.unit.test.d.ts.map +0 -1
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
// Paradigm: each block-scoped describe('<source-filename>.test.ts', ...) preserves
|
|
17
17
|
// source as ancestorTitles[0]. Top-level imports merged + deduped.
|
|
18
18
|
|
|
19
|
+
import { readFileSync } from 'node:fs';
|
|
19
20
|
import type { Result, RhiError, ShaderModule } from '@forgeax/engine-rhi';
|
|
20
21
|
import { beforeAll, describe, expect, it } from 'vitest';
|
|
21
22
|
import type { ShaderError } from '../errors.js';
|
|
@@ -31,6 +32,19 @@ import {
|
|
|
31
32
|
ShaderRegistry,
|
|
32
33
|
} from '../ShaderRegistry.js';
|
|
33
34
|
|
|
35
|
+
{
|
|
36
|
+
const sceneTemporalSource = readFileSync(
|
|
37
|
+
new URL('../scene-temporal.wgsl', import.meta.url),
|
|
38
|
+
'utf8',
|
|
39
|
+
);
|
|
40
|
+
describe('scene-temporal accessor ABI', () => {
|
|
41
|
+
it('keeps the public shader module free of compiler and backend policy imports', () => {
|
|
42
|
+
expect(sceneTemporalSource).toContain('forgeax_scene_temporal');
|
|
43
|
+
expect(sceneTemporalSource).not.toMatch(/naga|shader-compiler|engine-rhi/i);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
34
48
|
{
|
|
35
49
|
// --- from fullscreen-triangle.test.ts ---
|
|
36
50
|
// @forgeax/engine-shader/__tests__/fullscreen-triangle.test.ts -
|
|
@@ -179,7 +193,7 @@ import {
|
|
|
179
193
|
});
|
|
180
194
|
});
|
|
181
195
|
|
|
182
|
-
describe('fxaa.wgsl SSOT gate (AC-05)', () => {
|
|
196
|
+
describe('fxaa.wgsl display-encoded SSOT gate (AC-05)', () => {
|
|
183
197
|
it('does NOT define fullscreen_triangle (SSOT is in common.wgsl)', () => {
|
|
184
198
|
expect(fxaaSource).not.toMatch(/fn fullscreen_triangle/);
|
|
185
199
|
});
|
|
@@ -191,6 +205,17 @@ import {
|
|
|
191
205
|
it('imports forgeax_view::common::FullscreenOutput', () => {
|
|
192
206
|
expect(fxaaSource).toContain('#import forgeax_view::common::FullscreenOutput');
|
|
193
207
|
});
|
|
208
|
+
|
|
209
|
+
it('declares display-encoded input/output without an OETF owner', () => {
|
|
210
|
+
expect(fxaaSource).toContain('display-encoded');
|
|
211
|
+
expect(fxaaSource).not.toContain('linearLdrColorDomain');
|
|
212
|
+
expect(fxaaSource).not.toContain('linearToSrgbOetf');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('returns encoded colors directly from early and final paths', () => {
|
|
216
|
+
expect(fxaaSource).toMatch(/return vec4<f32>\(centerColor, 1\.0\);/);
|
|
217
|
+
expect(fxaaSource).toMatch(/return vec4<f32>\(finalColor, 1\.0\);/);
|
|
218
|
+
});
|
|
194
219
|
});
|
|
195
220
|
|
|
196
221
|
describe('fxaa.wgsl bindings', () => {
|
|
@@ -779,6 +804,7 @@ import {
|
|
|
779
804
|
'#pragma variant_axis STORAGE_BUFFER_AVAILABLE',
|
|
780
805
|
'#pragma variant_axis CLUSTER_FORWARD_AVAILABLE',
|
|
781
806
|
'#pragma variant_axis VERTEX_COLOR_AVAILABLE',
|
|
807
|
+
'#pragma variant_axis TRANSMISSION_AVAILABLE',
|
|
782
808
|
]);
|
|
783
809
|
});
|
|
784
810
|
|
|
@@ -923,12 +949,19 @@ import {
|
|
|
923
949
|
'clearcoatRoughness',
|
|
924
950
|
'specularTint',
|
|
925
951
|
'normalScale',
|
|
952
|
+
'transmission',
|
|
953
|
+
'ior',
|
|
954
|
+
'thickness',
|
|
955
|
+
'attenuationColor',
|
|
956
|
+
'attenuationDistance',
|
|
926
957
|
'baseColorTexture',
|
|
927
958
|
'metallicRoughnessTexture',
|
|
928
959
|
'normalTexture',
|
|
929
960
|
'specularTintTexture',
|
|
930
961
|
'emissiveTexture',
|
|
931
962
|
'occlusionTexture',
|
|
963
|
+
'transmissionTexture',
|
|
964
|
+
'thicknessTexture',
|
|
932
965
|
] as const;
|
|
933
966
|
|
|
934
967
|
const STUB_WGSL = '// stub composed pbr-skin wgsl\n';
|
|
@@ -987,7 +1020,7 @@ import {
|
|
|
987
1020
|
const registry = makeMockRegistry();
|
|
988
1021
|
registerDefaultStandardPbrSkin(registry, STUB_WGSL, STORAGE_CAPS);
|
|
989
1022
|
const entry = lookup(registry);
|
|
990
|
-
expect(entry.paramSchema).toHaveLength(
|
|
1023
|
+
expect(entry.paramSchema).toHaveLength(28);
|
|
991
1024
|
});
|
|
992
1025
|
|
|
993
1026
|
it('paramSchema names match default-standard-pbr schema field-for-field', () => {
|
|
@@ -1020,7 +1053,12 @@ import {
|
|
|
1020
1053
|
'f32',
|
|
1021
1054
|
'f32',
|
|
1022
1055
|
'f32',
|
|
1023
|
-
// specular tint and
|
|
1056
|
+
// specular tint, transmission, and attenuation controls.
|
|
1057
|
+
'vec3',
|
|
1058
|
+
'f32',
|
|
1059
|
+
'f32',
|
|
1060
|
+
'f32',
|
|
1061
|
+
'f32',
|
|
1024
1062
|
'vec3',
|
|
1025
1063
|
'f32',
|
|
1026
1064
|
'texture2d',
|
|
@@ -1029,6 +1067,8 @@ import {
|
|
|
1029
1067
|
'texture2d',
|
|
1030
1068
|
'texture2d',
|
|
1031
1069
|
'texture2d',
|
|
1070
|
+
'texture2d',
|
|
1071
|
+
'texture2d',
|
|
1032
1072
|
]);
|
|
1033
1073
|
});
|
|
1034
1074
|
});
|
|
@@ -117,6 +117,7 @@ describe('w6 (b) -- pbr / unlit / sprite-adjacent shaders DO NOT pick up PER_INS
|
|
|
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 TRANSMISSION_AVAILABLE',
|
|
120
121
|
]);
|
|
121
122
|
});
|
|
122
123
|
|
|
@@ -207,3 +208,21 @@ describe('w6 (d) -- MAX_UNIFORM_INSTANCES=128 stays unchanged under the new axis
|
|
|
207
208
|
expect(src).toMatch(/MAX_UNIFORM_INSTANCES\s*=\s*128/);
|
|
208
209
|
});
|
|
209
210
|
});
|
|
211
|
+
|
|
212
|
+
describe('HDR/display fragment contract', () => {
|
|
213
|
+
for (const file of ['sprite.wgsl', 'sprite-lit.wgsl'] as const) {
|
|
214
|
+
it(`${file} keeps LDR OETF in fs_main and HDR output linear`, () => {
|
|
215
|
+
const src = stripComments(readWgsl(file));
|
|
216
|
+
const ldrStart = src.indexOf('fn fs_main(');
|
|
217
|
+
const hdrStart = src.indexOf('fn fs_main_hdr(');
|
|
218
|
+
expect(ldrStart).toBeGreaterThanOrEqual(0);
|
|
219
|
+
expect(hdrStart).toBeGreaterThan(ldrStart);
|
|
220
|
+
|
|
221
|
+
const ldrBody = src.slice(ldrStart, hdrStart);
|
|
222
|
+
const hdrBody = src.slice(hdrStart);
|
|
223
|
+
expect(ldrBody).toContain('linear_to_srgb(');
|
|
224
|
+
expect(hdrBody).not.toContain('linear_to_srgb(');
|
|
225
|
+
expect(hdrBody).toContain('return vec4<f32>');
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
});
|
|
@@ -3,24 +3,29 @@ import { resolve } from 'node:path';
|
|
|
3
3
|
import { describe, expect, it } from 'vitest';
|
|
4
4
|
|
|
5
5
|
const source = readFileSync(resolve(import.meta.dirname, '../taa-resolve.wgsl'), 'utf8');
|
|
6
|
+
const pbrTemporal = readFileSync(resolve(import.meta.dirname, '../pbr-temporal.wgsl'), 'utf8');
|
|
7
|
+
const sceneTemporal = readFileSync(resolve(import.meta.dirname, '../scene-temporal.wgsl'), 'utf8');
|
|
6
8
|
|
|
7
9
|
describe('taa-resolve.wgsl', () => {
|
|
8
10
|
it('keeps the fixed packed temporal and two-attachment ABI', () => {
|
|
9
11
|
expect(source).toContain('struct TaaResolveParams');
|
|
10
12
|
expect(source).toContain('@location(0) color');
|
|
11
13
|
expect(source).toContain('@location(1) temporal');
|
|
12
|
-
expect(source).toContain('
|
|
14
|
+
expect(source).toContain('return TaaResolveOutput(');
|
|
15
|
+
expect(source).toContain('temporal,');
|
|
13
16
|
expect(source).toContain('current.a');
|
|
17
|
+
expect(source).toContain('1.0 - clamp(temporal.w');
|
|
18
|
+
expect(source).not.toContain('1.0 - clamp(current.a');
|
|
14
19
|
});
|
|
15
20
|
|
|
16
21
|
it('contains every v1 rejection and adaptive weighting term', () => {
|
|
17
|
-
expect(source).toContain('uv + params.currentJitterUv');
|
|
22
|
+
expect(source).toContain('in.uv + params.currentJitterUv');
|
|
18
23
|
expect(source).toContain('in.uv - temporal.xy');
|
|
19
24
|
expect(source).toContain('closestCurrentTemporal(pixel, dimensions)');
|
|
20
25
|
expect(source).toContain('historyInBounds');
|
|
21
26
|
expect(source).toContain('depthDelta > depthThreshold');
|
|
22
27
|
expect(source).toContain('neighborhoodSquareMean');
|
|
23
|
-
expect(source).toContain('clamp(rgbToYCoCg(
|
|
28
|
+
expect(source).toContain('clamp(rgbToYCoCg(history.rgb), clipMin, clipMax)');
|
|
24
29
|
expect(source).toContain('reactiveFactor');
|
|
25
30
|
expect(source).toContain('velocityFactor');
|
|
26
31
|
expect(source).toContain('depthFactor');
|
|
@@ -28,5 +33,72 @@ describe('taa-resolve.wgsl', () => {
|
|
|
28
33
|
expect(source).toContain('mix(0.88, 0.97, lumaFactor)');
|
|
29
34
|
expect(source).toContain('params.temporalFrameIndex < 8u');
|
|
30
35
|
expect(source).toContain('progressiveWeight');
|
|
36
|
+
expect(source).toContain('let dimensions = vec2<i32>(textureDimensions(currentColor, 0));');
|
|
37
|
+
expect(source).toContain('closestCurrentTemporal(pixel, dimensions)');
|
|
38
|
+
expect(source).toContain('vec2<f32>(dimensions)');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('compiles the PBR temporal seam with coverage-aware reactive output', async () => {
|
|
42
|
+
const compilerPath = resolve(import.meta.dirname, '../../../shader-compiler/dist/index.mjs');
|
|
43
|
+
const { compileShader } = await import(/* @vite-ignore */ compilerPath);
|
|
44
|
+
const common = `
|
|
45
|
+
fn sampleMaterialTexture(
|
|
46
|
+
texture : texture_2d<f32>,
|
|
47
|
+
textureSampler : sampler,
|
|
48
|
+
uv : vec2<f32>,
|
|
49
|
+
uvScale : vec2<f32>,
|
|
50
|
+
) -> vec4<f32> {
|
|
51
|
+
return textureSample(texture, textureSampler, uv * uvScale);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
`;
|
|
55
|
+
const sceneTemporalBody = sceneTemporal
|
|
56
|
+
.replace(/^#define_import_path.*$/gm, '')
|
|
57
|
+
.replace(/^#import.*$/gm, '');
|
|
58
|
+
const entry = `${common}
|
|
59
|
+
${sceneTemporalBody}
|
|
60
|
+
${pbrTemporal.replace(/^#define_import_path.*$/gm, '').replace(/^#import.*$/gm, '')}
|
|
61
|
+
@group(0) @binding(0) var colorTexture : texture_2d<f32>;
|
|
62
|
+
@group(0) @binding(1) var colorSampler : sampler;
|
|
63
|
+
@fragment
|
|
64
|
+
fn fs_temporal_probe() -> @location(0) vec4<f32> {
|
|
65
|
+
return projectPbrSceneTemporal(
|
|
66
|
+
0.75, 0.5, colorTexture, colorSampler,
|
|
67
|
+
vec4<f32>(0.0, 0.0, 1.0, 1.0), vec4<f32>(0.0),
|
|
68
|
+
vec4<f32>(0.0, 0.0, 1.0, 1.0), vec4<f32>(0.0),
|
|
69
|
+
vec4<f32>(0.0, 0.0, 0.0, 0.0), 0.0,
|
|
70
|
+
vec2<f32>(0.5), vec2<f32>(0.5), vec2<f32>(0.5), vec2<f32>(0.5),
|
|
71
|
+
vec2<f32>(0.5), vec2<f32>(0.5), vec2<f32>(0.5), vec2<f32>(0.5),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
`;
|
|
75
|
+
const compiled = await compileShader(entry, {
|
|
76
|
+
id: 'test::pbr-temporal-reactive',
|
|
77
|
+
defines: { PER_INSTANCE_REGION: false, STORAGE_BUFFER_AVAILABLE: true },
|
|
78
|
+
});
|
|
79
|
+
expect(compiled.ok, compiled.ok ? undefined : String(compiled.error)).toBe(true);
|
|
80
|
+
if (!compiled.ok) return;
|
|
81
|
+
expect(compiled.value.wgsl).toContain('resolvePbrTemporalReactive');
|
|
82
|
+
expect(compiled.value.wgsl).toContain('max(clamp(reactive');
|
|
83
|
+
const discardIndex = pbrTemporal.indexOf('discard;');
|
|
84
|
+
const reactiveIndex = pbrTemporal.indexOf('let reactiveCoverage');
|
|
85
|
+
expect(discardIndex).toBeGreaterThanOrEqual(0);
|
|
86
|
+
expect(discardIndex).toBeLessThan(reactiveIndex);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('rejects a dev-only forced-reactive-zero falsifier', () => {
|
|
90
|
+
const reactiveCoverageLine =
|
|
91
|
+
'let reactiveCoverage = resolvePbrTemporalReactive(reactive, baseColorAlpha, baseSample.a);';
|
|
92
|
+
const falsified = pbrTemporal.replace(reactiveCoverageLine, 'let reactiveCoverage = 0.0;');
|
|
93
|
+
expect(() => {
|
|
94
|
+
expect(falsified).toContain(reactiveCoverageLine);
|
|
95
|
+
expect(falsified).toContain('let reactiveCoverage = 0.0;');
|
|
96
|
+
}).toThrow();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('keeps the reactive coverage contract explicit', () => {
|
|
100
|
+
expect(pbrTemporal).toContain('resolvePbrTemporalReactive');
|
|
101
|
+
expect(pbrTemporal).toContain('coverageReactive');
|
|
102
|
+
expect(pbrTemporal).not.toContain('return packSceneTemporal(currentClip, previousClip, 0.0)');
|
|
31
103
|
});
|
|
32
104
|
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { describe, expect, it } from 'vitest';
|
|
4
|
+
|
|
5
|
+
const shader = readFileSync(resolve(import.meta.dirname, '../default-standard-pbr.wgsl'), 'utf8');
|
|
6
|
+
|
|
7
|
+
describe('standard transmission thickness scale contract', () => {
|
|
8
|
+
it('converts local authored thickness through the combined local-to-world basis', () => {
|
|
9
|
+
expect(shader).toContain('let localToWorld = entityWorld * instanceLocal;');
|
|
10
|
+
expect(shader).toContain(
|
|
11
|
+
'let localToWorld = meshes[0].worldFromLocal * instances[in.instanceIdx].localFromInstance;',
|
|
12
|
+
);
|
|
13
|
+
expect(shader).toContain('let worldToLocal0 = vec3<f32>(');
|
|
14
|
+
expect(shader).toContain('let worldRefractedDirection =');
|
|
15
|
+
expect(shader).toMatch(
|
|
16
|
+
/finiteScalar\(material\.thickness, 0\.0\) \* length\(worldRefractedDirection\) \*\s+finiteScalar\(thicknessSample, 1\.0\)/,
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
});
|
package/src/common.wgsl
CHANGED
|
@@ -24,6 +24,9 @@ fn sampleMaterialTexture(
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// Shared linear-LDR -> display-sRGB OETF for raw swap-chain storage writes.
|
|
27
|
+
// The high branch deliberately locks Three.js r184's reference literal
|
|
28
|
+
// (`0.41666`) instead of spelling the mathematically exact `1.0 / 2.4`.
|
|
29
|
+
// That literal is part of the byte-level parity contract at 8-bit boundaries.
|
|
27
30
|
// sRGB texture views perform this conversion on attachment writes, but the
|
|
28
31
|
// native output and FXAA passes intentionally target the non-sRGB storage view.
|
|
29
32
|
fn linearToSrgbOetf(color : vec3<f32>) -> vec3<f32> {
|
|
@@ -278,6 +281,33 @@ struct SpotLightsArray {
|
|
|
278
281
|
};
|
|
279
282
|
|
|
280
283
|
@group(0) @binding(0) var<uniform> view : View;
|
|
284
|
+
|
|
285
|
+
// Shared screen-space ray reconstruction for scene Fog producers. Keeping the
|
|
286
|
+
// inverse-view and projection branch here prevents each producer from growing
|
|
287
|
+
// a second Fog formula or drifting from the View ABI.
|
|
288
|
+
fn fogWorldPoint(ndc : vec3<f32>) -> vec3<f32> {
|
|
289
|
+
let homogeneous = view.inverseViewProj * vec4<f32>(ndc, 1.0);
|
|
290
|
+
let divisor = select(1.0, homogeneous.w, abs(homogeneous.w) > 0.000001);
|
|
291
|
+
return homogeneous.xyz / divisor;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
fn fogRayFromNdc(ndc : vec3<f32>) -> FogRay {
|
|
295
|
+
let worldPosition = fogWorldPoint(ndc);
|
|
296
|
+
let nearPosition = fogWorldPoint(vec3<f32>(ndc.xy, 0.0));
|
|
297
|
+
let farPosition = fogWorldPoint(vec3<f32>(ndc.xy, 1.0));
|
|
298
|
+
let perspective = view.temporalProjection.z < 0.5;
|
|
299
|
+
let perspectiveVector = worldPosition - view.cameraPos;
|
|
300
|
+
let orthographicVector = farPosition - nearPosition;
|
|
301
|
+
let direction = normalize(select(orthographicVector, perspectiveVector, perspective));
|
|
302
|
+
let origin = select(nearPosition, view.cameraPos, perspective);
|
|
303
|
+
let rayDistance = select(
|
|
304
|
+
max(dot(worldPosition - nearPosition, direction), 0.0),
|
|
305
|
+
length(perspectiveVector),
|
|
306
|
+
perspective,
|
|
307
|
+
);
|
|
308
|
+
return FogRay(origin, direction, rayDistance);
|
|
309
|
+
}
|
|
310
|
+
|
|
281
311
|
#if STORAGE_BUFFER_AVAILABLE == true
|
|
282
312
|
@group(0) @binding(1) var<storage, read> pointLightsBuffer : PointLightsArray;
|
|
283
313
|
@group(0) @binding(2) var<storage, read> spotLightsBuffer : SpotLightsArray;
|
|
@@ -445,36 +475,6 @@ struct InstanceData {
|
|
|
445
475
|
#endif
|
|
446
476
|
};
|
|
447
477
|
|
|
448
|
-
fn sceneTemporalUv(clip : vec4<f32>) -> vec2<f32> {
|
|
449
|
-
let safeW = select(1e-6, clip.w, abs(clip.w) >= 1e-6);
|
|
450
|
-
let ndc = clip.xy / safeW;
|
|
451
|
-
return vec2<f32>(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
fn sceneTemporalViewDepth(clip : vec4<f32>) -> f32 {
|
|
455
|
-
let perspectiveDepth = max(clip.w, 0.0);
|
|
456
|
-
let ndcDepth = clip.z / max(abs(clip.w), 1e-6);
|
|
457
|
-
let orthographicDepth = view.temporalProjection.x +
|
|
458
|
-
ndcDepth * (view.temporalProjection.y - view.temporalProjection.x);
|
|
459
|
-
let viewDepth = select(
|
|
460
|
-
perspectiveDepth,
|
|
461
|
-
max(orthographicDepth, 0.0),
|
|
462
|
-
view.temporalProjection.z >= 0.5,
|
|
463
|
-
);
|
|
464
|
-
return log2(1.0 + viewDepth);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
fn packSceneTemporal(
|
|
468
|
-
currentClip : vec4<f32>,
|
|
469
|
-
previousClip : vec4<f32>,
|
|
470
|
-
reactive : f32,
|
|
471
|
-
) -> vec4<f32> {
|
|
472
|
-
return vec4<f32>(
|
|
473
|
-
sceneTemporalUv(currentClip) - sceneTemporalUv(previousClip),
|
|
474
|
-
sceneTemporalViewDepth(currentClip),
|
|
475
|
-
clamp(reactive, 0.0, 1.0),
|
|
476
|
-
);
|
|
477
|
-
}
|
|
478
478
|
#if STORAGE_BUFFER_AVAILABLE == true
|
|
479
479
|
@group(3) @binding(0) var<storage, read> instances : array<InstanceData>;
|
|
480
480
|
#else
|
|
@@ -88,6 +88,11 @@ struct Material {
|
|
|
88
88
|
clearcoatRoughness : f32,
|
|
89
89
|
specularTint : vec3<f32>,
|
|
90
90
|
normalScale : f32,
|
|
91
|
+
transmission : f32,
|
|
92
|
+
ior : f32,
|
|
93
|
+
thickness : f32,
|
|
94
|
+
attenuationColor : vec3<f32>,
|
|
95
|
+
attenuationDistance : f32,
|
|
91
96
|
baseColorTextureCoordinatesTransform : vec4<f32>,
|
|
92
97
|
baseColorTextureCoordinatesMetadata : vec4<f32>,
|
|
93
98
|
metallicRoughnessTextureCoordinatesTransform : vec4<f32>,
|
|
@@ -100,6 +105,10 @@ struct Material {
|
|
|
100
105
|
emissiveTextureCoordinatesMetadata : vec4<f32>,
|
|
101
106
|
occlusionTextureCoordinatesTransform : vec4<f32>,
|
|
102
107
|
occlusionTextureCoordinatesMetadata : vec4<f32>,
|
|
108
|
+
transmissionTextureCoordinatesTransform : vec4<f32>,
|
|
109
|
+
transmissionTextureCoordinatesMetadata : vec4<f32>,
|
|
110
|
+
thicknessTextureCoordinatesTransform : vec4<f32>,
|
|
111
|
+
thicknessTextureCoordinatesMetadata : vec4<f32>,
|
|
103
112
|
};
|
|
104
113
|
|
|
105
114
|
@group(1) @binding(0) var<uniform> material : Material;
|
|
@@ -115,6 +124,10 @@ struct Material {
|
|
|
115
124
|
@group(1) @binding(10) var emissiveTexture : texture_2d<f32>;
|
|
116
125
|
@group(1) @binding(11) var occlusionSampler : sampler;
|
|
117
126
|
@group(1) @binding(12) var occlusionTexture : texture_2d<f32>;
|
|
127
|
+
@group(1) @binding(13) var transmissionSampler : sampler;
|
|
128
|
+
@group(1) @binding(14) var transmissionTexture : texture_2d<f32>;
|
|
129
|
+
@group(1) @binding(15) var thicknessSampler : sampler;
|
|
130
|
+
@group(1) @binding(16) var thicknessTexture : texture_2d<f32>;
|
|
118
131
|
|
|
119
132
|
// Preserve filtering reflection for resources passed to the shared sampler.
|
|
120
133
|
fn materialTextureFilteringWitness() {
|
|
@@ -124,12 +137,16 @@ fn materialTextureFilteringWitness() {
|
|
|
124
137
|
let specularTint = specularTintTexture;
|
|
125
138
|
let emissive = emissiveTexture;
|
|
126
139
|
let occlusion = occlusionTexture;
|
|
140
|
+
let transmission = transmissionTexture;
|
|
141
|
+
let thickness = thicknessTexture;
|
|
127
142
|
let baseWitness = textureSample(base, baseColorSampler, vec2<f32>(0.0));
|
|
128
143
|
let metallicRoughnessWitness = textureSample(metallicRoughness, metallicRoughnessSampler, vec2<f32>(0.0));
|
|
129
144
|
let normalWitness = textureSample(normal, normalSampler, vec2<f32>(0.0));
|
|
130
145
|
let specularTintWitness = textureSample(specularTint, specularTintSampler, vec2<f32>(0.0));
|
|
131
146
|
let emissiveWitness = textureSample(emissive, emissiveSampler, vec2<f32>(0.0));
|
|
132
147
|
let occlusionWitness = textureSample(occlusion, occlusionSampler, vec2<f32>(0.0));
|
|
148
|
+
let transmissionWitness = textureSample(transmission, transmissionSampler, vec2<f32>(0.0));
|
|
149
|
+
let thicknessWitness = textureSample(thickness, thicknessSampler, vec2<f32>(0.0));
|
|
133
150
|
}
|
|
134
151
|
|
|
135
152
|
struct SkylightUniforms {
|
|
@@ -146,13 +163,13 @@ struct SkylightUniforms {
|
|
|
146
163
|
colorB : f32,
|
|
147
164
|
rotation : vec4<f32>,
|
|
148
165
|
};
|
|
149
|
-
@group(1) @binding(
|
|
150
|
-
@group(1) @binding(
|
|
151
|
-
@group(1) @binding(
|
|
152
|
-
@group(1) @binding(
|
|
153
|
-
@group(1) @binding(
|
|
154
|
-
@group(1) @binding(
|
|
155
|
-
@group(1) @binding(
|
|
166
|
+
@group(1) @binding(17) var irradianceMap : texture_cube<f32>;
|
|
167
|
+
@group(1) @binding(18) var irradianceSampler : sampler;
|
|
168
|
+
@group(1) @binding(19) var prefilterMap : texture_cube<f32>;
|
|
169
|
+
@group(1) @binding(20) var prefilterSampler : sampler;
|
|
170
|
+
@group(1) @binding(21) var brdfLut : texture_2d<f32>;
|
|
171
|
+
@group(1) @binding(22) var brdfLutSampler : sampler;
|
|
172
|
+
@group(1) @binding(23) var<uniform> skylight : SkylightUniforms;
|
|
156
173
|
|
|
157
174
|
#if STORAGE_BUFFER_AVAILABLE == true
|
|
158
175
|
@group(2) @binding(1) var<storage, read> palette : array<mat4x4<f32>>;
|
|
@@ -469,8 +486,8 @@ struct TemporalVsOut {
|
|
|
469
486
|
@location(5) uv5 : vec2<f32>,
|
|
470
487
|
@location(6) uv6 : vec2<f32>,
|
|
471
488
|
@location(7) uv7 : vec2<f32>,
|
|
472
|
-
@location(8) @interpolate(
|
|
473
|
-
@location(9) @interpolate(
|
|
489
|
+
@location(8) @interpolate(perspective) currentClip : vec4<f32>,
|
|
490
|
+
@location(9) @interpolate(perspective) previousClip : vec4<f32>,
|
|
474
491
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
475
492
|
@location(14) color : vec4<f32>,
|
|
476
493
|
#endif
|
|
@@ -531,6 +548,7 @@ fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
|
531
548
|
material.baseColorTextureCoordinatesMetadata,
|
|
532
549
|
in.currentClip,
|
|
533
550
|
in.previousClip,
|
|
551
|
+
view.temporalProjection,
|
|
534
552
|
reactive,
|
|
535
553
|
in.uv, in.uv1, in.uv2, in.uv3,
|
|
536
554
|
in.uv4, in.uv5, in.uv6, in.uv7,
|