@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
|
@@ -3,50 +3,49 @@
|
|
|
3
3
|
//
|
|
4
4
|
// The cluster module owns membership and raw payload decoding only. Punctual
|
|
5
5
|
// BRDF, range, cone, PCF, and fail-open behavior live in lighting-punctual.
|
|
6
|
+
// ProbeBlendRecord is intentionally absent: probes are not DirectLightSlot
|
|
7
|
+
// members and this cluster owner never performs an all-probe loop.
|
|
6
8
|
|
|
7
9
|
#define_import_path forgeax_standard::cluster
|
|
8
10
|
|
|
9
11
|
#import forgeax_pbr::lighting_punctual::{evalPoint, evalPointFlat, evalSpot, evalSpotFlat, evalSpotShadowed}
|
|
10
|
-
#import forgeax_pbr::
|
|
12
|
+
#import forgeax_pbr::lighting_spot_modifiers::{spotModifierFactors}
|
|
13
|
+
#import forgeax_pbr::lighting_rect_area::{evalRectAreaLtcGgx}
|
|
11
14
|
#import forgeax_view::common::{view}
|
|
12
15
|
#ifdef PROJECTOR_AVAILABLE
|
|
13
|
-
#import
|
|
16
|
+
#import forgeax_pbr::lighting_spot_projector::{sampleStandardSpotProjector}
|
|
14
17
|
#endif
|
|
15
18
|
#ifdef POINT_SHADOW_AVAILABLE
|
|
16
19
|
#import forgeax_pbr::lighting_punctual::{evalPointShadowed}
|
|
20
|
+
#import forgeax_view::common::{shadowParams}
|
|
17
21
|
#endif
|
|
18
22
|
|
|
19
23
|
#ifdef CLUSTER_FORWARD_AVAILABLE
|
|
20
24
|
|
|
21
25
|
const KIND_POINT: u32 = 0u;
|
|
22
26
|
const KIND_SPOT: u32 = 1u;
|
|
23
|
-
|
|
24
|
-
// marker. The remaining bits are the shadow-atlas tile (0..3), with zero as
|
|
25
|
-
// the explicit View-UBO lane for projector-only spots.
|
|
27
|
+
const KIND_RECT_AREA: u32 = 2u;
|
|
26
28
|
const PROJECTOR_FLAG: i32 = 0x40000000;
|
|
27
29
|
const TILE_MASK: i32 = 0x3fffffff;
|
|
28
30
|
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
color : vec4<f32>,
|
|
38
|
-
direction : vec4<f32>,
|
|
39
|
-
kind_and_shadow : vec4<u32>,
|
|
31
|
+
// DirectLightSlot is the byte-frozen 80B std430/std140 transport contract.
|
|
32
|
+
// The named rows match the host packer; metadata carries kind and identities.
|
|
33
|
+
struct DirectLightSlot {
|
|
34
|
+
position : vec4<f32>,
|
|
35
|
+
colorTimesIntensity : vec4<f32>,
|
|
36
|
+
direction : vec4<f32>,
|
|
37
|
+
auxiliary : vec4<f32>,
|
|
38
|
+
metadata : vec4<u32>,
|
|
40
39
|
};
|
|
41
40
|
|
|
42
|
-
const
|
|
41
|
+
const DIRECT_LIGHT_SLOT_BYTE_SIZE: u32 = 80u;
|
|
43
42
|
|
|
44
43
|
struct ClusterUniform {
|
|
45
44
|
grid : vec4<u32>,
|
|
46
45
|
near_far_log : vec4<f32>,
|
|
47
46
|
};
|
|
48
47
|
|
|
49
|
-
@group(2) @binding(3) var<storage, read> light_data : array<
|
|
48
|
+
@group(2) @binding(3) var<storage, read> light_data : array<DirectLightSlot, 256>;
|
|
50
49
|
@group(2) @binding(4) var<storage, read> cluster_grid : array<u32>;
|
|
51
50
|
@group(2) @binding(5) var<storage, read> light_index_list : array<u32>;
|
|
52
51
|
@group(2) @binding(6) var<uniform> cluster_uniform : ClusterUniform;
|
|
@@ -89,30 +88,10 @@ fn ndc_position_to_cluster(
|
|
|
89
88
|
return vec3(cx, cy, cz);
|
|
90
89
|
}
|
|
91
90
|
|
|
92
|
-
// Surface SpotLight projector sampling is owned by the same clustered light
|
|
93
|
-
// evaluator as the analytic cone/range contribution. The host marks the
|
|
94
|
-
// selected projector in the LightSlot flag and writes its matrix to the
|
|
95
|
-
// corresponding View lane (lane 0 for projector-only); no second
|
|
96
|
-
// material-local sampling loop or resource owner is allowed.
|
|
97
|
-
fn sampleStandardSpotProjector(
|
|
98
|
-
lightViewProj : mat4x4<f32>,
|
|
99
|
-
world_pos : vec3<f32>,
|
|
100
|
-
) -> vec3<f32> {
|
|
101
|
-
#ifdef PROJECTOR_AVAILABLE
|
|
102
|
-
let uv = projectSpotUv(lightViewProj, world_pos);
|
|
103
|
-
if (!(uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0)) {
|
|
104
|
-
return vec3<f32>(1.0);
|
|
105
|
-
}
|
|
106
|
-
return textureSampleLevel(projectorTexture, projectorSampler, uv, 0.0).rgb;
|
|
107
|
-
#else
|
|
108
|
-
return vec3<f32>(1.0);
|
|
109
|
-
#endif
|
|
110
|
-
}
|
|
111
|
-
|
|
112
91
|
// Decode one kind first, then delegate the complete evaluation to the shared
|
|
113
92
|
// punctual owner. Unknown kinds are explicitly zero contribution.
|
|
114
93
|
fn evaluate_cluster_light(
|
|
115
|
-
light :
|
|
94
|
+
light : DirectLightSlot,
|
|
116
95
|
world_pos : vec3<f32>,
|
|
117
96
|
normal : vec3<f32>,
|
|
118
97
|
view_dir : vec3<f32>,
|
|
@@ -122,78 +101,101 @@ fn evaluate_cluster_light(
|
|
|
122
101
|
f0 : vec3<f32>,
|
|
123
102
|
flat_2d : bool,
|
|
124
103
|
) -> vec3<f32> {
|
|
125
|
-
let kind = light.
|
|
104
|
+
let kind = light.metadata.x;
|
|
126
105
|
if (kind == KIND_POINT) {
|
|
127
106
|
if (flat_2d) {
|
|
128
107
|
return evalPointFlat(
|
|
129
|
-
light.position.xyz, light.
|
|
108
|
+
light.position.xyz, light.colorTimesIntensity.xyz, light.position.w,
|
|
130
109
|
world_pos, base_color,
|
|
131
110
|
);
|
|
132
111
|
}
|
|
133
112
|
#ifdef POINT_SHADOW_AVAILABLE
|
|
134
|
-
let layer = bitcast<i32>(light.
|
|
113
|
+
let layer = bitcast<i32>(light.metadata.y);
|
|
135
114
|
if (layer >= 0) {
|
|
115
|
+
let shadow = shadowParams[layer];
|
|
136
116
|
return evalPointShadowed(
|
|
137
|
-
light.position.xyz, light.
|
|
117
|
+
light.position.xyz, light.colorTimesIntensity.xyz, light.position.w,
|
|
138
118
|
world_pos, normal, view_dir, base_color, metallic, alpha_sq, f0,
|
|
139
119
|
layer,
|
|
140
|
-
|
|
141
|
-
|
|
120
|
+
shadow.x,
|
|
121
|
+
shadow.y,
|
|
142
122
|
0.005, 0.05,
|
|
143
123
|
);
|
|
144
124
|
}
|
|
145
125
|
#endif
|
|
146
126
|
return evalPoint(
|
|
147
|
-
light.position.xyz, light.
|
|
127
|
+
light.position.xyz, light.colorTimesIntensity.xyz, light.position.w,
|
|
148
128
|
world_pos, normal, view_dir, base_color, metallic, alpha_sq, f0,
|
|
149
129
|
);
|
|
150
130
|
}
|
|
151
131
|
if (kind == KIND_SPOT) {
|
|
152
132
|
if (flat_2d) {
|
|
153
133
|
return evalSpotFlat(
|
|
154
|
-
light.position.xyz, light.direction.xyz, light.
|
|
155
|
-
light.
|
|
134
|
+
light.position.xyz, light.direction.xyz, light.colorTimesIntensity.xyz,
|
|
135
|
+
light.colorTimesIntensity.w, light.direction.w, light.position.w,
|
|
156
136
|
world_pos, base_color,
|
|
157
137
|
);
|
|
158
138
|
}
|
|
159
|
-
let
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
139
|
+
let modifierFactors = spotModifierFactors(
|
|
140
|
+
light.position.xyz,
|
|
141
|
+
light.direction.xyz,
|
|
142
|
+
light.direction.w,
|
|
143
|
+
world_pos,
|
|
144
|
+
light.auxiliary.w,
|
|
145
|
+
light.metadata,
|
|
146
|
+
);
|
|
147
|
+
let modifier = vec3<f32>(modifierFactors.x) * modifierFactors.yzw;
|
|
148
|
+
let encoded_tile = bitcast<i32>(light.metadata.y);
|
|
149
|
+
// The optional projector marker shares the non-negative shadow identity
|
|
150
|
+
// lane. The direct-light packer keeps the lower tile bits intact.
|
|
151
|
+
var projector_selected = false;
|
|
152
|
+
var tile : i32 = encoded_tile;
|
|
163
153
|
if (encoded_tile >= 0 && (encoded_tile & PROJECTOR_FLAG) != 0) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
let projector = sampleStandardSpotProjector(view.spotLightViewProj[tile], world_pos);
|
|
167
|
-
return evalSpotShadowed(
|
|
168
|
-
light.position.xyz, light.direction.xyz, light.color.xyz,
|
|
169
|
-
// LightSlot packs cosInner in color.w and cosOuter in direction.w;
|
|
170
|
-
// keep the evaluator's named cone order intact at the decode boundary.
|
|
171
|
-
light.color.w, light.direction.w, light.position.w,
|
|
172
|
-
world_pos, normal, view_dir, base_color, metallic, alpha_sq, f0,
|
|
173
|
-
view.spotLightViewProj[tile], tile, 0.005, 0.05,
|
|
174
|
-
bitcast<f32>(light.kind_and_shadow.w),
|
|
175
|
-
bitcast<f32>(light.kind_and_shadow.z),
|
|
176
|
-
) * projector;
|
|
177
|
-
}
|
|
178
|
-
return vec3<f32>(0.0);
|
|
154
|
+
projector_selected = true;
|
|
155
|
+
tile = encoded_tile & TILE_MASK;
|
|
179
156
|
}
|
|
180
|
-
if (
|
|
181
|
-
let tile = encoded_tile;
|
|
157
|
+
if (tile >= 0 && tile < 4) {
|
|
182
158
|
return evalSpotShadowed(
|
|
183
|
-
light.position.xyz, light.direction.xyz, light.
|
|
184
|
-
//
|
|
159
|
+
light.position.xyz, light.direction.xyz, light.colorTimesIntensity.xyz,
|
|
160
|
+
// DirectLightSlot packs cosInner in color.w and cosOuter in direction.w;
|
|
185
161
|
// keep the evaluator's named cone order intact at the decode boundary.
|
|
186
|
-
light.
|
|
187
|
-
|
|
162
|
+
light.colorTimesIntensity.w, light.direction.w, light.position.w,
|
|
163
|
+
world_pos, normal, view_dir, base_color, metallic, alpha_sq, f0,
|
|
188
164
|
view.spotLightViewProj[tile], tile, 0.005, 0.05,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
)
|
|
165
|
+
3.0,
|
|
166
|
+
1.0,
|
|
167
|
+
) * modifier
|
|
168
|
+
#ifdef PROJECTOR_AVAILABLE
|
|
169
|
+
* select(
|
|
170
|
+
vec3<f32>(1.0),
|
|
171
|
+
sampleStandardSpotProjector(view.spotLightViewProj[tile], world_pos, light.metadata),
|
|
172
|
+
projector_selected,
|
|
173
|
+
)
|
|
174
|
+
#endif
|
|
175
|
+
;
|
|
192
176
|
}
|
|
193
177
|
return evalSpot(
|
|
194
|
-
light.position.xyz, light.direction.xyz, light.
|
|
195
|
-
light.
|
|
178
|
+
light.position.xyz, light.direction.xyz, light.colorTimesIntensity.xyz,
|
|
179
|
+
light.colorTimesIntensity.w, light.direction.w, light.position.w,
|
|
196
180
|
world_pos, normal, view_dir, base_color, metallic, alpha_sq, f0,
|
|
181
|
+
) * modifier;
|
|
182
|
+
}
|
|
183
|
+
if (kind == KIND_RECT_AREA) {
|
|
184
|
+
return evalRectAreaLtcGgx(
|
|
185
|
+
light.position.xyz,
|
|
186
|
+
light.colorTimesIntensity.xyz,
|
|
187
|
+
light.auxiliary.xyz,
|
|
188
|
+
light.direction.xyz,
|
|
189
|
+
light.colorTimesIntensity.w,
|
|
190
|
+
light.direction.w,
|
|
191
|
+
light.position.w,
|
|
192
|
+
world_pos,
|
|
193
|
+
normal,
|
|
194
|
+
view_dir,
|
|
195
|
+
base_color,
|
|
196
|
+
metallic,
|
|
197
|
+
alpha_sq,
|
|
198
|
+
f0,
|
|
197
199
|
);
|
|
198
200
|
}
|
|
199
201
|
return vec3<f32>(0.0);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#define_import_path forgeax_material::surface_v1
|
|
2
|
+
|
|
3
|
+
// Engine-owned input ABI for authored Standard material surfaces.
|
|
4
|
+
struct SurfaceInput {
|
|
5
|
+
positionOS : vec3<f32>,
|
|
6
|
+
positionWS : vec3<f32>,
|
|
7
|
+
geometricNormalWS : vec3<f32>,
|
|
8
|
+
tangentWS : vec4<f32>,
|
|
9
|
+
viewDirectionWS : vec3<f32>,
|
|
10
|
+
uv0 : vec2<f32>,
|
|
11
|
+
uv1 : vec2<f32>,
|
|
12
|
+
vertexColor : vec4<f32>,
|
|
13
|
+
frontFacing : bool,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Surface output is consumed by the Standard BRDF and pass family.
|
|
17
|
+
struct SurfaceData {
|
|
18
|
+
baseColor : vec3<f32>,
|
|
19
|
+
normalWS : vec3<f32>,
|
|
20
|
+
metallic : f32,
|
|
21
|
+
roughness : f32,
|
|
22
|
+
emissive : vec3<f32>,
|
|
23
|
+
occlusion : f32,
|
|
24
|
+
opacity : f32,
|
|
25
|
+
alphaClipThreshold : f32,
|
|
26
|
+
};
|
package/src/types.ts
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* no longer carry a bindingLayout sidecar (§Change stance: no v1/v2
|
|
18
18
|
* dual-path, no incremental drop window).
|
|
19
19
|
*/
|
|
20
|
+
import type { MaterialShaderArtifactReceipt } from './material/artifact-types.js';
|
|
20
21
|
export interface MaterialShaderManifestVariant {
|
|
21
22
|
readonly definesKey: string;
|
|
22
23
|
readonly defines: Record<string, boolean>;
|
|
@@ -33,7 +34,7 @@ export interface MaterialShaderManifestVariant {
|
|
|
33
34
|
*
|
|
34
35
|
* For Engine.create() internal use only; AI users never see this type.
|
|
35
36
|
*/
|
|
36
|
-
export interface MaterialShaderManifestEntry {
|
|
37
|
+
export interface MaterialShaderManifestEntry extends Partial<MaterialShaderArtifactReceipt> {
|
|
37
38
|
readonly identifier: string;
|
|
38
39
|
readonly sourcePath: string;
|
|
39
40
|
readonly composedWgsl: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#define_import_path forgeax_view::volume_inject
|
|
2
2
|
#import forgeax_pbr::shadow_pcf::{sample_shadow_2d_kernel}
|
|
3
3
|
#import forgeax_pbr::lighting_attenuation::{projectSpotUv}
|
|
4
|
+
#import forgeax_view::common::{DirectLightSlot}
|
|
4
5
|
// forgeax_pbr::lighting_punctual exports the surface-equivalent
|
|
5
6
|
// evalVolumePoint and evalVolumeSpot contract; this stage consumes their
|
|
6
7
|
// shared projector and shadow visibility facts without a second light owner.
|
|
@@ -29,41 +30,18 @@ struct VolumeParams {
|
|
|
29
30
|
optics : vec4<f32>,
|
|
30
31
|
};
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
invRangeSquared : f32,
|
|
35
|
-
colorTimesIntensity : vec3<f32>,
|
|
36
|
-
cosInner : f32,
|
|
37
|
-
direction : vec3<f32>,
|
|
38
|
-
cosOuter : f32,
|
|
39
|
-
depthBias : f32,
|
|
40
|
-
normalBias : f32,
|
|
41
|
-
pcfKernelSize : f32,
|
|
42
|
-
shadowAtlasTile : i32,
|
|
43
|
-
shadowIntensity : f32,
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
struct SpotLightsArray {
|
|
47
|
-
count : u32,
|
|
48
|
-
slots : array<SpotLight, 4>,
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
struct PointLight {
|
|
52
|
-
position : vec3<f32>,
|
|
53
|
-
invRangeSquared : f32,
|
|
54
|
-
colorTimesIntensity : vec3<f32>,
|
|
55
|
-
shadowAtlasLayer : i32,
|
|
56
|
-
};
|
|
33
|
+
const DIRECT_LIGHT_METADATA_SENTINEL : u32 = 0xffffffffu;
|
|
34
|
+
const DIRECT_LIGHT_TILE_MASK : u32 = 0x3fffffffu;
|
|
57
35
|
|
|
58
|
-
struct
|
|
59
|
-
|
|
60
|
-
|
|
36
|
+
struct ClusterUniform {
|
|
37
|
+
grid : vec4<u32>,
|
|
38
|
+
near_far_log : vec4<f32>,
|
|
61
39
|
};
|
|
62
40
|
|
|
63
41
|
@group(0) @binding(5) var<uniform> volume_params : VolumeParams;
|
|
64
42
|
@group(0) @binding(6) var volume_froxel : texture_storage_2d_array<rgba8unorm, write>;
|
|
65
|
-
@group(0) @binding(7) var<storage, read>
|
|
66
|
-
@group(0) @binding(8) var<
|
|
43
|
+
@group(0) @binding(7) var<storage, read> light_data : array<DirectLightSlot, 256>;
|
|
44
|
+
@group(0) @binding(8) var<uniform> cluster_uniform : ClusterUniform;
|
|
67
45
|
@group(0) @binding(0) var<uniform> view : View;
|
|
68
46
|
|
|
69
47
|
fn reconstruct_world(uv : vec2<f32>, depth : f32) -> vec3<f32> {
|
|
@@ -161,12 +139,32 @@ fn volume_cascade(world_position : vec3<f32>, view_depth : f32) -> f32 {
|
|
|
161
139
|
);
|
|
162
140
|
}
|
|
163
141
|
|
|
142
|
+
fn shadowAtlasTile(light : DirectLightSlot) -> i32 {
|
|
143
|
+
let encoded = light.metadata.y;
|
|
144
|
+
if (encoded == DIRECT_LIGHT_METADATA_SENTINEL) { return -1; }
|
|
145
|
+
// Projector-selected spots carry the marker in bit 30 and retain the
|
|
146
|
+
// selected shadow tile in the lower 30 bits. A projector-only spot uses
|
|
147
|
+
// tile zero, matching the surface projector contract.
|
|
148
|
+
let tile = encoded & DIRECT_LIGHT_TILE_MASK;
|
|
149
|
+
if (tile >= 4u) { return -1; }
|
|
150
|
+
return i32(tile);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
fn selected_cluster_light_slot(value : f32) -> i32 {
|
|
154
|
+
let slot = i32(round(value));
|
|
155
|
+
if (slot < 0) { return -1; }
|
|
156
|
+
let index = u32(slot);
|
|
157
|
+
if (index >= cluster_uniform.grid.w || index >= 256u) { return -1; }
|
|
158
|
+
return slot;
|
|
159
|
+
}
|
|
160
|
+
|
|
164
161
|
fn spot_shadow_visibility_at(world_position : vec3<f32>) -> f32 {
|
|
165
|
-
let slot =
|
|
166
|
-
if (slot
|
|
167
|
-
let light =
|
|
168
|
-
|
|
169
|
-
|
|
162
|
+
let slot = selected_cluster_light_slot(volume_params.light_color.w);
|
|
163
|
+
if (slot < 0) { return 0.0; }
|
|
164
|
+
let light = light_data[u32(slot)];
|
|
165
|
+
let encodedTile = shadowAtlasTile(light);
|
|
166
|
+
if (encodedTile < 0) { return 1.0; }
|
|
167
|
+
let tile = u32(encodedTile);
|
|
170
168
|
let projectorUv = projectSpotUv(view.spotLightViewProj[tile], world_position);
|
|
171
169
|
// The accepted projector tuple is sampled with textureSampleLevel by the
|
|
172
170
|
// surface and volume owners when a projector texture is present.
|
|
@@ -186,7 +184,7 @@ fn spot_shadow_visibility_at(world_position : vec3<f32>) -> f32 {
|
|
|
186
184
|
let shadow_size = vec2<f32>(textureDimensions(shadowMap));
|
|
187
185
|
return sample_shadow_2d_kernel(
|
|
188
186
|
shadowMap, shadowSampler, atlas_uv, 1.0 / shadow_size, projected.z,
|
|
189
|
-
light.
|
|
187
|
+
light.auxiliary.y, light.auxiliary.x, 1.0, 3.0,
|
|
190
188
|
);
|
|
191
189
|
}
|
|
192
190
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// forgeax_pbr::lighting_punctual remains the surface contract; the isolated
|
|
3
3
|
// attenuation module below provides its resource-free volume equivalents.
|
|
4
4
|
#import forgeax_pbr::lighting_attenuation::{projectSpotUv}
|
|
5
|
+
#import forgeax_view::common::{DirectLightSlot}
|
|
5
6
|
|
|
6
7
|
struct VolumeView {
|
|
7
8
|
_prefix : array<vec4<f32>, 6>,
|
|
@@ -11,20 +12,12 @@ struct VolumeView {
|
|
|
11
12
|
_viewTail : array<vec4<f32>, 18>,
|
|
12
13
|
spotLightViewProj : array<mat4x4<f32>, 4>,
|
|
13
14
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
shadowAtlasTile : i32,
|
|
20
|
-
shadowIntensity : f32,
|
|
15
|
+
// Volume reads the same full light_data payload as Standard surface Cluster;
|
|
16
|
+
// it has no secondary light mirror.
|
|
17
|
+
struct ClusterUniform {
|
|
18
|
+
grid : vec4<u32>,
|
|
19
|
+
near_far_log : vec4<f32>,
|
|
21
20
|
};
|
|
22
|
-
struct SpotLightsArray { count : u32, slots : array<SpotLight, 4>, };
|
|
23
|
-
struct PointLight {
|
|
24
|
-
position : vec3<f32>, invRangeSquared : f32,
|
|
25
|
-
colorTimesIntensity : vec3<f32>, shadowAtlasLayer : i32,
|
|
26
|
-
};
|
|
27
|
-
struct PointLightsArray { count : u32, slots : array<PointLight, 4>, };
|
|
28
21
|
struct VolumeParams {
|
|
29
22
|
bounds_min : vec4<f32>, bounds_max : vec4<f32>, extinction : vec4<f32>, albedo : vec4<f32>,
|
|
30
23
|
emission : vec4<f32>, light_direction : vec4<f32>, light_color : vec4<f32>, optics : vec4<f32>,
|
|
@@ -69,8 +62,8 @@ fn stratified32(seed : u32, frame_index : u32, odd_stride : u32) -> f32 {
|
|
|
69
62
|
@group(0) @binding(7) var density_sampler : sampler;
|
|
70
63
|
@group(0) @binding(8) var history_seed : texture_storage_2d<rgba16float, write>;
|
|
71
64
|
@group(0) @binding(9) var temporal_seed : texture_storage_2d<rgba16float, write>;
|
|
72
|
-
@group(0) @binding(10) var<storage, read>
|
|
73
|
-
@group(0) @binding(11) var<
|
|
65
|
+
@group(0) @binding(10) var<storage, read> light_data : array<DirectLightSlot, 256>;
|
|
66
|
+
@group(0) @binding(11) var<uniform> cluster_uniform : ClusterUniform;
|
|
74
67
|
@group(0) @binding(12) var projectorTexture : texture_2d<f32>;
|
|
75
68
|
@group(0) @binding(13) var projectorSampler : sampler;
|
|
76
69
|
|
|
@@ -107,26 +100,43 @@ fn evalSpotAttenuation(
|
|
|
107
100
|
return distance * smoothstep(cosOuter, cosInner, dot(direction, -normalize(lightDir)));
|
|
108
101
|
}
|
|
109
102
|
|
|
110
|
-
fn
|
|
111
|
-
|
|
112
|
-
|
|
103
|
+
fn shadowAtlasTile(light : DirectLightSlot) -> i32 {
|
|
104
|
+
let encoded = light.metadata.y;
|
|
105
|
+
if (encoded == 0xffffffffu) { return -1; }
|
|
106
|
+
let tile = encoded & 0x3fffffffu;
|
|
107
|
+
if (tile >= 4u) { return -1; }
|
|
108
|
+
return i32(tile);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
fn spotProjectorMatrix(light : DirectLightSlot) -> mat4x4<f32> {
|
|
112
|
+
let tile = shadowAtlasTile(light);
|
|
113
|
+
if (tile >= 0) {
|
|
114
|
+
return volume_view.spotLightViewProj[tile];
|
|
113
115
|
}
|
|
114
116
|
return volume_view.spotLightViewProj[0];
|
|
115
117
|
}
|
|
116
118
|
|
|
119
|
+
fn selected_cluster_light_slot(value : f32) -> i32 {
|
|
120
|
+
let slot = i32(round(value));
|
|
121
|
+
if (slot < 0) { return -1; }
|
|
122
|
+
let index = u32(slot);
|
|
123
|
+
if (index >= cluster_uniform.grid.w || index >= 256u) { return -1; }
|
|
124
|
+
return slot;
|
|
125
|
+
}
|
|
126
|
+
|
|
117
127
|
fn spot_attenuation_at(world_position : vec3<f32>) -> f32 {
|
|
118
|
-
let slot =
|
|
119
|
-
if (slot
|
|
120
|
-
let light =
|
|
128
|
+
let slot = selected_cluster_light_slot(volume_params.light_color.w);
|
|
129
|
+
if (slot < 0) { return 0.0; }
|
|
130
|
+
let light = light_data[u32(slot)];
|
|
121
131
|
let projectorUv = projectSpotUv(spotProjectorMatrix(light), world_position);
|
|
122
132
|
if (any(projectorUv < vec2<f32>(0.0)) || any(projectorUv > vec2<f32>(1.0))) { return 0.0; }
|
|
123
133
|
return evalSpotAttenuation(
|
|
124
|
-
light.position,
|
|
125
|
-
light.direction,
|
|
134
|
+
light.position.xyz,
|
|
135
|
+
light.direction.xyz,
|
|
126
136
|
world_position,
|
|
127
|
-
light.
|
|
128
|
-
light.
|
|
129
|
-
light.
|
|
137
|
+
light.colorTimesIntensity.w,
|
|
138
|
+
light.direction.w,
|
|
139
|
+
light.position.w,
|
|
130
140
|
);
|
|
131
141
|
}
|
|
132
142
|
|
|
@@ -151,21 +161,21 @@ fn evalVolumeSpot(
|
|
|
151
161
|
}
|
|
152
162
|
|
|
153
163
|
fn volume_point_radiance(world_position : vec3<f32>) -> vec3<f32> {
|
|
154
|
-
let pointSlot =
|
|
155
|
-
if (pointSlot
|
|
156
|
-
let point =
|
|
164
|
+
let pointSlot = selected_cluster_light_slot(volume_params.emission.w);
|
|
165
|
+
if (pointSlot < 0) { return vec3<f32>(0.0); }
|
|
166
|
+
let point = light_data[u32(pointSlot)];
|
|
157
167
|
return evalVolumePoint(
|
|
158
|
-
point.position, point.colorTimesIntensity, point.
|
|
168
|
+
point.position.xyz, point.colorTimesIntensity.xyz, point.position.w, world_position,
|
|
159
169
|
);
|
|
160
170
|
}
|
|
161
171
|
|
|
162
172
|
fn volume_spot_radiance(world_position : vec3<f32>) -> vec3<f32> {
|
|
163
|
-
let spotSlot =
|
|
164
|
-
if (spotSlot
|
|
165
|
-
let spot =
|
|
173
|
+
let spotSlot = selected_cluster_light_slot(volume_params.light_color.w);
|
|
174
|
+
if (spotSlot < 0) { return vec3<f32>(0.0); }
|
|
175
|
+
let spot = light_data[u32(spotSlot)];
|
|
166
176
|
let radiance = evalVolumeSpot(
|
|
167
|
-
spot.position, spot.direction, spot.colorTimesIntensity,
|
|
168
|
-
spot.
|
|
177
|
+
spot.position.xyz, spot.direction.xyz, spot.colorTimesIntensity.xyz,
|
|
178
|
+
spot.colorTimesIntensity.w, spot.direction.w, spot.position.w, world_position,
|
|
169
179
|
);
|
|
170
180
|
let projectorUv = projectSpotUv(spotProjectorMatrix(spot), world_position);
|
|
171
181
|
if (!(projectorUv.x >= 0.0 && projectorUv.x <= 1.0 && projectorUv.y >= 0.0 && projectorUv.y <= 1.0)) {
|
|
@@ -178,9 +188,9 @@ fn volume_spot_radiance(world_position : vec3<f32>) -> vec3<f32> {
|
|
|
178
188
|
}
|
|
179
189
|
|
|
180
190
|
fn volume_spot_shadow_intensity() -> f32 {
|
|
181
|
-
let spotSlot =
|
|
182
|
-
if (spotSlot
|
|
183
|
-
return clamp(
|
|
191
|
+
let spotSlot = selected_cluster_light_slot(volume_params.light_color.w);
|
|
192
|
+
if (spotSlot < 0) { return 1.0; }
|
|
193
|
+
return clamp(light_data[u32(spotSlot)].auxiliary.z, 0.0, 1.0);
|
|
184
194
|
}
|
|
185
195
|
|
|
186
196
|
fn volume_light_radiance(world_position : vec3<f32>) -> vec3<f32> {
|