@forgeax/engine-shader 0.1.21 → 0.1.24
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 +14 -5
- package/dist/ShaderRegistry.d.ts.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +33042 -55
- 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 +36 -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 +0 -17
- package/src/__tests__/default-standard-pbr-alpha.unit.test.ts +9 -3
- package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +67 -122
- package/src/__tests__/deferred-lighting-ssao.test.ts +3 -3
- package/src/__tests__/direct-light-layout.unit.test.ts +40 -0
- package/src/__tests__/ibl-irradiance.unit.test.ts +16 -4
- package/src/__tests__/ibl-sampling.unit.test.ts +3 -4
- package/src/__tests__/lighting-directional.unit.test.ts +36 -0
- package/src/__tests__/lighting-point-spot-volume.unit.test.ts +8 -2
- package/src/__tests__/lighting-punctual.unit.test.ts +40 -5
- 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-builtins.unit.test.ts +21 -3
- package/src/__tests__/material-contract.unit.test.ts +188 -14
- package/src/__tests__/physical-clearcoat.unit.test.ts +65 -0
- package/src/__tests__/probe-lighting-composition.unit.test.ts +39 -0
- package/src/__tests__/rect-area-brdf.unit.test.ts +36 -0
- package/src/__tests__/reflection-probe-sampling.unit.test.ts +24 -2
- package/src/__tests__/shader-manifest-transmission.unit.test.ts +8 -10
- package/src/__tests__/shader.unit.test.ts +54 -85
- package/src/__tests__/spot-cookie-composition.unit.test.ts +58 -0
- package/src/__tests__/spot-modifier-composition.unit.test.ts +19 -0
- package/src/__tests__/sprite-variants.unit.test.ts +4 -1
- package/src/__tests__/standard-webgl2-varying-budget.unit.test.ts +17 -0
- package/src/__tests__/surface-v1-negative.unit.test.ts +37 -0
- package/src/__tests__/surface-v1.unit.test.ts +83 -0
- package/src/__tests__/transmission-thickness-scale.unit.test.ts +2 -6
- package/src/__tests__/vertex-color-variant.unit.test.ts +16 -5
- package/src/brdf.wgsl +115 -1
- package/src/common.wgsl +53 -79
- package/src/default-standard-pbr-skin.wgsl +687 -144
- package/src/default-standard-pbr.wgsl +600 -247
- package/src/default_standard_surface.wgsl +88 -0
- package/src/hdrp-cluster-forward.wgsl +225 -0
- package/src/ibl-irradiance.wgsl +47 -9
- package/src/ibl-prefilter.wgsl +13 -3
- package/src/ibl-sampling.wgsl +2 -2
- package/src/ibl-shared.wgsl +9 -2
- package/src/index.ts +23 -0
- package/src/lighting-directional.wgsl +9 -117
- package/src/lighting-probe.wgsl +51 -0
- package/src/lighting-punctual.wgsl +21 -11
- package/src/lighting-rect-area.wgsl +162 -0
- package/src/lighting-spot-modifiers.wgsl +100 -0
- package/src/lighting-spot-projector.wgsl +31 -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/physical/anisotropy.wgsl +26 -0
- package/src/material/physical/clearcoat.wgsl +24 -0
- package/src/material/physical/iridescence.wgsl +23 -0
- package/src/material/physical/sheen.wgsl +17 -0
- package/src/material-schemas.ts +77 -30
- package/src/register-default-sprite-lit.ts +3 -6
- package/src/register-default-standard-pbr-skin.ts +2 -2
- package/src/sprite-lit.wgsl +3 -3
- package/src/sprite.wgsl +1 -1
- package/src/standard-cluster.wgsl +80 -78
- package/src/surface_v1.wgsl +26 -0
- package/src/types.ts +2 -1
- package/src/volume/volume-inject.wgsl +34 -36
- package/src/volume/volume-integrate.wgsl +48 -38
- package/dist/.tsbuildinfo +0 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The table source is the Three.js r184 RectAreaLight implementation. The
|
|
3
|
+
* source arrays are converted to IEEE-754 binary16 offline and checked into
|
|
4
|
+
* `tables.ts`; the runtime never fetches or parses a source file.
|
|
5
|
+
*/
|
|
6
|
+
export declare const LTC_SOURCE_PROVENANCE: {
|
|
7
|
+
readonly source: "Three.js";
|
|
8
|
+
readonly revision: "r184";
|
|
9
|
+
readonly sourceUrl: "https://github.com/mrdoob/three.js/blob/r184/examples/jsm/lights/RectAreaLightTexturesLib.js";
|
|
10
|
+
readonly upstreamData: "selfshadow/ltc_code fit/results/ltc.js";
|
|
11
|
+
readonly generator: "deterministic Three.js source-array to rgba16float conversion";
|
|
12
|
+
readonly inputSha256: {
|
|
13
|
+
readonly lambert: "cf5cf21e5c112d2095c7e2418cb0a1ac54636e275d73e42f3453646c67f26814";
|
|
14
|
+
readonly ggx: "3b1b09080b26104498db277c14fc1733786465c6958e7a8403d688b1e24c2ff5";
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=provenance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../../src/ltc/provenance.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;CAWxB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { LTC_SOURCE_PROVENANCE } from './provenance';
|
|
2
|
+
export { LTC_SOURCE_PROVENANCE };
|
|
3
|
+
export declare const LTC_TABLE_WIDTH = 64;
|
|
4
|
+
export declare const LTC_TABLE_HEIGHT = 64;
|
|
5
|
+
export declare const LTC_TABLES: {
|
|
6
|
+
readonly lambert: Uint16Array<ArrayBuffer>;
|
|
7
|
+
readonly ggx: Uint16Array<ArrayBuffer>;
|
|
8
|
+
};
|
|
9
|
+
export declare const LTC_TABLE_HASHES: {
|
|
10
|
+
readonly lambert: "a391de32f868fd4aa8774917b793174b7be804c08e2fb8924c30f31d7aa8dcd7";
|
|
11
|
+
readonly ggx: "fa1ecbc6deb3c85ddf603cdf1e98e30279444f905eb1cebb849d761b570dd696";
|
|
12
|
+
};
|
|
13
|
+
export declare const LTC_TABLE_INPUT_HASHES: {
|
|
14
|
+
readonly lambert: "cf5cf21e5c112d2095c7e2418cb0a1ac54636e275d73e42f3453646c67f26814";
|
|
15
|
+
readonly ggx: "3b1b09080b26104498db277c14fc1733786465c6958e7a8403d688b1e24c2ff5";
|
|
16
|
+
};
|
|
17
|
+
export declare function generateLtcTables(): {
|
|
18
|
+
lambert: Uint16Array;
|
|
19
|
+
ggx: Uint16Array;
|
|
20
|
+
};
|
|
21
|
+
export declare function hashLtcTable(table: Uint16Array): string;
|
|
22
|
+
//# sourceMappingURL=tables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../src/ltc/tables.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,EAAE,qBAAqB,EAAE,CAAC;AACjC,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAorFnC,eAAO,MAAM,UAAU;;;CAAkD,CAAC;AAC1E,eAAO,MAAM,gBAAgB;;;CAGnB,CAAC;AACX,eAAO,MAAM,sBAAsB;;;CAAoC,CAAC;AAExE,wBAAgB,iBAAiB,IAAI;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,WAAW,CAAA;CAAE,CAE9E;AAwED,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAEvD"}
|
|
@@ -1,13 +1,65 @@
|
|
|
1
1
|
import type { BindGroupLayoutDescriptor } from '@forgeax/engine-types';
|
|
2
|
+
export interface MaterialShaderResourceSlot {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly parameter: string;
|
|
5
|
+
readonly kind: 'sampler' | 'texture';
|
|
6
|
+
readonly group: number;
|
|
7
|
+
readonly binding: number;
|
|
8
|
+
}
|
|
9
|
+
export interface MaterialShaderVertexInput {
|
|
10
|
+
readonly semantic: string;
|
|
11
|
+
readonly location: number;
|
|
12
|
+
readonly format: string;
|
|
13
|
+
}
|
|
14
|
+
export interface MaterialShaderArtifactReceipt {
|
|
15
|
+
readonly directEntry: string;
|
|
16
|
+
readonly sceneIndexEntry: string;
|
|
17
|
+
readonly materialRow: {
|
|
18
|
+
readonly byteLength: number;
|
|
19
|
+
readonly fields: readonly string[];
|
|
20
|
+
};
|
|
21
|
+
readonly resourceSlots: readonly MaterialShaderResourceSlot[];
|
|
22
|
+
readonly uvSets: readonly {
|
|
23
|
+
readonly parameter: string;
|
|
24
|
+
readonly set: number;
|
|
25
|
+
}[];
|
|
26
|
+
readonly vertexInputs: readonly MaterialShaderVertexInput[];
|
|
27
|
+
readonly alphaMask: {
|
|
28
|
+
readonly cutoff: string;
|
|
29
|
+
readonly source: string;
|
|
30
|
+
};
|
|
31
|
+
readonly skinPaletteAddress?: {
|
|
32
|
+
readonly group: number;
|
|
33
|
+
readonly binding: number;
|
|
34
|
+
readonly stride: number;
|
|
35
|
+
};
|
|
36
|
+
readonly reflection: {
|
|
37
|
+
readonly layoutIdentity: string;
|
|
38
|
+
readonly resourceSlots: readonly MaterialShaderResourceSlot[];
|
|
39
|
+
readonly vertexInputs: readonly MaterialShaderVertexInput[];
|
|
40
|
+
};
|
|
41
|
+
readonly receiptIdentity: string;
|
|
42
|
+
readonly generation: number;
|
|
43
|
+
}
|
|
2
44
|
export interface MaterialShaderArtifact {
|
|
3
45
|
readonly material: string;
|
|
4
46
|
readonly pass: string;
|
|
5
47
|
readonly wgsl: string;
|
|
6
48
|
readonly layoutIdentity: string;
|
|
49
|
+
/** Naga-reflected UV set count used to derive clamp-to-last vertex aliases. */
|
|
50
|
+
readonly uvSetCount?: number;
|
|
7
51
|
readonly bindings: readonly BindGroupLayoutDescriptor[];
|
|
8
52
|
readonly deps: readonly string[];
|
|
9
53
|
readonly vertexInputs: readonly Readonly<Record<string, unknown>>[];
|
|
10
54
|
readonly specializationKey?: string;
|
|
55
|
+
/** Producer-owned ABI facts shared by direct and scene-index consumers. */
|
|
56
|
+
readonly receipt?: MaterialShaderArtifactReceipt;
|
|
11
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Builds the one Standard PBR ABI receipt consumed by both material lanes.
|
|
60
|
+
* The skinned module uses the same material producer and only adds its palette
|
|
61
|
+
* address; it must retain the same receipt identity for material compatibility.
|
|
62
|
+
*/
|
|
63
|
+
export declare function createStandardPbrArtifactReceipt(skinned?: boolean): MaterialShaderArtifactReceipt;
|
|
12
64
|
export declare function isMaterialShaderArtifact(value: unknown): value is MaterialShaderArtifact;
|
|
13
65
|
//# sourceMappingURL=artifact-types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-types.d.ts","sourceRoot":"","sources":["../../src/material/artifact-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAEvE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,SAAS,yBAAyB,EAAE,CAAC;IACxD,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IACpE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"artifact-types.d.ts","sourceRoot":"","sources":["../../src/material/artifact-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAEvE,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE;QACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;KACpC,CAAC;IACF,QAAQ,CAAC,aAAa,EAAE,SAAS,0BAA0B,EAAE,CAAC;IAC9D,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjF,QAAQ,CAAC,YAAY,EAAE,SAAS,yBAAyB,EAAE,CAAC;IAC5D,QAAQ,CAAC,SAAS,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzE,QAAQ,CAAC,kBAAkB,CAAC,EAAE;QAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,QAAQ,CAAC,UAAU,EAAE;QACnB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;QAChC,QAAQ,CAAC,aAAa,EAAE,SAAS,0BAA0B,EAAE,CAAC;QAC9D,QAAQ,CAAC,YAAY,EAAE,SAAS,yBAAyB,EAAE,CAAC;KAC7D,CAAC;IACF,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,+EAA+E;IAC/E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,yBAAyB,EAAE,CAAC;IACxD,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,YAAY,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IACpE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,CAAC,EAAE,6BAA6B,CAAC;CAClD;AA8DD;;;;GAIG;AACH,wBAAgB,gCAAgC,CAAC,OAAO,UAAQ,GAAG,6BAA6B,CA2B/F;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,sBAAsB,CAYxF"}
|
|
@@ -1,7 +1,43 @@
|
|
|
1
1
|
import type { ParamSchemaEntry } from '@forgeax/engine-types';
|
|
2
|
+
import { type MaterialShaderArtifactReceipt } from './material/artifact-types.js';
|
|
2
3
|
export declare const STANDARD_PBR_ALPHA_CUTOFF_DEFAULT = 0;
|
|
3
4
|
/** Shared material contract for the standard PBR and skinned PBR shaders. */
|
|
4
5
|
export declare const DEFAULT_STANDARD_PBR_PARAM_SCHEMA: readonly ParamSchemaEntry[];
|
|
6
|
+
export type { StandardPhysicalTextureField } from '@forgeax/engine-types';
|
|
7
|
+
/**
|
|
8
|
+
* Backward-compatible export name for callers that need the physical
|
|
9
|
+
* projection. Every entry is selected from the Standard root schema above;
|
|
10
|
+
* no independent defaults or binding inventory can drift from it.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Stable physical texture injection order. The order is part of the
|
|
14
|
+
* Standard template's resource ABI and is derived from the root schema by
|
|
15
|
+
* `standardPhysicalTextureFields`; render/cook must not maintain another
|
|
16
|
+
* field inventory.
|
|
17
|
+
*/
|
|
18
|
+
export { STANDARD_PHYSICAL_TEXTURE_FIELDS, standardPhysicalTextureFields, } from '@forgeax/engine-types';
|
|
19
|
+
export declare const STANDARD_PHYSICAL_LAYER_PARAM_SCHEMA: readonly ParamSchemaEntry[];
|
|
20
|
+
/**
|
|
21
|
+
* Canonical static Standard entry. It deliberately contains only the base
|
|
22
|
+
* contract (including the specular extension) so a base-only material keeps
|
|
23
|
+
* its Deferred path and carries no second-stage physical resources.
|
|
24
|
+
*/
|
|
25
|
+
export declare const STANDARD_BASE_PARAM_SCHEMA: readonly ParamSchemaEntry[];
|
|
26
|
+
/**
|
|
27
|
+
* Canonical boot-time user region for the shared Standard material BGL.
|
|
28
|
+
*
|
|
29
|
+
* It is still a projection of the root schema, but unlike the author-facing
|
|
30
|
+
* base-only projection it retains the pre-existing transmission texture pair.
|
|
31
|
+
* The template reserves those two pairs before the IBL/backdrop injection so
|
|
32
|
+
* a base-only shader and a transmissive shader can share one boot layout;
|
|
33
|
+
* physical texture pairs are never included here and are appended only for a
|
|
34
|
+
* root contract that declares them.
|
|
35
|
+
*/
|
|
36
|
+
export declare const STANDARD_PIPELINE_PARAM_SCHEMA: readonly ParamSchemaEntry[];
|
|
37
|
+
/** The single producer receipt shared by direct and scene-index Standard PBR. */
|
|
38
|
+
export declare const STANDARD_PBR_ARTIFACT_RECEIPT: MaterialShaderArtifactReceipt;
|
|
39
|
+
/** Skinned Standard PBR keeps the material receipt identity and adds palette ABI. */
|
|
40
|
+
export declare const STANDARD_PBR_SKIN_ARTIFACT_RECEIPT: MaterialShaderArtifactReceipt;
|
|
5
41
|
export declare const DEFAULT_UNLIT_PARAM_SCHEMA: readonly ParamSchemaEntry[];
|
|
6
42
|
export declare const DEFAULT_SPRITE_PARAM_SCHEMA: readonly ParamSchemaEntry[];
|
|
7
43
|
export declare const DEFAULT_MSDF_TEXT_PARAM_SCHEMA: readonly ParamSchemaEntry[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"material-schemas.d.ts","sourceRoot":"","sources":["../src/material-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"material-schemas.d.ts","sourceRoot":"","sources":["../src/material-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAK9D,OAAO,EAEL,KAAK,6BAA6B,EACnC,MAAM,8BAA8B,CAAC;AAEtC,eAAO,MAAM,iCAAiC,IAAI,CAAC;AAEnD,6EAA6E;AAC7E,eAAO,MAAM,iCAAiC,6BAAiC,CAAC;AAEhF,YAAY,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAC1E;;;;GAIG;AACH;;;;;GAKG;AACH,OAAO,EACL,gCAAgC,EAChC,6BAA6B,GAC9B,MAAM,uBAAuB,CAAC;AAE/B,eAAO,MAAM,oCAAoC,EAAE,SAAS,gBAAgB,EAGzE,CAAC;AAEJ;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,gBAAgB,EAY/D,CAAC;AAEJ;;;;;;;;;GASG;AACH,eAAO,MAAM,8BAA8B,EAAE,SAAS,gBAAgB,EAOnE,CAAC;AAEJ,iFAAiF;AACjF,eAAO,MAAM,6BAA6B,EAAE,6BACR,CAAC;AAErC,qFAAqF;AACrF,eAAO,MAAM,kCAAkC,EAAE,6BACT,CAAC;AAEzC,eAAO,MAAM,0BAA0B,EAAE,SAAS,gBAAgB,EAIjE,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,SAAS,gBAAgB,EAMlE,CAAC;AAEF,eAAO,MAAM,8BAA8B,EAAE,SAAS,gBAAgB,EAIrE,CAAC"}
|
|
@@ -22,12 +22,9 @@ export interface SpriteLitCaps {
|
|
|
22
22
|
* compiled by @forgeax/engine-vite-plugin-shader at build time and surfaced
|
|
23
23
|
* in manifest.json. The caller reads it from the manifest entries.
|
|
24
24
|
*
|
|
25
|
-
* `caps.storageBuffer`
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* The BGL itself is built downstream in runtime/pbr-pipeline.ts (sprite-lit
|
|
29
|
-
* reuses pbr-view / pbr-mesh-array / pbr-instances unchanged; AC-07
|
|
30
|
-
* BGL byte-identical).
|
|
25
|
+
* `caps.storageBuffer` is retained for the shared mesh/instance layout
|
|
26
|
+
* selection. Sprite-lit local lighting reads the same Standard Cluster group
|
|
27
|
+
* as PBR; the view group has no direct point/spot arrays.
|
|
31
28
|
*
|
|
32
29
|
* @param registry ShaderRegistry instance (engine boot path).
|
|
33
30
|
* @param composedWgsl Post-naga_oil composed WGSL source string.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register-default-sprite-lit.d.ts","sourceRoot":"","sources":["../src/register-default-sprite-lit.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAiBjD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED
|
|
1
|
+
{"version":3,"file":"register-default-sprite-lit.d.ts","sourceRoot":"","sources":["../src/register-default-sprite-lit.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAiBjD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,cAAc,EACxB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,aAAa,GAClB,IAAI,CAUN"}
|
package/dist/types.d.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* no longer carry a bindingLayout sidecar (§Change stance: no v1/v2
|
|
12
12
|
* dual-path, no incremental drop window).
|
|
13
13
|
*/
|
|
14
|
+
import type { MaterialShaderArtifactReceipt } from './material/artifact-types.js';
|
|
14
15
|
export interface MaterialShaderManifestVariant {
|
|
15
16
|
readonly definesKey: string;
|
|
16
17
|
readonly defines: Record<string, boolean>;
|
|
@@ -26,7 +27,7 @@ export interface MaterialShaderManifestVariant {
|
|
|
26
27
|
*
|
|
27
28
|
* For Engine.create() internal use only; AI users never see this type.
|
|
28
29
|
*/
|
|
29
|
-
export interface MaterialShaderManifestEntry {
|
|
30
|
+
export interface MaterialShaderManifestEntry extends Partial<MaterialShaderArtifactReceipt> {
|
|
30
31
|
readonly identifier: string;
|
|
31
32
|
readonly sourcePath: string;
|
|
32
33
|
readonly composedWgsl: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC;AAClF,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,2BAA4B,SAAQ,OAAO,CAAC,6BAA6B,CAAC;IACzF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,6BAA6B,EAAE,CAAC;IAC5D;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,2BAA2B,EAClC,UAAU,EAAE,MAAM,GACjB,6BAA6B,GAAG,SAAS,CAE3C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgeax/engine-shader",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.24",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
"LICENSE"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@forgeax/engine-rhi": "0.1.
|
|
26
|
-
"@forgeax/engine-types": "0.1.
|
|
25
|
+
"@forgeax/engine-rhi": "0.1.24",
|
|
26
|
+
"@forgeax/engine-types": "0.1.24",
|
|
27
27
|
"@webgpu/types": "^0.1.71"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@forgeax/engine-vite-plugin-shader": "0.1.
|
|
30
|
+
"@forgeax/engine-vite-plugin-shader": "0.1.24"
|
|
31
31
|
},
|
|
32
32
|
"forgeax": {
|
|
33
33
|
"metrics": {
|
package/src/ShaderRegistry.ts
CHANGED
|
@@ -356,23 +356,6 @@ export class ShaderRegistry {
|
|
|
356
356
|
}
|
|
357
357
|
variantKeys.add(variant.definesKey);
|
|
358
358
|
}
|
|
359
|
-
if (ms.identifier === 'forgeax::default-standard-pbr') {
|
|
360
|
-
const transmissionStates = new Set(
|
|
361
|
-
ms.variants
|
|
362
|
-
.filter((variant) => 'TRANSMISSION_AVAILABLE' in variant.defines)
|
|
363
|
-
.map((variant) => variant.defines.TRANSMISSION_AVAILABLE),
|
|
364
|
-
);
|
|
365
|
-
if (!transmissionStates.has(false) || !transmissionStates.has(true)) {
|
|
366
|
-
return err(
|
|
367
|
-
manifestMalformed({
|
|
368
|
-
message:
|
|
369
|
-
'ShaderRegistry: Standard material shader is missing a transmission variant state',
|
|
370
|
-
hint: 'forgeax::default-standard-pbr must declare TRANSMISSION_AVAILABLE=false and true',
|
|
371
|
-
reason: `declared transmission states: ${JSON.stringify([...transmissionStates])}`,
|
|
372
|
-
}),
|
|
373
|
-
);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
359
|
validatedMaterialShaderEntries.push(ms);
|
|
377
360
|
}
|
|
378
361
|
}
|
|
@@ -6,15 +6,21 @@ const shader = readFileSync(
|
|
|
6
6
|
fileURLToPath(new URL('../default-standard-pbr.wgsl', import.meta.url)),
|
|
7
7
|
'utf8',
|
|
8
8
|
);
|
|
9
|
+
const surface = readFileSync(
|
|
10
|
+
fileURLToPath(new URL('../default_standard_surface.wgsl', import.meta.url)),
|
|
11
|
+
'utf8',
|
|
12
|
+
);
|
|
9
13
|
|
|
10
14
|
describe('standard PBR MASK alpha contract', () => {
|
|
11
15
|
it('multiplies factor alpha by sampled texture alpha before discard', () => {
|
|
12
|
-
expect(
|
|
16
|
+
expect(surface).toContain(
|
|
17
|
+
'clamp(material.baseColor.a * baseSample.a * vertexColor.a, 0.0, 1.0)',
|
|
18
|
+
);
|
|
13
19
|
});
|
|
14
20
|
|
|
15
21
|
it('matches Three r184 alphaTest by discarding values equal to or below cutoff', () => {
|
|
16
|
-
expect(shader).toContain('
|
|
17
|
-
expect(shader).not.toContain('
|
|
22
|
+
expect(shader).toContain('surface.opacity <= surface.alphaClipThreshold');
|
|
23
|
+
expect(shader).not.toContain('surface.opacity < surface.alphaClipThreshold');
|
|
18
24
|
});
|
|
19
25
|
|
|
20
26
|
it('keeps alpha PBR on the shared Directional factor path', () => {
|
|
@@ -19,13 +19,17 @@ function device(): ShaderRegistryDevice {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
async function engineManifest(): Promise<EngineShaderManifest> {
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
engineManifestPromise ??= import('@forgeax/engine-vite-plugin-shader').then(
|
|
23
|
+
({ buildEngineShaderManifest }) => buildEngineShaderManifest(),
|
|
24
|
+
);
|
|
25
|
+
return engineManifestPromise;
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
let engineManifestPromise: Promise<EngineShaderManifest> | undefined;
|
|
29
|
+
|
|
26
30
|
describe('default Standard PBR transmission manifest contract', () => {
|
|
27
|
-
it('publishes
|
|
28
|
-
timeout:
|
|
31
|
+
it('publishes every supported capability combination including transmission', {
|
|
32
|
+
timeout: 60_000,
|
|
29
33
|
}, async () => {
|
|
30
34
|
const manifest = await engineManifest();
|
|
31
35
|
const standard = manifest.materialShaders.find(
|
|
@@ -34,86 +38,37 @@ describe('default Standard PBR transmission manifest contract', () => {
|
|
|
34
38
|
|
|
35
39
|
expect(standard).toBeDefined();
|
|
36
40
|
if (standard === undefined) return;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// combinations, i.e. 48 exact variants.
|
|
44
|
-
expect(transmissionVariants).toHaveLength(48);
|
|
45
|
-
expect(new Set(transmissionVariants.map((variant) => variant.definesKey)).size).toBe(48);
|
|
46
|
-
// Projector sampling is owned by the shared Cluster module. The
|
|
47
|
-
// non-cluster capability rows intentionally have no projector consumer
|
|
48
|
-
// after the direct-light/fallback cut, so their true/false projector keys
|
|
49
|
-
// alias one source instead of carrying duplicate WGSL. The 16 non-cluster
|
|
50
|
-
// pairs therefore collapse 48 logical keys to 32 source identities.
|
|
51
|
-
expect(new Set(transmissionVariants.map((variant) => variant.composedWgsl)).size).toBe(32);
|
|
52
|
-
const nonClusterPairs = transmissionVariants.filter(
|
|
53
|
-
(variant) => variant.defines.CLUSTER_FORWARD_AVAILABLE === false,
|
|
54
|
-
);
|
|
55
|
-
for (const variant of nonClusterPairs) {
|
|
56
|
-
const projectorTwin = nonClusterPairs.find(
|
|
57
|
-
(candidate) =>
|
|
58
|
-
candidate.defines.STORAGE_BUFFER_AVAILABLE === variant.defines.STORAGE_BUFFER_AVAILABLE &&
|
|
59
|
-
candidate.defines.TRANSMISSION_AVAILABLE === variant.defines.TRANSMISSION_AVAILABLE &&
|
|
60
|
-
candidate.defines.VERTEX_COLOR_AVAILABLE === variant.defines.VERTEX_COLOR_AVAILABLE &&
|
|
61
|
-
candidate.defines.DIRECTIONAL_PCSS_AVAILABLE ===
|
|
62
|
-
variant.defines.DIRECTIONAL_PCSS_AVAILABLE &&
|
|
63
|
-
candidate.defines.PROJECTOR_AVAILABLE !== variant.defines.PROJECTOR_AVAILABLE,
|
|
64
|
-
);
|
|
65
|
-
expect(projectorTwin?.composedWgsl).toBe(variant.composedWgsl);
|
|
66
|
-
}
|
|
41
|
+
// Nine declared axes produce 512 combinations. The compiler publishes
|
|
42
|
+
// the supported subset: storage is required by cluster/probe/extended
|
|
43
|
+
// lighting, and extended lighting also requires the projector lane.
|
|
44
|
+
expect(standard.variants).toHaveLength(224);
|
|
45
|
+
expect(new Set(standard.variants.map((variant) => variant.definesKey)).size).toBe(224);
|
|
46
|
+
expect(new Set(standard.variants.map((variant) => variant.composedWgsl)).size).toBe(224);
|
|
67
47
|
expect(
|
|
68
|
-
|
|
48
|
+
standard.variants.every(
|
|
69
49
|
(variant) =>
|
|
70
50
|
!(
|
|
71
51
|
variant.defines.CLUSTER_FORWARD_AVAILABLE === true &&
|
|
72
52
|
variant.defines.STORAGE_BUFFER_AVAILABLE === false
|
|
53
|
+
) &&
|
|
54
|
+
!(
|
|
55
|
+
variant.defines.PROBE_BLEND_AVAILABLE === true &&
|
|
56
|
+
variant.defines.STORAGE_BUFFER_AVAILABLE === false
|
|
57
|
+
) &&
|
|
58
|
+
!(
|
|
59
|
+
variant.defines.EXTENDED_LIGHTING_AVAILABLE === true &&
|
|
60
|
+
(variant.defines.STORAGE_BUFFER_AVAILABLE === false ||
|
|
61
|
+
variant.defines.PROJECTOR_AVAILABLE === false)
|
|
73
62
|
),
|
|
74
63
|
),
|
|
75
64
|
).toBe(true);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
for (const vertexColorAvailable of [false, true]) {
|
|
80
|
-
for (const directionalPcssAvailable of [false, true]) {
|
|
81
|
-
for (const transmissionAvailable of [false, true]) {
|
|
82
|
-
for (const projectorAvailable of [false, true]) {
|
|
83
|
-
const expectedKeyEntries = [
|
|
84
|
-
`CLUSTER_FORWARD_AVAILABLE=${clusterForwardAvailable}`,
|
|
85
|
-
`DIRECTIONAL_PCSS_AVAILABLE=${directionalPcssAvailable}`,
|
|
86
|
-
`PROJECTOR_AVAILABLE=${projectorAvailable}`,
|
|
87
|
-
`STORAGE_BUFFER_AVAILABLE=${storageBufferAvailable}`,
|
|
88
|
-
`TRANSMISSION_AVAILABLE=${transmissionAvailable}`,
|
|
89
|
-
`VERTEX_COLOR_AVAILABLE=${vertexColorAvailable}`,
|
|
90
|
-
];
|
|
91
|
-
const expectedKey = expectedKeyEntries.every((entry) => entry.endsWith('=true'))
|
|
92
|
-
? ''
|
|
93
|
-
: expectedKeyEntries.join('+');
|
|
94
|
-
expect(transmissionVariants).toContainEqual(
|
|
95
|
-
expect.objectContaining({
|
|
96
|
-
definesKey: expectedKey,
|
|
97
|
-
defines: expect.objectContaining({
|
|
98
|
-
CLUSTER_FORWARD_AVAILABLE: clusterForwardAvailable,
|
|
99
|
-
DIRECTIONAL_PCSS_AVAILABLE: directionalPcssAvailable,
|
|
100
|
-
PROJECTOR_AVAILABLE: projectorAvailable,
|
|
101
|
-
STORAGE_BUFFER_AVAILABLE: storageBufferAvailable,
|
|
102
|
-
TRANSMISSION_AVAILABLE: transmissionAvailable,
|
|
103
|
-
VERTEX_COLOR_AVAILABLE: vertexColorAvailable,
|
|
104
|
-
}),
|
|
105
|
-
}),
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
65
|
+
expect(standard.variants.every((variant) => 'TRANSMISSION_AVAILABLE' in variant.defines)).toBe(
|
|
66
|
+
true,
|
|
67
|
+
);
|
|
113
68
|
});
|
|
114
69
|
|
|
115
|
-
it('omits transmission
|
|
116
|
-
timeout:
|
|
70
|
+
it('omits transmission and physical resources from the non-transmission fallback variant', {
|
|
71
|
+
timeout: 60_000,
|
|
117
72
|
}, async () => {
|
|
118
73
|
const manifest = await engineManifest();
|
|
119
74
|
const standard = manifest.materialShaders.find(
|
|
@@ -126,23 +81,38 @@ describe('default Standard PBR transmission manifest contract', () => {
|
|
|
126
81
|
(variant) =>
|
|
127
82
|
variant.defines.CLUSTER_FORWARD_AVAILABLE === false &&
|
|
128
83
|
variant.defines.STORAGE_BUFFER_AVAILABLE === true &&
|
|
84
|
+
variant.defines.EXTENDED_LIGHTING_AVAILABLE === false &&
|
|
85
|
+
variant.defines.PROBE_BLEND_AVAILABLE === false &&
|
|
129
86
|
variant.defines.TRANSMISSION_AVAILABLE === false &&
|
|
130
87
|
variant.defines.VERTEX_COLOR_AVAILABLE === false,
|
|
131
88
|
);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
89
|
+
expect(fallback).toBeDefined();
|
|
90
|
+
if (fallback === undefined) return;
|
|
91
|
+
expect(fallback.composedWgsl).not.toMatch(
|
|
92
|
+
/var(?:<[^>]+>)?\s+(?:transmissionSampler|transmissionTexture|thicknessSampler|thicknessTexture|clearcoatTexture|clearcoatRoughnessTexture|clearcoatNormalTexture|anisotropyTexture|sheenColorTexture|sheenRoughnessTexture|iridescenceTexture|iridescenceThicknessTexture|specularTexture)\b/u,
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('publishes the same explicit transmission axis for the skin Standard template', {
|
|
97
|
+
timeout: 60_000,
|
|
98
|
+
}, async () => {
|
|
99
|
+
const manifest = await engineManifest();
|
|
100
|
+
const skin = manifest.materialShaders.find((entry) => entry.identifier === 'forgeax::pbr-skin');
|
|
101
|
+
expect(skin).toBeDefined();
|
|
102
|
+
if (skin === undefined) return;
|
|
103
|
+
expect(skin.variants.length).toBeGreaterThan(0);
|
|
104
|
+
expect(skin.variants.every((variant) => 'TRANSMISSION_AVAILABLE' in variant.defines)).toBe(
|
|
105
|
+
true,
|
|
106
|
+
);
|
|
107
|
+
expect(skin.variants.some((variant) => variant.defines.TRANSMISSION_AVAILABLE === false)).toBe(
|
|
108
|
+
true,
|
|
109
|
+
);
|
|
110
|
+
expect(skin.variants.some((variant) => variant.defines.TRANSMISSION_AVAILABLE === true)).toBe(
|
|
111
|
+
true,
|
|
138
112
|
);
|
|
139
|
-
expect(fallback?.composedWgsl).not.toContain('@binding(13)');
|
|
140
|
-
expect(fallback?.composedWgsl).not.toContain('@binding(24)');
|
|
141
|
-
expect(transmission?.composedWgsl).toContain('@binding(13)');
|
|
142
|
-
expect(transmission?.composedWgsl).toContain('@binding(24)');
|
|
143
113
|
});
|
|
144
114
|
|
|
145
|
-
it('registers one Standard row and resolves a declared variant by exact key', {
|
|
115
|
+
it('registers one Standard row and resolves a declared capability variant by exact key', {
|
|
146
116
|
timeout: 20_000,
|
|
147
117
|
}, async () => {
|
|
148
118
|
const manifest = await engineManifest();
|
|
@@ -166,38 +136,13 @@ describe('default Standard PBR transmission manifest contract', () => {
|
|
|
166
136
|
expect(registered).toBeDefined();
|
|
167
137
|
if (registered === undefined) return;
|
|
168
138
|
|
|
169
|
-
const
|
|
170
|
-
(
|
|
139
|
+
const variant = registered.variants.find(
|
|
140
|
+
(candidate) => candidate.defines.CLUSTER_FORWARD_AVAILABLE === false,
|
|
171
141
|
);
|
|
172
|
-
expect(
|
|
173
|
-
if (
|
|
174
|
-
expect(findVariantByKey(registered,
|
|
175
|
-
expect(
|
|
176
|
-
findVariantByKey(registered, `${transmission.definesKey}+UNDECLARED=true`),
|
|
177
|
-
).toBeUndefined();
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('rejects a manifest with a missing transmission variant before prewarm', async () => {
|
|
181
|
-
const missingVariant = {
|
|
182
|
-
identifier: 'forgeax::default-standard-pbr',
|
|
183
|
-
sourcePath: 'default-standard-pbr.wgsl',
|
|
184
|
-
composedWgsl: 'standard-default',
|
|
185
|
-
paramSchema: '[]',
|
|
186
|
-
variants: [
|
|
187
|
-
{
|
|
188
|
-
definesKey: 'TRANSMISSION_AVAILABLE=false',
|
|
189
|
-
defines: { TRANSMISSION_AVAILABLE: false },
|
|
190
|
-
composedWgsl: 'standard-no-transmission',
|
|
191
|
-
},
|
|
192
|
-
],
|
|
193
|
-
};
|
|
194
|
-
const registry = new ShaderRegistry({
|
|
195
|
-
device: device(),
|
|
196
|
-
manifestUrl: dataUrl({ entries: [], materialShaders: [missingVariant] }),
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
const loaded = await registry.loadManifest();
|
|
200
|
-
expect(loaded.ok).toBe(false);
|
|
142
|
+
expect(variant).toBeDefined();
|
|
143
|
+
if (variant === undefined) return;
|
|
144
|
+
expect(findVariantByKey(registered, variant.definesKey)).toBe(variant);
|
|
145
|
+
expect(findVariantByKey(registered, `${variant.definesKey}+UNDECLARED=true`)).toBeUndefined();
|
|
201
146
|
});
|
|
202
147
|
|
|
203
148
|
it('rejects duplicate variant keys instead of allowing ambiguous prewarm', async () => {
|
|
@@ -208,14 +153,14 @@ describe('default Standard PBR transmission manifest contract', () => {
|
|
|
208
153
|
paramSchema: '[]',
|
|
209
154
|
variants: [
|
|
210
155
|
{
|
|
211
|
-
definesKey: '
|
|
212
|
-
defines: {
|
|
213
|
-
composedWgsl: 'standard-
|
|
156
|
+
definesKey: 'CLUSTER_FORWARD_AVAILABLE=false',
|
|
157
|
+
defines: { CLUSTER_FORWARD_AVAILABLE: false },
|
|
158
|
+
composedWgsl: 'standard-a',
|
|
214
159
|
},
|
|
215
160
|
{
|
|
216
|
-
definesKey: '
|
|
217
|
-
defines: {
|
|
218
|
-
composedWgsl: 'standard-
|
|
161
|
+
definesKey: 'CLUSTER_FORWARD_AVAILABLE=false',
|
|
162
|
+
defines: { CLUSTER_FORWARD_AVAILABLE: false },
|
|
163
|
+
composedWgsl: 'standard-b',
|
|
219
164
|
},
|
|
220
165
|
],
|
|
221
166
|
};
|
|
@@ -51,12 +51,12 @@ function stripComments(src: string): string {
|
|
|
51
51
|
|
|
52
52
|
describe('deferred lighting ambient SSAO synthesis (M2 / w11)', () => {
|
|
53
53
|
it('(a) ambient calculation uses occlusionStrength * bakedAO texture sample', () => {
|
|
54
|
-
// The ambient expression starts from kD * irradiance before
|
|
55
|
-
//
|
|
54
|
+
// The ambient expression starts from kD * irradiance before skylight terms
|
|
55
|
+
// are applied; ao comes from material.occlusionStrength.
|
|
56
56
|
// M7/w32 changed `let` -> `var` so the SSAO blend line `ambient *= mix(...)` can mutate it.
|
|
57
57
|
const codeOnly = stripComments(pbrSource);
|
|
58
58
|
expect(codeOnly).toMatch(/var\s+ambient\s*=\s*\(*\s*kD\s*\*\s*irradiance/);
|
|
59
|
-
expect(codeOnly).toMatch(/
|
|
59
|
+
expect(codeOnly).toMatch(/surface\.occlusion/);
|
|
60
60
|
expect(codeOnly).toMatch(/skylight\.intensity/);
|
|
61
61
|
});
|
|
62
62
|
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
describe('DirectLightSlot WGSL ABI', () => {
|
|
9
|
+
it('declares the canonical five-row 80-byte slot', async () => {
|
|
10
|
+
const common = await readShader('common.wgsl');
|
|
11
|
+
const match = common.match(/struct DirectLightSlot\s*\{([\s\S]*?)\};/);
|
|
12
|
+
|
|
13
|
+
expect(match).not.toBeNull();
|
|
14
|
+
const struct = match?.[1] ?? '';
|
|
15
|
+
expect(struct.match(/vec4<f32>/g)).toHaveLength(4);
|
|
16
|
+
expect(struct.match(/vec4<u32>/g)).toHaveLength(1);
|
|
17
|
+
expect(struct).toContain('row0');
|
|
18
|
+
expect(struct).toContain('row1');
|
|
19
|
+
expect(struct).toContain('row2');
|
|
20
|
+
expect(struct).toContain('row3');
|
|
21
|
+
expect(struct).toContain('metadata : vec4<u32>');
|
|
22
|
+
expect(common).not.toContain('struct DirectLightArray');
|
|
23
|
+
expect(common).not.toContain('array<DirectLightSlot, 4>');
|
|
24
|
+
expect(common).toContain('@group(0) @binding(0)');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('keeps the slot type owned by the unified Cluster shader consumer', async () => {
|
|
28
|
+
const common = await readShader('common.wgsl');
|
|
29
|
+
const standard = await readShader('default-standard-pbr.wgsl');
|
|
30
|
+
const clustered = await readShader('standard-cluster.wgsl');
|
|
31
|
+
|
|
32
|
+
expect(common).toContain('struct DirectLightSlot');
|
|
33
|
+
expect(standard).not.toContain('DirectLightSlot');
|
|
34
|
+
expect(standard).not.toContain('pointLightsBuffer');
|
|
35
|
+
expect(standard).not.toContain('spotLightsBuffer');
|
|
36
|
+
expect(clustered).toContain('DirectLightSlot');
|
|
37
|
+
expect(clustered).toContain('array<DirectLightSlot, 256>');
|
|
38
|
+
expect(clustered).not.toContain('struct LightSlot');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -27,17 +27,29 @@ beforeAll(async () => {
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
describe('IBL irradiance payload contract', () => {
|
|
30
|
-
it('records that convolution
|
|
31
|
-
expect(irradianceSource).toContain('
|
|
30
|
+
it('records that convolution stores Lambert-normalized E over pi', () => {
|
|
31
|
+
expect(irradianceSource).toContain('IRRADIANCE_PAYLOAD_E_OVER_PI');
|
|
32
32
|
expect(irradianceSource).toMatch(/irradiance\s*=\s*PI\s*\*\s*irradiance/);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
it('keeps the constant-environment analytic oracle finite and explicit', () => {
|
|
36
36
|
const environment = 0.72;
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
// The hemisphere integral is E=pi*L for a constant environment. The
|
|
38
|
+
// convolution shader's PI*sampleAverage stores E/pi=L directly, which is
|
|
39
|
+
// the Lambert diffuse radiance consumed by the material shader.
|
|
40
|
+
const convolvedPayload = environment;
|
|
41
|
+
const diffuseRadiance = convolvedPayload;
|
|
39
42
|
|
|
40
43
|
expect(diffuseRadiance).toBeCloseTo(environment, 12);
|
|
41
44
|
expect(Number.isFinite(diffuseRadiance)).toBe(true);
|
|
42
45
|
});
|
|
46
|
+
|
|
47
|
+
it('rejects NaN and infinity before a sample reaches the accumulation sum', () => {
|
|
48
|
+
expect(irradianceSource).toMatch(/const IRRADIANCE_SAMPLE_DELTA:\s*f32\s*=\s*0\.05/);
|
|
49
|
+
expect(irradianceSource).toContain('fn irradianceFiniteScalar');
|
|
50
|
+
expect(irradianceSource).toContain('abs(value) <= 65504.0');
|
|
51
|
+
expect(irradianceSource).toMatch(
|
|
52
|
+
/irradianceFiniteVec3\(sampleVec\)[\s\S]*irradianceFiniteVec3\(sampleColor\)[\s\S]*irradianceFiniteScalar\(sampleWeight\)/,
|
|
53
|
+
);
|
|
54
|
+
});
|
|
43
55
|
});
|