@forgeax/engine-shader 0.1.28 → 0.1.29
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/NOTICE +35 -0
- package/README.md +39 -0
- package/dist/ShaderRegistry.d.ts +8 -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 +312 -71
- package/dist/index.mjs.map +1 -1
- package/dist/material/artifact-types.d.ts +12 -2
- 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/package.json +5 -4
- package/src/ShaderRegistry.ts +10 -0
- package/src/__tests__/auto-exposure-graph.unit.test.ts +59 -0
- package/src/__tests__/bloom-fxaa-tonemap.unit.test.ts +30 -15
- package/src/__tests__/builtin-texture-sampling-contract.test.ts +1 -1
- package/src/__tests__/default-standard-pbr-alpha.unit.test.ts +2 -1
- package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +167 -6
- package/src/__tests__/direct-light-layout.unit.test.ts +1 -1
- package/src/__tests__/lighting-punctual.unit.test.ts +29 -1
- package/src/__tests__/ltc-provenance.unit.test.ts +13 -2
- package/src/__tests__/material-builtins.unit.test.ts +1 -0
- package/src/__tests__/material-contract.unit.test.ts +42 -9
- package/src/__tests__/material-derived-builtins.integration.test.ts +18 -2
- package/src/__tests__/probe-lighting-composition.unit.test.ts +1 -1
- package/src/__tests__/reflection-probe-sampling.unit.test.ts +23 -2
- package/src/__tests__/scene-temporal.unit.test.ts +10 -0
- package/src/__tests__/shader.unit.test.ts +1 -0
- package/src/__tests__/sprite-variants.unit.test.ts +1 -0
- package/src/__tests__/ssr-artifact.unit.test.ts +141 -0
- package/src/__tests__/ssr-bgl.integration.test.ts +185 -0
- package/src/__tests__/standard-output-domain.unit.test.ts +36 -0
- package/src/__tests__/standard-pbr-artifact-receipt.unit.test.ts +93 -0
- package/src/__tests__/standard-surface-pass-evaluation.integration.test.ts +16 -4
- package/src/__tests__/surface-v1.unit.test.ts +6 -0
- package/src/__tests__/taa-resolve.unit.test.ts +20 -9
- package/src/__tests__/transmission-thickness-scale.unit.test.ts +6 -1
- package/src/__tests__/transparent-pbr.unit.test.ts +1 -1
- package/src/__tests__/vertex-color-variant.unit.test.ts +4 -1
- package/src/atmosphere-background.wgsl +3 -3
- package/src/auto-exposure-meter.wgsl +179 -0
- package/src/color-lut.wgsl +17 -0
- package/src/common.wgsl +7 -5
- package/src/default-standard-pbr-skin.wgsl +142 -40
- package/src/default-standard-pbr.wgsl +198 -82
- package/src/default_standard_surface.wgsl +56 -19
- package/src/fxaa.wgsl +9 -5
- package/src/ibl-sampling.wgsl +30 -6
- package/src/index.ts +189 -0
- package/src/lighting-punctual.wgsl +5 -6
- package/src/material/artifact-types.ts +109 -63
- package/src/material-schemas.ts +84 -8
- package/src/output-encoding.wgsl +10 -0
- package/src/pbr-temporal.wgsl +9 -1
- package/src/scene-temporal.wgsl +2 -0
- package/src/shadow-pcf.wgsl +6 -8
- package/src/shadow-surface.wgsl +16 -0
- package/src/shadow_caster.wgsl +99 -61
- package/src/sprite-lit.wgsl +4 -4
- package/src/sprite.wgsl +3 -3
- package/src/ssr-compose.wgsl +129 -0
- package/src/ssr-hiz-reduce.wgsl +53 -0
- package/src/ssr-hiz.wgsl +66 -0
- package/src/ssr-temporal.wgsl +305 -0
- package/src/ssr-trace.wgsl +784 -0
- package/src/standard-cluster.wgsl +6 -4
- package/src/standard-surface.wgsl +696 -0
- package/src/surface_v1.wgsl +6 -0
- package/src/taa-resolve.wgsl +222 -28
- package/src/tbn.wgsl +1 -1
- package/src/tonemap.wgsl +37 -18
- package/src/unlit.wgsl +3 -3
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
type BindGroupLayoutDescriptor,
|
|
3
|
+
derive,
|
|
4
|
+
type MaterialParticleInput,
|
|
5
|
+
type ParamSchemaEntry,
|
|
6
|
+
STANDARD_MATERIAL_PARAM_SCHEMA,
|
|
7
|
+
STANDARD_PHYSICAL_TEXTURE_FIELDS,
|
|
8
|
+
} from '@forgeax/engine-types';
|
|
2
9
|
|
|
3
10
|
export interface MaterialShaderResourceSlot {
|
|
4
11
|
readonly name: string;
|
|
@@ -49,44 +56,59 @@ export interface MaterialShaderArtifact {
|
|
|
49
56
|
readonly bindings: readonly BindGroupLayoutDescriptor[];
|
|
50
57
|
readonly deps: readonly string[];
|
|
51
58
|
readonly vertexInputs: readonly Readonly<Record<string, unknown>>[];
|
|
59
|
+
/** Reflection-owned dynamic values supplied by a VFX renderer. */
|
|
60
|
+
readonly particleInputs?: readonly MaterialParticleInput[];
|
|
52
61
|
readonly specializationKey?: string;
|
|
53
62
|
/** Producer-owned ABI facts shared by direct and scene-index consumers. */
|
|
54
63
|
readonly receipt?: MaterialShaderArtifactReceipt;
|
|
55
64
|
}
|
|
56
65
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Canonical Standard material schema consumed by the scene-index row.
|
|
68
|
+
* Physical texture handles stay in the fixed injected range (26..45); their
|
|
69
|
+
* numeric factors remain in this row, after the seven stable user texture
|
|
70
|
+
* coordinate pairs. Keeping this projection beside the receipt lets the
|
|
71
|
+
* shader producer and the render-side table consume one schema identity.
|
|
72
|
+
*/
|
|
73
|
+
export const STANDARD_PIPELINE_PARAM_SCHEMA: readonly ParamSchemaEntry[] =
|
|
74
|
+
STANDARD_MATERIAL_PARAM_SCHEMA.filter(
|
|
75
|
+
(entry) =>
|
|
76
|
+
!(
|
|
77
|
+
entry.type.startsWith('texture') &&
|
|
78
|
+
STANDARD_PHYSICAL_TEXTURE_FIELDS.includes(
|
|
79
|
+
entry.name as (typeof STANDARD_PHYSICAL_TEXTURE_FIELDS)[number],
|
|
80
|
+
)
|
|
81
|
+
),
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const STANDARD_PIPELINE_DERIVED = derive(STANDARD_PIPELINE_PARAM_SCHEMA);
|
|
67
85
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
function isNumericParam(entry: ParamSchemaEntry): boolean {
|
|
87
|
+
return (
|
|
88
|
+
entry.type === 'f32' ||
|
|
89
|
+
entry.type === 'i32' ||
|
|
90
|
+
entry.type === 'u32' ||
|
|
91
|
+
entry.type === 'vec2' ||
|
|
92
|
+
entry.type === 'vec3' ||
|
|
93
|
+
entry.type === 'vec4' ||
|
|
94
|
+
entry.type === 'color'
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function standardPbrMaterialRowFields(): readonly string[] {
|
|
99
|
+
const coordinates = new Map(
|
|
100
|
+
STANDARD_PIPELINE_DERIVED.coordinateRecords.map((record) => [record.parameter, record]),
|
|
101
|
+
);
|
|
102
|
+
return STANDARD_PIPELINE_PARAM_SCHEMA.flatMap((entry) => {
|
|
103
|
+
if (isNumericParam(entry)) return [entry.name];
|
|
104
|
+
if (entry.type.startsWith('texture')) {
|
|
105
|
+
const record = coordinates.get(entry.name);
|
|
106
|
+
if (record === undefined) throw new Error(`missing coordinate record for ${entry.name}`);
|
|
107
|
+
return [record.transformMember, record.metadataMember];
|
|
108
|
+
}
|
|
109
|
+
return [];
|
|
110
|
+
});
|
|
111
|
+
}
|
|
90
112
|
|
|
91
113
|
const STANDARD_PBR_VERTEX_INPUTS: readonly MaterialShaderVertexInput[] = [
|
|
92
114
|
{ semantic: 'position', location: 0, format: 'float32x3' },
|
|
@@ -96,22 +118,18 @@ const STANDARD_PBR_VERTEX_INPUTS: readonly MaterialShaderVertexInput[] = [
|
|
|
96
118
|
];
|
|
97
119
|
|
|
98
120
|
function standardPbrResourceSlots(): MaterialShaderResourceSlot[] {
|
|
99
|
-
return
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
group: 1,
|
|
112
|
-
binding: index * 2 + 2,
|
|
113
|
-
},
|
|
114
|
-
]);
|
|
121
|
+
return STANDARD_PIPELINE_DERIVED.resourceBindings.flatMap((resource) => {
|
|
122
|
+
if (resource.kind !== 'sampler' && resource.kind !== 'texture') return [];
|
|
123
|
+
return [
|
|
124
|
+
{
|
|
125
|
+
name: resource.name,
|
|
126
|
+
parameter: resource.parameter ?? resource.name,
|
|
127
|
+
kind: resource.kind,
|
|
128
|
+
group: 1,
|
|
129
|
+
binding: resource.binding,
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
});
|
|
115
133
|
}
|
|
116
134
|
|
|
117
135
|
/**
|
|
@@ -119,38 +137,65 @@ function standardPbrResourceSlots(): MaterialShaderResourceSlot[] {
|
|
|
119
137
|
* The skinned module uses the same material producer and only adds its palette
|
|
120
138
|
* address; it must retain the same receipt identity for material compatibility.
|
|
121
139
|
*/
|
|
122
|
-
export function createStandardPbrArtifactReceipt(
|
|
140
|
+
export function createStandardPbrArtifactReceipt(
|
|
141
|
+
skinned = false,
|
|
142
|
+
vertexColorAvailable = false,
|
|
143
|
+
): MaterialShaderArtifactReceipt {
|
|
123
144
|
const resourceSlots = standardPbrResourceSlots();
|
|
124
|
-
const uvSets =
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
145
|
+
const uvSets = STANDARD_PIPELINE_DERIVED.coordinateRecords.map((record) => ({
|
|
146
|
+
parameter: record.parameter,
|
|
147
|
+
set: 0,
|
|
148
|
+
}));
|
|
149
|
+
const vertexInputs = [
|
|
150
|
+
...STANDARD_PBR_VERTEX_INPUTS,
|
|
151
|
+
...(skinned
|
|
152
|
+
? [
|
|
153
|
+
{ semantic: 'skinIndex', location: 4, format: 'uint16x4' },
|
|
154
|
+
{ semantic: 'skinWeight', location: 5, format: 'float32x4' },
|
|
155
|
+
]
|
|
156
|
+
: []),
|
|
157
|
+
...(vertexColorAvailable ? [{ semantic: 'color', location: 13, format: 'float32x4' }] : []),
|
|
158
|
+
];
|
|
159
|
+
const layoutIdentity = `standard-pbr/material-row-v2${vertexColorAvailable ? '/vertex-color' : ''}`;
|
|
132
160
|
return {
|
|
133
161
|
directEntry: 'vs_main',
|
|
134
162
|
sceneIndexEntry: 'vs_scene_index',
|
|
135
|
-
materialRow: {
|
|
163
|
+
materialRow: {
|
|
164
|
+
byteLength: STANDARD_PIPELINE_DERIVED.totalBytes,
|
|
165
|
+
fields: [...standardPbrMaterialRowFields()],
|
|
166
|
+
},
|
|
136
167
|
resourceSlots,
|
|
137
168
|
uvSets,
|
|
138
169
|
vertexInputs,
|
|
139
170
|
alphaMask: { cutoff: 'alphaCutoff', source: 'baseColor.a' },
|
|
140
171
|
...(skinned ? { skinPaletteAddress: { group: 2, binding: 1, stride: 64 } } : {}),
|
|
141
172
|
reflection: {
|
|
142
|
-
layoutIdentity
|
|
173
|
+
layoutIdentity,
|
|
143
174
|
resourceSlots,
|
|
144
175
|
vertexInputs,
|
|
145
176
|
},
|
|
146
|
-
receiptIdentity:
|
|
147
|
-
generation:
|
|
177
|
+
receiptIdentity: layoutIdentity,
|
|
178
|
+
generation: 2,
|
|
148
179
|
};
|
|
149
180
|
}
|
|
150
181
|
|
|
151
182
|
export function isMaterialShaderArtifact(value: unknown): value is MaterialShaderArtifact {
|
|
152
183
|
if (value === null || typeof value !== 'object') return false;
|
|
153
184
|
const artifact = value as Partial<MaterialShaderArtifact>;
|
|
185
|
+
const particleInputs = artifact.particleInputs;
|
|
186
|
+
const particleInputsValid =
|
|
187
|
+
particleInputs === undefined ||
|
|
188
|
+
(Array.isArray(particleInputs) &&
|
|
189
|
+
particleInputs.every(
|
|
190
|
+
(input) =>
|
|
191
|
+
input !== null &&
|
|
192
|
+
typeof input === 'object' &&
|
|
193
|
+
typeof input.name === 'string' &&
|
|
194
|
+
typeof input.type === 'string' &&
|
|
195
|
+
typeof input.visibility === 'string' &&
|
|
196
|
+
Number.isInteger(input.lane) &&
|
|
197
|
+
input.lane >= 0,
|
|
198
|
+
));
|
|
154
199
|
return (
|
|
155
200
|
typeof artifact.material === 'string' &&
|
|
156
201
|
typeof artifact.pass === 'string' &&
|
|
@@ -158,6 +203,7 @@ export function isMaterialShaderArtifact(value: unknown): value is MaterialShade
|
|
|
158
203
|
typeof artifact.layoutIdentity === 'string' &&
|
|
159
204
|
Array.isArray(artifact.bindings) &&
|
|
160
205
|
Array.isArray(artifact.deps) &&
|
|
161
|
-
Array.isArray(artifact.vertexInputs)
|
|
206
|
+
Array.isArray(artifact.vertexInputs) &&
|
|
207
|
+
particleInputsValid
|
|
162
208
|
);
|
|
163
209
|
}
|
package/src/material-schemas.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
STANDARD_PHYSICAL_PARAMETER_NAMES,
|
|
5
5
|
} from '@forgeax/engine-types';
|
|
6
6
|
import {
|
|
7
|
+
STANDARD_PIPELINE_PARAM_SCHEMA as CANONICAL_STANDARD_PIPELINE_PARAM_SCHEMA,
|
|
7
8
|
createStandardPbrArtifactReceipt,
|
|
8
9
|
type MaterialShaderArtifactReceipt,
|
|
9
10
|
} from './material/artifact-types.js';
|
|
@@ -13,6 +14,24 @@ export const STANDARD_PBR_ALPHA_CUTOFF_DEFAULT = 0;
|
|
|
13
14
|
/** Shared material contract for the standard PBR and skinned PBR shaders. */
|
|
14
15
|
export const DEFAULT_STANDARD_PBR_PARAM_SCHEMA = STANDARD_MATERIAL_PARAM_SCHEMA;
|
|
15
16
|
|
|
17
|
+
/** Numeric WGSL override ID, stable across Naga symbol mangling. */
|
|
18
|
+
export const STANDARD_TEXTURE_MASK_OVERRIDE = '64000';
|
|
19
|
+
|
|
20
|
+
const standardTextureBits: ReadonlyMap<string, number> = new Map(
|
|
21
|
+
DEFAULT_STANDARD_PBR_PARAM_SCHEMA.filter((entry) => entry.type === 'texture2d').map(
|
|
22
|
+
(entry, index) => [entry.name, 2 ** index],
|
|
23
|
+
),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
/** Presence follows the authored contract, never asynchronous GPU residency. */
|
|
27
|
+
export function standardTextureMask(schema: readonly ParamSchemaEntry[]): number {
|
|
28
|
+
let mask = 0;
|
|
29
|
+
for (const entry of schema) {
|
|
30
|
+
if (entry.type === 'texture2d') mask |= standardTextureBits.get(entry.name) ?? 0;
|
|
31
|
+
}
|
|
32
|
+
return mask;
|
|
33
|
+
}
|
|
34
|
+
|
|
16
35
|
export type { StandardPhysicalTextureField } from '@forgeax/engine-types';
|
|
17
36
|
/**
|
|
18
37
|
* Backward-compatible export name for callers that need the physical
|
|
@@ -64,14 +83,71 @@ export const STANDARD_BASE_PARAM_SCHEMA: readonly ParamSchemaEntry[] =
|
|
|
64
83
|
* physical texture pairs are never included here and are appended only for a
|
|
65
84
|
* root contract that declares them.
|
|
66
85
|
*/
|
|
67
|
-
export
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
86
|
+
// Keep the runtime schema export on the receipt producer's canonical
|
|
87
|
+
// projection. The shader artifact, GPU Scene row, and material bind-group
|
|
88
|
+
// builder must not each filter the Standard root independently.
|
|
89
|
+
export const STANDARD_PIPELINE_PARAM_SCHEMA = CANONICAL_STANDARD_PIPELINE_PARAM_SCHEMA;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Fixed material contract consumed by the VFX mesh adapters.
|
|
93
|
+
*
|
|
94
|
+
* `standard-surface.wgsl` is an authored surface rather than the generated
|
|
95
|
+
* Standard root, so its Material block must be projected explicitly when a
|
|
96
|
+
* particle shader is published. Keep the projection derived from the
|
|
97
|
+
* canonical Standard vocabulary wherever possible; only the surface-owned
|
|
98
|
+
* `specularTint` scalar and texture alias are local to this contract. The
|
|
99
|
+
* order is the WGSL ABI: numeric fields first (384 B), followed by eight
|
|
100
|
+
* sampler/texture pairs (bindings 1..16), then the shared IBL injection at
|
|
101
|
+
* bindings 17..23.
|
|
102
|
+
*/
|
|
103
|
+
const PARTICLE_SURFACE_NUMERIC_FIELDS = [
|
|
104
|
+
'baseColor',
|
|
105
|
+
'metallic',
|
|
106
|
+
'roughness',
|
|
107
|
+
'metallicChannel',
|
|
108
|
+
'roughnessChannel',
|
|
109
|
+
'aoChannel',
|
|
110
|
+
'extraChannel',
|
|
111
|
+
'emissive',
|
|
112
|
+
'emissiveIntensity',
|
|
113
|
+
'occlusionStrength',
|
|
114
|
+
'alphaCutoff',
|
|
115
|
+
'clearcoat',
|
|
116
|
+
'clearcoatRoughness',
|
|
117
|
+
'specularTint',
|
|
118
|
+
'normalScale',
|
|
119
|
+
'transmission',
|
|
120
|
+
'ior',
|
|
121
|
+
'thickness',
|
|
122
|
+
'attenuationColor',
|
|
123
|
+
'attenuationDistance',
|
|
124
|
+
] as const;
|
|
125
|
+
|
|
126
|
+
const PARTICLE_SURFACE_TEXTURE_FIELDS = [
|
|
127
|
+
'baseColorTexture',
|
|
128
|
+
'metallicRoughnessTexture',
|
|
129
|
+
'normalTexture',
|
|
130
|
+
'specularTintTexture',
|
|
131
|
+
'emissiveTexture',
|
|
132
|
+
'occlusionTexture',
|
|
133
|
+
'transmissionTexture',
|
|
134
|
+
'thicknessTexture',
|
|
135
|
+
] as const;
|
|
136
|
+
|
|
137
|
+
function particleSurfaceSchemaEntry(name: string): ParamSchemaEntry {
|
|
138
|
+
const entry = DEFAULT_STANDARD_PBR_PARAM_SCHEMA.find((candidate) => candidate.name === name);
|
|
139
|
+
if (entry !== undefined) return entry;
|
|
140
|
+
if (name === 'specularTint') {
|
|
141
|
+
return { name, type: 'vec3', colorSpace: 'srgb', default: [1, 1, 1] };
|
|
142
|
+
}
|
|
143
|
+
if (name === 'specularTintTexture') return { name, type: 'texture2d' };
|
|
144
|
+
throw new Error(`Particle Standard Surface schema field is missing: ${name}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const PARTICLE_MESH_SURFACE_PARAM_SCHEMA: readonly ParamSchemaEntry[] = Object.freeze([
|
|
148
|
+
...PARTICLE_SURFACE_NUMERIC_FIELDS.map(particleSurfaceSchemaEntry),
|
|
149
|
+
...PARTICLE_SURFACE_TEXTURE_FIELDS.map(particleSurfaceSchemaEntry),
|
|
150
|
+
]);
|
|
75
151
|
|
|
76
152
|
/** The single producer receipt shared by direct and scene-index Standard PBR. */
|
|
77
153
|
export const STANDARD_PBR_ARTIFACT_RECEIPT: MaterialShaderArtifactReceipt =
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#define_import_path forgeax_view::output_encoding
|
|
2
|
+
|
|
3
|
+
// The single explicit output encoding owner for raw surface writes.
|
|
4
|
+
#import forgeax_view::common::{linearToSrgbOetf}
|
|
5
|
+
|
|
6
|
+
// Convert linear-LDR RGB once and preserve the source alpha channel exactly.
|
|
7
|
+
fn encodeOutput(linearColor : vec3<f32>, alpha : f32) -> vec4<f32> {
|
|
8
|
+
let encoded = linearToSrgbOetf(linearColor);
|
|
9
|
+
return vec4<f32>(encoded, alpha);
|
|
10
|
+
}
|
package/src/pbr-temporal.wgsl
CHANGED
|
@@ -43,10 +43,13 @@ fn resolvePbrTemporalReactive(
|
|
|
43
43
|
fn projectPbrSceneTemporal(
|
|
44
44
|
baseColorAlpha : f32,
|
|
45
45
|
alphaCutoff : f32,
|
|
46
|
+
#ifdef BASE_COLOR_TEXTURE_AVAILABLE
|
|
47
|
+
baseColorTextureEnabled : bool,
|
|
46
48
|
baseColorTexture : texture_2d<f32>,
|
|
47
49
|
baseColorSampler : sampler,
|
|
48
50
|
transform : vec4<f32>,
|
|
49
51
|
metadata : vec4<f32>,
|
|
52
|
+
#endif
|
|
50
53
|
currentClip : vec4<f32>,
|
|
51
54
|
previousClip : vec4<f32>,
|
|
52
55
|
temporalProjection : vec4<f32>,
|
|
@@ -60,6 +63,9 @@ fn projectPbrSceneTemporal(
|
|
|
60
63
|
uv6 : vec2<f32>,
|
|
61
64
|
uv7 : vec2<f32>,
|
|
62
65
|
) -> vec4<f32> {
|
|
66
|
+
var baseSample = vec4<f32>(1.0);
|
|
67
|
+
#ifdef BASE_COLOR_TEXTURE_AVAILABLE
|
|
68
|
+
if (baseColorTextureEnabled) {
|
|
63
69
|
let baseUv = transformedPbrTemporalUv(
|
|
64
70
|
transform,
|
|
65
71
|
metadata,
|
|
@@ -72,12 +78,14 @@ fn projectPbrSceneTemporal(
|
|
|
72
78
|
uv6,
|
|
73
79
|
uv7,
|
|
74
80
|
);
|
|
75
|
-
|
|
81
|
+
baseSample = sampleMaterialTexture(
|
|
76
82
|
baseColorTexture,
|
|
77
83
|
baseColorSampler,
|
|
78
84
|
baseUv,
|
|
79
85
|
metadata.zw,
|
|
80
86
|
);
|
|
87
|
+
}
|
|
88
|
+
#endif
|
|
81
89
|
if (alphaCutoff > 0.0 && baseColorAlpha * baseSample.a <= alphaCutoff) {
|
|
82
90
|
discard;
|
|
83
91
|
}
|
package/src/scene-temporal.wgsl
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// The sole shader interpretation of forgeax::scene-data::temporal-v1.
|
|
4
4
|
// The packed target is rgba16float at render resolution and is sampled only.
|
|
5
|
+
// Coverage uses the main (possibly jittered) raster/depth lattice. Motion UV
|
|
6
|
+
// values themselves exclude jitter; an unjittered consumer offsets its read.
|
|
5
7
|
|
|
6
8
|
const SCENE_DATA_TEMPORAL_V1_CLEAR : vec4<f32> = vec4<f32>(0.0, 0.0, -1.0, 1.0);
|
|
7
9
|
|
package/src/shadow-pcf.wgsl
CHANGED
|
@@ -142,9 +142,9 @@ fn sample_shadow_2d_kernel(
|
|
|
142
142
|
// `shadowAtlas` is @group(0) @binding(5) texture_depth_cube_array.
|
|
143
143
|
// `shadowSampler` is @group(0) @binding(4) sampler_comparison (shared with
|
|
144
144
|
// directional shadows — same sampler type, no dimension distinction).
|
|
145
|
-
// `lightLocal` is the light-to-fragment direction vector in the cubemap's
|
|
146
|
-
// coordinate system
|
|
147
|
-
//
|
|
145
|
+
// `lightLocal` is the raw light-to-fragment direction vector in the cubemap's
|
|
146
|
+
// coordinate system. evalPointShadowed and buildPointShadowMatrices share the
|
|
147
|
+
// CubeCamera face directions and clip-space-X reflection convention.
|
|
148
148
|
// `layer` is the cube_array layer index (i32; 0..3 for 4 shadow-casting
|
|
149
149
|
// point lights; sentinel -1 must be gated upstream — this function assumes
|
|
150
150
|
// a valid layer).
|
|
@@ -170,11 +170,9 @@ fn sample_shadow_cube_hw2x2(
|
|
|
170
170
|
depthBias : f32,
|
|
171
171
|
nDotL : f32,
|
|
172
172
|
) -> f32 {
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
|
|
176
|
-
let bias = max(normalBias * (1.0 - nDotL), depthBias / 1000.0);
|
|
177
|
-
let adjustedDepth = depthRef - bias;
|
|
173
|
+
// The public depthBias is a normalized depth floor, as for the directional
|
|
174
|
+
// receiver. Do not shrink it by 1000: that leaves front-lit faces uncovered.
|
|
175
|
+
let adjustedDepth = shadow_biased_receiver_depth(depthRef, normalBias, depthBias, nDotL);
|
|
178
176
|
|
|
179
177
|
// Single textureSampleCompareLevel call: the comparison sampler resolves a
|
|
180
178
|
// 2x2 PCF neighborhood at the silicon level; clamp-to-edge address mode
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#define_import_path forgeax_shadow::surface
|
|
2
|
+
#import forgeax_view::common::{View, ShadowCasterCascade, view, shadowCasterCascade}
|
|
3
|
+
|
|
4
|
+
// All geometry producers use the shadow owner's selected light view. Point
|
|
5
|
+
// faces supply their face view through View; spots use the caster uniform.
|
|
6
|
+
fn projectShadowPosition(worldPosition: vec4<f32>) -> vec4<f32> {
|
|
7
|
+
if (shadowCasterCascade.isSpot == 1u) {
|
|
8
|
+
return shadowCasterCascade.spotLightViewProj * worldPosition;
|
|
9
|
+
}
|
|
10
|
+
switch shadowCasterCascade.index {
|
|
11
|
+
case 0u: { return view.lightViewProj_A * worldPosition; }
|
|
12
|
+
case 1u: { return view.lightViewProj_B * worldPosition; }
|
|
13
|
+
case 2u: { return view.lightViewProj_C * worldPosition; }
|
|
14
|
+
default: { return view.lightViewProj_D * worldPosition; }
|
|
15
|
+
}
|
|
16
|
+
}
|