@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.
@@ -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
- // vertex-only depth pass for directional shadow map. No fragment stage --
7
- // the GPU writes depth automatically from gl_Position.z (depth32float RT).
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) -> @builtin(position) vec4<f32> {
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 worldPos = meshes[0].worldFromLocal * instances[idx].localFromInstance * vec4<f32>(in.position, 1.0);
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
- return shadowCasterCascade.spotLightViewProj * worldPos;
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
- return lvp * worldPos;
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>,