@forgeax/engine-shader 0.1.24 → 0.1.26
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 +97 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +35 -7
- package/dist/index.mjs.map +1 -1
- package/dist/material/artifact-registry.d.ts +1 -0
- package/dist/material/artifact-registry.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/builtin-texture-sampling-contract.test.ts +10 -1
- package/src/__tests__/deferred-lighting-ssao.test.ts +4 -1
- package/src/__tests__/material-builtins.unit.test.ts +6 -6
- package/src/__tests__/standard-pbr-webgl2-interface.unit.test.ts +33 -0
- package/src/__tests__/standard-physical-layer.unit.test.ts +16 -0
- package/src/__tests__/standard-surface-pass-evaluation.integration.test.ts +38 -0
- package/src/__tests__/surface-lighting-provenance.integration.test.ts +59 -0
- package/src/__tests__/surface-pass-templates.integration.test.ts +28 -0
- package/src/default-standard-pbr-skin.wgsl +40 -14
- package/src/default-standard-pbr.wgsl +14 -14
- package/src/default_standard_surface.wgsl +5 -5
- package/src/index.ts +36 -4
- package/src/material/artifact-registry.ts +20 -1
- package/src/material/standard-physical-layer.wgsl +12 -0
- package/src/opaque_surface.wgsl +9 -0
- package/src/shadow_caster.wgsl +80 -6
- package/src/unlit.wgsl +12 -0
package/src/shadow_caster.wgsl
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
#pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
2
2
|
#pragma variant_axis SKINNING_DISABLED
|
|
3
|
+
#pragma material_slot surface
|
|
4
|
+
#define_import_path forgeax::default-shadow-caster
|
|
5
|
+
#import forgeax_material::slot::surface::{evaluate_surface}
|
|
6
|
+
#import forgeax_material::surface_v1::{SurfaceInput, SurfaceData}
|
|
7
|
+
#import forgeax_material::parameters::{material}
|
|
3
8
|
|
|
4
9
|
// @forgeax/engine-shader shadow_caster.wgsl
|
|
5
10
|
// feat-20260520-directional-light-shadow-mapping M1c / w9 (D-9 / AC-09):
|
|
6
|
-
//
|
|
7
|
-
//
|
|
11
|
+
// depth pass for directional shadow map. The fragment stage evaluates the
|
|
12
|
+
// selected Standard Surface so opacity/alpha-clip stays identical to Forward
|
|
13
|
+
// and Deferred (depth32float is still the only render target).
|
|
8
14
|
//
|
|
9
15
|
// feat-20260613-csm-cascaded-shadow-maps M5 / w28: per-cascade
|
|
10
16
|
// lightViewProj selection. Each cascade pass writes a different
|
|
@@ -29,12 +35,25 @@
|
|
|
29
35
|
|
|
30
36
|
struct VsInput {
|
|
31
37
|
@location(0) position : vec3<f32>,
|
|
38
|
+
@location(1) normal : vec3<f32>,
|
|
39
|
+
@location(2) uv : vec2<f32>,
|
|
40
|
+
@location(3) tangent : vec4<f32>,
|
|
32
41
|
#if SKINNING_DISABLED == false
|
|
33
42
|
@location(4) skinIndex : vec4<u32>,
|
|
34
43
|
@location(5) skinWeight : vec4<f32>,
|
|
35
44
|
#endif
|
|
36
45
|
};
|
|
37
46
|
|
|
47
|
+
struct VsOut {
|
|
48
|
+
@builtin(position) clip : vec4<f32>,
|
|
49
|
+
@location(0) positionOS : vec3<f32>,
|
|
50
|
+
@location(1) positionWS : vec3<f32>,
|
|
51
|
+
@location(2) normalWS : vec3<f32>,
|
|
52
|
+
@location(3) tangentWS : vec4<f32>,
|
|
53
|
+
@location(4) surfaceUv : vec2<f32>,
|
|
54
|
+
@location(5) vertexColor : vec4<f32>,
|
|
55
|
+
};
|
|
56
|
+
|
|
38
57
|
#if SKINNING_DISABLED == false
|
|
39
58
|
#if STORAGE_BUFFER_AVAILABLE == true
|
|
40
59
|
@group(2) @binding(1) var<storage, read> palette : array<mat4x4<f32>>;
|
|
@@ -53,7 +72,7 @@ fn _cascadeLightViewProj(layer : u32) -> mat4x4<f32> {
|
|
|
53
72
|
}
|
|
54
73
|
|
|
55
74
|
@vertex
|
|
56
|
-
fn vs_main(in : VsInput, @builtin(instance_index) idx : u32) ->
|
|
75
|
+
fn vs_main(in : VsInput, @builtin(instance_index) idx : u32) -> VsOut {
|
|
57
76
|
#if SKINNING_DISABLED == false
|
|
58
77
|
let skinMatrix = palette[in.skinIndex.x] * in.skinWeight.x +
|
|
59
78
|
palette[in.skinIndex.y] * in.skinWeight.y +
|
|
@@ -63,8 +82,14 @@ fn vs_main(in : VsInput, @builtin(instance_index) idx : u32) -> @builtin(positio
|
|
|
63
82
|
// produce world-space positions. Applying meshes[0].worldFromLocal again
|
|
64
83
|
// would double-transform a parented skinned entity.
|
|
65
84
|
let worldPos = skinMatrix * vec4<f32>(in.position, 1.0);
|
|
85
|
+
let worldNormal = normalize((skinMatrix * vec4<f32>(in.normal, 0.0)).xyz);
|
|
86
|
+
let worldTangent = normalize((skinMatrix * vec4<f32>(in.tangent.xyz, 0.0)).xyz);
|
|
66
87
|
#else
|
|
67
|
-
let
|
|
88
|
+
let instanceLocal = instances[idx].localFromInstance;
|
|
89
|
+
let worldMatrix = meshes[0].worldFromLocal * instanceLocal;
|
|
90
|
+
let worldPos = worldMatrix * vec4<f32>(in.position, 1.0);
|
|
91
|
+
let worldNormal = normalize((worldMatrix * vec4<f32>(in.normal, 0.0)).xyz);
|
|
92
|
+
let worldTangent = normalize((worldMatrix * vec4<f32>(in.tangent.xyz, 0.0)).xyz);
|
|
68
93
|
#endif
|
|
69
94
|
// feat-20260625-spot-light-shadow-mapping M2 / w10 (D-1): spot shadow passes
|
|
70
95
|
// set `isSpot = 1u` and write their perspective matrix into
|
|
@@ -72,8 +97,57 @@ fn vs_main(in : VsInput, @builtin(instance_index) idx : u32) -> @builtin(positio
|
|
|
72
97
|
// `view.lightViewProj_A..D` via `index`. Routing on the discriminant keeps
|
|
73
98
|
// the spot matrix out of the directional View UBO (no same-frame contention).
|
|
74
99
|
if (shadowCasterCascade.isSpot == 1u) {
|
|
75
|
-
|
|
100
|
+
var out : VsOut;
|
|
101
|
+
out.clip = shadowCasterCascade.spotLightViewProj * worldPos;
|
|
102
|
+
out.positionOS = in.position;
|
|
103
|
+
out.positionWS = worldPos.xyz;
|
|
104
|
+
out.normalWS = worldNormal;
|
|
105
|
+
out.tangentWS = vec4<f32>(worldTangent, in.tangent.w);
|
|
106
|
+
out.surfaceUv = in.uv;
|
|
107
|
+
out.vertexColor = vec4<f32>(1.0);
|
|
108
|
+
return out;
|
|
76
109
|
}
|
|
77
110
|
let lvp = _cascadeLightViewProj(shadowCasterCascade.index);
|
|
78
|
-
|
|
111
|
+
var out : VsOut;
|
|
112
|
+
out.clip = lvp * worldPos;
|
|
113
|
+
out.positionOS = in.position;
|
|
114
|
+
out.positionWS = worldPos.xyz;
|
|
115
|
+
out.normalWS = worldNormal;
|
|
116
|
+
out.tangentWS = vec4<f32>(worldTangent, in.tangent.w);
|
|
117
|
+
out.surfaceUv = in.uv;
|
|
118
|
+
out.vertexColor = vec4<f32>(1.0);
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
fn evaluateShadowSurface(in : VsOut, frontFacing : bool) -> SurfaceData {
|
|
123
|
+
let viewDirectionWS = normalize(view.cameraPos - in.positionWS);
|
|
124
|
+
return evaluate_surface(SurfaceInput(
|
|
125
|
+
in.positionOS,
|
|
126
|
+
in.positionWS,
|
|
127
|
+
in.normalWS,
|
|
128
|
+
in.tangentWS,
|
|
129
|
+
viewDirectionWS,
|
|
130
|
+
in.surfaceUv,
|
|
131
|
+
in.surfaceUv,
|
|
132
|
+
in.vertexColor,
|
|
133
|
+
frontFacing,
|
|
134
|
+
));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
fn alphaTestShadowSurface(surface : SurfaceData) {
|
|
138
|
+
if (surface.alphaClipThreshold > 0.0 && surface.opacity <= surface.alphaClipThreshold) {
|
|
139
|
+
discard;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// The depth-only path evaluates the selected Surface so alpha-clip ownership
|
|
144
|
+
// remains shared with Forward and Deferred without mutating vertex data.
|
|
145
|
+
@fragment
|
|
146
|
+
fn fs_shadow(in : VsOut, @builtin(front_facing) frontFacing : bool) {
|
|
147
|
+
alphaTestShadowSurface(evaluateShadowSurface(in, frontFacing));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@fragment
|
|
151
|
+
fn fs_main(in : VsOut, @builtin(front_facing) frontFacing : bool) {
|
|
152
|
+
alphaTestShadowSurface(evaluateShadowSurface(in, frontFacing));
|
|
79
153
|
}
|
package/src/unlit.wgsl
CHANGED
|
@@ -111,6 +111,18 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
111
111
|
return vec4<f32>(material.baseColor.rgb * texSample.rgb * vertexColor.rgb, alpha);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
// Depth-only shadow variant. Keep alpha clipping identical to the color path,
|
|
115
|
+
// but return no color target because the shadow pass has a depth attachment only.
|
|
116
|
+
@fragment
|
|
117
|
+
fn fs_shadow(in : VsOut) {
|
|
118
|
+
let texSample = sampleMaterialTextureLinear(baseColorTexture, baseColorSampler, in.uv, material.baseColorTextureCoordinatesMetadata.zw);
|
|
119
|
+
let vertexColor = materialVertexColor(in);
|
|
120
|
+
let alpha = material.baseColor.a * texSample.a * vertexColor.a;
|
|
121
|
+
if (material.alphaCutoff > 0.0 && alpha < material.alphaCutoff) {
|
|
122
|
+
discard;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
114
126
|
struct TemporalVsOut {
|
|
115
127
|
@builtin(position) clip : vec4<f32>,
|
|
116
128
|
@location(0) uv : vec2<f32>,
|