@forgeax/engine-shader 0.1.6 → 0.1.19
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 +61 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/ShaderRegistry.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +53 -2
- package/dist/index.mjs.map +1 -1
- package/dist/material-schemas.d.ts.map +1 -1
- package/package.json +6 -3
- package/src/ShaderRegistry.ts +50 -0
- package/src/__tests__/bloom-fxaa-tonemap.unit.test.ts +86 -12
- package/src/__tests__/default-standard-pbr-transmission.unit.test.ts +181 -0
- package/src/__tests__/lighting-punctual.unit.test.ts +1 -1
- package/src/__tests__/material-builtins.unit.test.ts +38 -1
- package/src/__tests__/material-contract.unit.test.ts +123 -0
- package/src/__tests__/motion-blur.unit.test.ts +44 -0
- package/src/__tests__/points-lines-shader.unit.test.ts +43 -0
- package/src/__tests__/scene-temporal.unit.test.ts +23 -5
- package/src/__tests__/shader-manifest-transmission.unit.test.ts +75 -0
- package/src/__tests__/shader.unit.test.ts +43 -3
- package/src/__tests__/sprite-variants.unit.test.ts +19 -0
- package/src/__tests__/taa-resolve.unit.test.ts +75 -3
- package/src/__tests__/transmission-thickness-scale.unit.test.ts +19 -0
- package/src/common.wgsl +30 -30
- package/src/default-standard-pbr-skin.wgsl +27 -9
- package/src/default-standard-pbr.wgsl +209 -28
- package/src/fxaa.wgsl +5 -6
- package/src/index.ts +3 -0
- package/src/material-schemas.ts +7 -0
- package/src/motion-blur.wgsl +77 -0
- package/src/pbr-temporal.wgsl +15 -2
- package/src/points-lines.wgsl +103 -0
- package/src/scene-temporal.wgsl +54 -0
- package/src/shadow_caster.wgsl +27 -2
- package/src/sprite-lit.wgsl +5 -4
- package/src/sprite.wgsl +5 -4
- package/src/taa-resolve.wgsl +76 -81
- package/src/tonemap.wgsl +7 -9
- package/src/unlit.wgsl +6 -5
- package/dist/__tests__/atmosphere-background.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-background.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-cubemap-orientation.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-cubemap-orientation.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-preetham-numeric.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-preetham-numeric.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-preetham-red.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-preetham-red.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-preetham.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-preetham.test.d.ts.map +0 -1
- package/dist/__tests__/atmosphere-three-r184-reference.test.d.ts +0 -2
- package/dist/__tests__/atmosphere-three-r184-reference.test.d.ts.map +0 -1
- package/dist/__tests__/bloom-fxaa-tonemap.unit.test.d.ts +0 -2
- package/dist/__tests__/bloom-fxaa-tonemap.unit.test.d.ts.map +0 -1
- package/dist/__tests__/builtin-texture-sampling-contract.test.d.ts +0 -2
- package/dist/__tests__/builtin-texture-sampling-contract.test.d.ts.map +0 -1
- package/dist/__tests__/default-standard-pbr-alpha.unit.test.d.ts +0 -2
- package/dist/__tests__/default-standard-pbr-alpha.unit.test.d.ts.map +0 -1
- package/dist/__tests__/deferred-lighting-ssao.test.d.ts +0 -2
- package/dist/__tests__/deferred-lighting-ssao.test.d.ts.map +0 -1
- package/dist/__tests__/fog-oracle.unit.test.d.ts +0 -2
- package/dist/__tests__/fog-oracle.unit.test.d.ts.map +0 -1
- package/dist/__tests__/fog-orthographic-ray.unit.test.d.ts +0 -2
- package/dist/__tests__/fog-orthographic-ray.unit.test.d.ts.map +0 -1
- package/dist/__tests__/fog-producer-matrix.integration.test.d.ts +0 -2
- package/dist/__tests__/fog-producer-matrix.integration.test.d.ts.map +0 -1
- package/dist/__tests__/hdrp-ssao-compile.test.d.ts +0 -2
- package/dist/__tests__/hdrp-ssao-compile.test.d.ts.map +0 -1
- package/dist/__tests__/ibl-irradiance.unit.test.d.ts +0 -2
- package/dist/__tests__/ibl-irradiance.unit.test.d.ts.map +0 -1
- package/dist/__tests__/ibl-sampling.unit.test.d.ts +0 -2
- package/dist/__tests__/ibl-sampling.unit.test.d.ts.map +0 -1
- package/dist/__tests__/lighting-punctual.unit.test.d.ts +0 -2
- package/dist/__tests__/lighting-punctual.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-artifact-dedup.unit.test.d.ts +0 -2
- package/dist/__tests__/material-artifact-dedup.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-builtins.unit.test.d.ts +0 -2
- package/dist/__tests__/material-builtins.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-contract.unit.test.d.ts +0 -2
- package/dist/__tests__/material-contract.unit.test.d.ts.map +0 -1
- package/dist/__tests__/material-derived-builtins.integration.test.d.ts +0 -2
- package/dist/__tests__/material-derived-builtins.integration.test.d.ts.map +0 -1
- package/dist/__tests__/public-surface.unit.test.d.ts +0 -2
- package/dist/__tests__/public-surface.unit.test.d.ts.map +0 -1
- package/dist/__tests__/scene-temporal.unit.test.d.ts +0 -2
- package/dist/__tests__/scene-temporal.unit.test.d.ts.map +0 -1
- package/dist/__tests__/shader-registry-transaction.unit.test.d.ts +0 -2
- package/dist/__tests__/shader-registry-transaction.unit.test.d.ts.map +0 -1
- package/dist/__tests__/shader.unit.test.d.ts +0 -2
- package/dist/__tests__/shader.unit.test.d.ts.map +0 -1
- package/dist/__tests__/sprite-lit-shader.test.d.ts +0 -2
- package/dist/__tests__/sprite-lit-shader.test.d.ts.map +0 -1
- package/dist/__tests__/sprite-variants.unit.test.d.ts +0 -2
- package/dist/__tests__/sprite-variants.unit.test.d.ts.map +0 -1
- package/dist/__tests__/taa-resolve.unit.test.d.ts +0 -2
- package/dist/__tests__/taa-resolve.unit.test.d.ts.map +0 -1
- package/dist/__tests__/transparent-pbr.unit.test.d.ts +0 -2
- package/dist/__tests__/transparent-pbr.unit.test.d.ts.map +0 -1
- package/dist/__tests__/vertex-color-variant.unit.test.d.ts +0 -2
- package/dist/__tests__/vertex-color-variant.unit.test.d.ts.map +0 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#define_import_path forgeax_material::points_lines
|
|
2
|
+
|
|
3
|
+
// Portable triangle expansion for square/circle points and independent butt
|
|
4
|
+
// line-list segments. All dimensions are physical pixels; no native wide
|
|
5
|
+
// primitive state is required by any raster lane.
|
|
6
|
+
|
|
7
|
+
struct PointsLinesView {
|
|
8
|
+
worldViewProj : mat4x4<f32>,
|
|
9
|
+
model : mat4x4<f32>,
|
|
10
|
+
physicalViewport : vec2<f32>,
|
|
11
|
+
style : vec4<f32>,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
@group(0) @binding(10) var<uniform> pointsLinesView : PointsLinesView;
|
|
15
|
+
|
|
16
|
+
struct PointsLinesMaterial {
|
|
17
|
+
baseColor : vec4<f32>,
|
|
18
|
+
alphaCutoff : f32,
|
|
19
|
+
_materialPadding : vec3<f32>,
|
|
20
|
+
baseColorTextureCoordinatesTransform : vec4<f32>,
|
|
21
|
+
baseColorTextureCoordinatesMetadata : vec4<f32>,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
@group(1) @binding(0) var<uniform> material : PointsLinesMaterial;
|
|
25
|
+
@group(1) @binding(1) var baseColorSampler : sampler;
|
|
26
|
+
@group(1) @binding(2) var baseColorTexture : texture_2d<f32>;
|
|
27
|
+
|
|
28
|
+
struct PointsLinesVertex {
|
|
29
|
+
@location(0) position : vec3<f32>,
|
|
30
|
+
@location(1) otherPosition : vec3<f32>,
|
|
31
|
+
@location(2) corner : vec2<f32>,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
struct PointsLinesFragment {
|
|
35
|
+
@builtin(position) position : vec4<f32>,
|
|
36
|
+
@location(0) @interpolate(flat) shape : f32,
|
|
37
|
+
@location(1) @interpolate(linear) sampleCenter : vec2<f32>,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
fn clipPixelDelta(clip : vec4<f32>, pixels : vec2<f32>) -> vec2<f32> {
|
|
41
|
+
let viewport = max(pointsLinesView.physicalViewport, vec2<f32>(1.0, 1.0));
|
|
42
|
+
let ndcPerPixel = vec2<f32>(2.0 / viewport.x, -2.0 / viewport.y);
|
|
43
|
+
return pixels * ndcPerPixel * clip.w;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fn expandPoint(position : vec3<f32>, corner : vec2<f32>, sizePx : f32) -> vec4<f32> {
|
|
47
|
+
let clip = pointsLinesView.worldViewProj * pointsLinesView.model * vec4<f32>(position, 1.0);
|
|
48
|
+
let offset = clipPixelDelta(clip, corner * (sizePx * 0.5));
|
|
49
|
+
return vec4<f32>(clip.xy + offset, clip.z, clip.w);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fn expandLine(
|
|
53
|
+
start : vec3<f32>,
|
|
54
|
+
end : vec3<f32>,
|
|
55
|
+
corner : vec2<f32>,
|
|
56
|
+
widthPx : f32,
|
|
57
|
+
) -> vec4<f32> {
|
|
58
|
+
let startClip = pointsLinesView.worldViewProj * pointsLinesView.model * vec4<f32>(start, 1.0);
|
|
59
|
+
let endClip = pointsLinesView.worldViewProj * pointsLinesView.model * vec4<f32>(end, 1.0);
|
|
60
|
+
let startNdc = startClip.xy / max(startClip.w, 0.000001);
|
|
61
|
+
let endNdc = endClip.xy / max(endClip.w, 0.000001);
|
|
62
|
+
let tangent = endNdc - startNdc;
|
|
63
|
+
let safeTangent = select(vec2<f32>(1.0, 0.0), tangent, dot(tangent, tangent) > 0.0000001);
|
|
64
|
+
let axis = normalize(safeTangent);
|
|
65
|
+
let normal = vec2<f32>(-axis.y, axis.x);
|
|
66
|
+
let endpoint = select(startClip, endClip, corner.x > 0.0);
|
|
67
|
+
let offset = clipPixelDelta(endpoint, normal * corner.y * (widthPx * 0.5));
|
|
68
|
+
// butt line-list: each segment owns exactly its two endpoint positions.
|
|
69
|
+
return vec4<f32>(endpoint.xy + offset, endpoint.z, endpoint.w);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fn circleCoverage(sampleCenter : vec2<f32>) -> bool {
|
|
73
|
+
return dot(sampleCenter, sampleCenter) <= 1.0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@vertex
|
|
77
|
+
fn vs_main(input : PointsLinesVertex) -> PointsLinesFragment {
|
|
78
|
+
let isLine = pointsLinesView.style.y > 0.5;
|
|
79
|
+
let isCircle = pointsLinesView.style.z > 0.5 && !isLine;
|
|
80
|
+
var output : PointsLinesFragment;
|
|
81
|
+
output.position = select(
|
|
82
|
+
expandPoint(input.position, input.corner, pointsLinesView.style.x),
|
|
83
|
+
expandLine(input.position, input.otherPosition, input.corner, pointsLinesView.style.x),
|
|
84
|
+
isLine,
|
|
85
|
+
);
|
|
86
|
+
output.shape = select(0.0, 1.0, isCircle);
|
|
87
|
+
output.sampleCenter = select(input.corner, vec2<f32>(0.0, 0.0), isLine);
|
|
88
|
+
return output;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@fragment
|
|
92
|
+
fn fs_main(input : PointsLinesFragment) -> @location(0) vec4<f32> {
|
|
93
|
+
if input.shape > 0.5 && !circleCoverage(input.sampleCenter) {
|
|
94
|
+
discard;
|
|
95
|
+
}
|
|
96
|
+
let uv = material.baseColorTextureCoordinatesMetadata.xy;
|
|
97
|
+
let textureColor = textureSample(baseColorTexture, baseColorSampler, uv);
|
|
98
|
+
let color = material.baseColor * textureColor;
|
|
99
|
+
if material.alphaCutoff > 0.0 && color.a < material.alphaCutoff {
|
|
100
|
+
discard;
|
|
101
|
+
}
|
|
102
|
+
return color;
|
|
103
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#define_import_path forgeax_scene_temporal
|
|
2
|
+
|
|
3
|
+
// The sole shader interpretation of forgeax::scene-data::temporal-v1.
|
|
4
|
+
// The packed target is rgba16float at render resolution and is sampled only.
|
|
5
|
+
|
|
6
|
+
const SCENE_DATA_TEMPORAL_V1_CLEAR : vec4<f32> = vec4<f32>(0.0, 0.0, -1.0, 1.0);
|
|
7
|
+
|
|
8
|
+
struct SceneTemporalV1 {
|
|
9
|
+
motionUv : vec2<f32>,
|
|
10
|
+
viewDepth : f32,
|
|
11
|
+
reactive : f32,
|
|
12
|
+
validDepth : bool,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
fn unpackSceneTemporalV1(packed : vec4<f32>) -> SceneTemporalV1 {
|
|
16
|
+
let invalidDepth = packed.z < 0.0;
|
|
17
|
+
let validDepth = !invalidDepth;
|
|
18
|
+
let viewDepth = select(0.0, exp2(packed.z) - 1.0, validDepth);
|
|
19
|
+
let motionUv = packed.xy;
|
|
20
|
+
let reactive = clamp(packed.w, 0.0, 1.0);
|
|
21
|
+
return SceneTemporalV1(motionUv, viewDepth, reactive, validDepth);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fn sceneTemporalUv(clip : vec4<f32>) -> vec2<f32> {
|
|
25
|
+
let safeW = select(1e-6, clip.w, abs(clip.w) >= 1e-6);
|
|
26
|
+
let ndc = clip.xy / safeW;
|
|
27
|
+
return vec2<f32>(ndc.x * 0.5 + 0.5, 0.5 - ndc.y * 0.5);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fn sceneTemporalViewDepth(clip : vec4<f32>, temporalProjection : vec4<f32>) -> f32 {
|
|
31
|
+
let perspectiveDepth = max(clip.w, 0.0);
|
|
32
|
+
let ndcDepth = clip.z / max(abs(clip.w), 1e-6);
|
|
33
|
+
let orthographicDepth = temporalProjection.x +
|
|
34
|
+
ndcDepth * (temporalProjection.y - temporalProjection.x);
|
|
35
|
+
let viewDepth = select(
|
|
36
|
+
perspectiveDepth,
|
|
37
|
+
max(orthographicDepth, 0.0),
|
|
38
|
+
temporalProjection.z >= 0.5,
|
|
39
|
+
);
|
|
40
|
+
return log2(1.0 + viewDepth);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fn packSceneTemporalV1(
|
|
44
|
+
currentClip : vec4<f32>,
|
|
45
|
+
previousClip : vec4<f32>,
|
|
46
|
+
temporalProjection : vec4<f32>,
|
|
47
|
+
reactive : f32,
|
|
48
|
+
) -> vec4<f32> {
|
|
49
|
+
return vec4<f32>(
|
|
50
|
+
sceneTemporalUv(currentClip) - sceneTemporalUv(previousClip),
|
|
51
|
+
sceneTemporalViewDepth(currentClip, temporalProjection),
|
|
52
|
+
clamp(reactive, 0.0, 1.0),
|
|
53
|
+
);
|
|
54
|
+
}
|
package/src/shadow_caster.wgsl
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
2
|
+
#pragma variant_axis SKINNING_DISABLED
|
|
2
3
|
|
|
3
4
|
// @forgeax/engine-shader shadow_caster.wgsl
|
|
4
5
|
// feat-20260520-directional-light-shadow-mapping M1c / w9 (D-9 / AC-09):
|
|
@@ -20,15 +21,28 @@
|
|
|
20
21
|
// @group(2) binding(0) meshes : array<Mesh> -- common.wgsl
|
|
21
22
|
// @group(3) binding(0) instances : array<InstanceData> -- common.wgsl
|
|
22
23
|
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
24
|
+
// SKINNING_DISABLED=true consumes the ordinary 12F mesh layout; the explicit
|
|
25
|
+
// false variant consumes JOINTS_0 / WEIGHTS_0 from the same 18F layout as pbr-skin and
|
|
26
|
+
// projects the animated world-space palette result into the shadow view.
|
|
25
27
|
|
|
26
28
|
#import forgeax_view::common::{View, Mesh, InstanceData, ShadowCasterCascade, view, shadowCasterCascade, meshes, instances}
|
|
27
29
|
|
|
28
30
|
struct VsInput {
|
|
29
31
|
@location(0) position : vec3<f32>,
|
|
32
|
+
#if SKINNING_DISABLED == false
|
|
33
|
+
@location(4) skinIndex : vec4<u32>,
|
|
34
|
+
@location(5) skinWeight : vec4<f32>,
|
|
35
|
+
#endif
|
|
30
36
|
};
|
|
31
37
|
|
|
38
|
+
#if SKINNING_DISABLED == false
|
|
39
|
+
#if STORAGE_BUFFER_AVAILABLE == true
|
|
40
|
+
@group(2) @binding(1) var<storage, read> palette : array<mat4x4<f32>>;
|
|
41
|
+
#else
|
|
42
|
+
@group(2) @binding(1) var<uniform> palette : array<mat4x4<f32>, 255>;
|
|
43
|
+
#endif
|
|
44
|
+
#endif
|
|
45
|
+
|
|
32
46
|
fn _cascadeLightViewProj(layer : u32) -> mat4x4<f32> {
|
|
33
47
|
switch (layer) {
|
|
34
48
|
case 0u: { return view.lightViewProj_A; }
|
|
@@ -40,7 +54,18 @@ fn _cascadeLightViewProj(layer : u32) -> mat4x4<f32> {
|
|
|
40
54
|
|
|
41
55
|
@vertex
|
|
42
56
|
fn vs_main(in : VsInput, @builtin(instance_index) idx : u32) -> @builtin(position) vec4<f32> {
|
|
57
|
+
#if SKINNING_DISABLED == false
|
|
58
|
+
let skinMatrix = palette[in.skinIndex.x] * in.skinWeight.x +
|
|
59
|
+
palette[in.skinIndex.y] * in.skinWeight.y +
|
|
60
|
+
palette[in.skinIndex.z] * in.skinWeight.z +
|
|
61
|
+
palette[in.skinIndex.w] * in.skinWeight.w;
|
|
62
|
+
// Palette entries are jointWorld * inverseBind and therefore already
|
|
63
|
+
// produce world-space positions. Applying meshes[0].worldFromLocal again
|
|
64
|
+
// would double-transform a parented skinned entity.
|
|
65
|
+
let worldPos = skinMatrix * vec4<f32>(in.position, 1.0);
|
|
66
|
+
#else
|
|
43
67
|
let worldPos = meshes[0].worldFromLocal * instances[idx].localFromInstance * vec4<f32>(in.position, 1.0);
|
|
68
|
+
#endif
|
|
44
69
|
// feat-20260625-spot-light-shadow-mapping M2 / w10 (D-1): spot shadow passes
|
|
45
70
|
// set `isSpot = 1u` and write their perspective matrix into
|
|
46
71
|
// `spotLightViewProj`; directional cascade passes keep `isSpot = 0u` and read
|
package/src/sprite-lit.wgsl
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
2
2
|
#define_import_path forgeax_material::sprite-lit
|
|
3
3
|
|
|
4
|
-
#import forgeax_view::common::{View, FogViewParams, FogRay, Mesh, InstanceData, PointLight, SpotLight, view, meshes, instances, pointLightsBuffer, spotLightsBuffer, sampleMaterialTexture
|
|
4
|
+
#import forgeax_view::common::{View, FogViewParams, FogRay, Mesh, InstanceData, PointLight, SpotLight, view, meshes, instances, pointLightsBuffer, spotLightsBuffer, sampleMaterialTexture}
|
|
5
|
+
#import forgeax_scene_temporal::{packSceneTemporalV1}
|
|
5
6
|
#import forgeax_view::fog::{apply_fog}
|
|
6
7
|
|
|
7
8
|
// @forgeax/engine-shader - sprite-lit.wgsl
|
|
@@ -272,8 +273,8 @@ fn fs_main_hdr(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
272
273
|
struct TemporalVsOut {
|
|
273
274
|
@builtin(position) clip : vec4<f32>,
|
|
274
275
|
@location(0) uvAtlas : vec2<f32>,
|
|
275
|
-
@location(1) @interpolate(
|
|
276
|
-
@location(2) @interpolate(
|
|
276
|
+
@location(1) @interpolate(perspective) currentClip : vec4<f32>,
|
|
277
|
+
@location(2) @interpolate(perspective) previousClip : vec4<f32>,
|
|
277
278
|
};
|
|
278
279
|
|
|
279
280
|
@vertex
|
|
@@ -301,7 +302,7 @@ fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
|
301
302
|
if (alpha <= 0.0) {
|
|
302
303
|
discard;
|
|
303
304
|
}
|
|
304
|
-
return
|
|
305
|
+
return packSceneTemporalV1(in.currentClip, in.previousClip, view.temporalProjection, 1.0);
|
|
305
306
|
}
|
|
306
307
|
// sprite-lit needs at least one light in the scene to be visible; for unlit
|
|
307
308
|
// sprites use forgeax::sprite (1 string change in MaterialAsset).
|
package/src/sprite.wgsl
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
#pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
3
3
|
#pragma variant_axis PER_INSTANCE_REGION
|
|
4
4
|
|
|
5
|
-
#import forgeax_view::common::{View, FogViewParams, FogRay, Mesh, InstanceData, view, meshes, instances, sampleMaterialTexture
|
|
5
|
+
#import forgeax_view::common::{View, FogViewParams, FogRay, Mesh, InstanceData, view, meshes, instances, sampleMaterialTexture}
|
|
6
|
+
#import forgeax_scene_temporal::{packSceneTemporalV1}
|
|
6
7
|
#import forgeax_view::fog::{apply_fog}
|
|
7
8
|
|
|
8
9
|
// @forgeax/engine-shader - sprite.wgsl (feat-20260520-2d-sprite-layer-mvp /
|
|
@@ -330,8 +331,8 @@ fn fs_main_hdr(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
330
331
|
struct TemporalVsOut {
|
|
331
332
|
@builtin(position) clip : vec4<f32>,
|
|
332
333
|
@location(0) uvAtlas : vec2<f32>,
|
|
333
|
-
@location(1) @interpolate(
|
|
334
|
-
@location(2) @interpolate(
|
|
334
|
+
@location(1) @interpolate(perspective) currentClip : vec4<f32>,
|
|
335
|
+
@location(2) @interpolate(perspective) previousClip : vec4<f32>,
|
|
335
336
|
};
|
|
336
337
|
|
|
337
338
|
@vertex
|
|
@@ -359,7 +360,7 @@ fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
|
359
360
|
if (alpha <= 0.0) {
|
|
360
361
|
discard;
|
|
361
362
|
}
|
|
362
|
-
return
|
|
363
|
+
return packSceneTemporalV1(in.currentClip, in.previousClip, view.temporalProjection, 1.0);
|
|
363
364
|
}
|
|
364
365
|
// sprite is unlit; do NOT read the directional-light fields of the View
|
|
365
366
|
// UBO -- same SSOT hard rule as the header comment above; R5 mitigation.
|
package/src/taa-resolve.wgsl
CHANGED
|
@@ -1,7 +1,28 @@
|
|
|
1
1
|
#define_import_path forgeax_view::taa_resolve
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
// Keep the fragment input shape explicit for compilers that tree-shake imported
|
|
4
|
+
// type declarations before resolving the vertex entry point.
|
|
5
|
+
struct FullscreenOutput {
|
|
6
|
+
@builtin(position) position : vec4<f32>,
|
|
7
|
+
@location(0) uv : vec2<f32>,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
fn fullscreen_triangle(vertex_index : u32) -> FullscreenOutput {
|
|
11
|
+
var x : f32 = -1.0;
|
|
12
|
+
var y : f32 = -1.0;
|
|
13
|
+
if (vertex_index == 1u) {
|
|
14
|
+
x = 3.0;
|
|
15
|
+
}
|
|
16
|
+
if (vertex_index == 2u) {
|
|
17
|
+
y = 3.0;
|
|
18
|
+
}
|
|
19
|
+
let u : f32 = (x + 1.0) * 0.5;
|
|
20
|
+
let v : f32 = 1.0 - (y + 1.0) * 0.5;
|
|
21
|
+
var out : FullscreenOutput;
|
|
22
|
+
out.position = vec4<f32>(x, y, 0.0, 1.0);
|
|
23
|
+
out.uv = vec2<f32>(u, v);
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
5
26
|
|
|
6
27
|
struct TaaResolveParams {
|
|
7
28
|
currentJitterUv : vec2<f32>,
|
|
@@ -14,12 +35,15 @@ struct TaaResolveOutput {
|
|
|
14
35
|
@location(1) temporal : vec4<f32>,
|
|
15
36
|
};
|
|
16
37
|
|
|
17
|
-
@group(
|
|
18
|
-
@group(
|
|
19
|
-
@group(
|
|
20
|
-
@group(
|
|
21
|
-
@group(
|
|
22
|
-
@group(
|
|
38
|
+
@group(1) @binding(0) var currentColor : texture_2d<f32>;
|
|
39
|
+
@group(1) @binding(1) var currentSampler : sampler;
|
|
40
|
+
@group(1) @binding(2) var historyColor : texture_2d<f32>;
|
|
41
|
+
@group(1) @binding(3) var historySampler : sampler;
|
|
42
|
+
@group(1) @binding(4) var historyTemporal : texture_2d<f32>;
|
|
43
|
+
@group(1) @binding(5) var temporalSampler : sampler;
|
|
44
|
+
@group(1) @binding(6) var currentTemporal : texture_2d<f32>;
|
|
45
|
+
@group(1) @binding(7) var currentTemporalSampler : sampler;
|
|
46
|
+
@group(1) @binding(8) var<uniform> params : TaaResolveParams;
|
|
23
47
|
|
|
24
48
|
fn rgbToYCoCg(rgb : vec3<f32>) -> vec3<f32> {
|
|
25
49
|
return vec3<f32>(
|
|
@@ -37,93 +61,64 @@ fn luminance(rgb : vec3<f32>) -> f32 {
|
|
|
37
61
|
return dot(rgb, vec3<f32>(0.2126, 0.7152, 0.0722));
|
|
38
62
|
}
|
|
39
63
|
|
|
40
|
-
fn
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// packed motion is already unjittered currentUv - previousUv.
|
|
44
|
-
return textureSampleLevel(currentColor, historySampler, uv + params.currentJitterUv, 0.0);
|
|
64
|
+
fn closestCurrentTemporal(pixel : vec2<i32>, dimensions : vec2<i32>) -> vec4<f32> {
|
|
65
|
+
let clamped = clamp(pixel, vec2<i32>(0), dimensions - vec2<i32>(1));
|
|
66
|
+
return textureLoad(currentTemporal, clamped, 0);
|
|
45
67
|
}
|
|
46
68
|
|
|
47
|
-
fn
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
let candidate = textureLoad(currentTemporal, samplePixel, 0);
|
|
54
|
-
if candidate.z >= 0.0 && candidate.z < closestDepth {
|
|
55
|
-
closest = candidate;
|
|
56
|
-
closestDepth = candidate.z;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return closest;
|
|
69
|
+
fn neighborhoodSquareMean(uv : vec2<f32>, texel : vec2<f32>) -> vec3<f32> {
|
|
70
|
+
let north = textureSampleLevel(currentColor, currentSampler, uv + vec2<f32>(0.0, texel.y), 0.0).rgb;
|
|
71
|
+
let south = textureSampleLevel(currentColor, currentSampler, uv - vec2<f32>(0.0, texel.y), 0.0).rgb;
|
|
72
|
+
let east = textureSampleLevel(currentColor, currentSampler, uv + vec2<f32>(texel.x, 0.0), 0.0).rgb;
|
|
73
|
+
let west = textureSampleLevel(currentColor, currentSampler, uv - vec2<f32>(texel.x, 0.0), 0.0).rgb;
|
|
74
|
+
return (north + south + east + west) * 0.25;
|
|
61
75
|
}
|
|
62
76
|
|
|
63
77
|
@vertex
|
|
64
|
-
fn
|
|
78
|
+
fn vs_main(@builtin(vertex_index) vertexIndex : u32) -> FullscreenOutput {
|
|
65
79
|
return fullscreen_triangle(vertexIndex);
|
|
66
80
|
}
|
|
67
81
|
|
|
68
|
-
@fragment
|
|
69
82
|
fn fs_taa_resolve(in : FullscreenOutput) -> TaaResolveOutput {
|
|
70
|
-
let dimensions = vec2<i32>(textureDimensions(currentColor));
|
|
71
|
-
let
|
|
72
|
-
let current =
|
|
83
|
+
let dimensions = vec2<i32>(textureDimensions(currentColor, 0));
|
|
84
|
+
let texel = 1.0 / vec2<f32>(dimensions);
|
|
85
|
+
let current = textureSampleLevel(currentColor, currentSampler, in.uv + params.currentJitterUv, 0.0);
|
|
86
|
+
let pixel = vec2<i32>(in.uv * vec2<f32>(dimensions));
|
|
73
87
|
let temporal = closestCurrentTemporal(pixel, dimensions);
|
|
74
|
-
|
|
75
|
-
var output : TaaResolveOutput;
|
|
76
|
-
output.temporal = temporal;
|
|
77
|
-
output.color = current;
|
|
78
|
-
|
|
79
88
|
let historyUv = in.uv - temporal.xy;
|
|
80
|
-
let historyInBounds = all(historyUv >= vec2<f32>(0.0)) && all(historyUv
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
let previousTemporal = textureSampleLevel(historyTemporal, historySampler, historyUv, 0.0);
|
|
89
|
+
let historyInBounds = all(historyUv >= vec2<f32>(0.0)) && all(historyUv <= vec2<f32>(1.0));
|
|
90
|
+
let history = textureSampleLevel(historyColor, historySampler, historyUv, 0.0);
|
|
91
|
+
let previousTemporal = textureSampleLevel(historyTemporal, temporalSampler, historyUv, 0.0);
|
|
86
92
|
let depthDelta = abs(previousTemporal.z - temporal.z);
|
|
87
|
-
let depthThreshold =
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
neighborhoodMin = min(neighborhoodMin, sampleValue);
|
|
101
|
-
neighborhoodMax = max(neighborhoodMax, sampleValue);
|
|
102
|
-
neighborhoodMean += sampleValue;
|
|
103
|
-
neighborhoodSquareMean += sampleValue * sampleValue;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
neighborhoodMean /= 9.0;
|
|
107
|
-
neighborhoodSquareMean /= 9.0;
|
|
108
|
-
let sigma = sqrt(max(neighborhoodSquareMean - neighborhoodMean * neighborhoodMean, vec3<f32>(0.0)));
|
|
109
|
-
let clipMin = max(neighborhoodMin, neighborhoodMean - sigma * 1.25);
|
|
110
|
-
let clipMax = min(neighborhoodMax, neighborhoodMean + sigma * 1.25);
|
|
111
|
-
|
|
112
|
-
let sampledHistory = textureSampleLevel(historyColor, historySampler, historyUv, 0.0);
|
|
113
|
-
let clippedHistoryRgb = yCoCgToRgb(clamp(rgbToYCoCg(sampledHistory.rgb), clipMin, clipMax));
|
|
114
|
-
let velocityFactor = 1.0 - clamp(length(temporal.xy) * 64.0, 0.0, 1.0);
|
|
93
|
+
let depthThreshold = 0.0025 + temporal.z * 0.01;
|
|
94
|
+
let rejected =
|
|
95
|
+
params.historyValid == 0u ||
|
|
96
|
+
!historyInBounds ||
|
|
97
|
+
temporal.z < 0.0 ||
|
|
98
|
+
previousTemporal.z < 0.0 ||
|
|
99
|
+
depthDelta > depthThreshold;
|
|
100
|
+
let neighborhood = neighborhoodSquareMean(in.uv, texel);
|
|
101
|
+
let neighborhoodDelta = abs(luminance(current.rgb) - luminance(neighborhood));
|
|
102
|
+
let clipPadding = vec3<f32>(0.5);
|
|
103
|
+
let clipMin = rgbToYCoCg(min(current.rgb, neighborhood - vec3<f32>(neighborhoodDelta) - clipPadding));
|
|
104
|
+
let clipMax = rgbToYCoCg(max(current.rgb, neighborhood + vec3<f32>(neighborhoodDelta) + clipPadding));
|
|
105
|
+
let clippedHistoryRgb = yCoCgToRgb(clamp(rgbToYCoCg(history.rgb), clipMin, clipMax));
|
|
115
106
|
let reactiveFactor = 1.0 - clamp(temporal.w, 0.0, 1.0);
|
|
107
|
+
let velocityFactor = 1.0 - clamp(length(temporal.xy) * 64.0, 0.0, 1.0);
|
|
116
108
|
let depthFactor = 1.0 - clamp(depthDelta / depthThreshold, 0.0, 1.0);
|
|
117
|
-
let
|
|
118
|
-
let historyLuma = luminance(clippedHistoryRgb);
|
|
119
|
-
let lumaDelta = abs(historyLuma - currentLuma);
|
|
120
|
-
let unbiasedLumaDelta = clamp(lumaDelta / max(max(currentLuma, historyLuma), 0.2), 0.0, 1.0);
|
|
109
|
+
let unbiasedLumaDelta = clamp(abs(luminance(current.rgb) - luminance(history.rgb)), 0.0, 1.0);
|
|
121
110
|
let lumaFactor = (1.0 - unbiasedLumaDelta) * (1.0 - unbiasedLumaDelta);
|
|
122
111
|
let fixedPresetWeight = mix(0.88, 0.97, lumaFactor);
|
|
123
|
-
let
|
|
124
|
-
let
|
|
125
|
-
let
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
112
|
+
let progressiveWeight = select(fixedPresetWeight, 0.0, params.temporalFrameIndex < 8u);
|
|
113
|
+
let historyWeight = progressiveWeight * reactiveFactor * velocityFactor * depthFactor;
|
|
114
|
+
let resolved = select(mix(current.rgb, clippedHistoryRgb, historyWeight), current.rgb, rejected);
|
|
115
|
+
return TaaResolveOutput(
|
|
116
|
+
vec4<f32>(resolved, current.a),
|
|
117
|
+
temporal,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@fragment
|
|
122
|
+
fn fs_main(in : FullscreenOutput) -> TaaResolveOutput {
|
|
123
|
+
return fs_taa_resolve(in);
|
|
129
124
|
}
|
package/src/tonemap.wgsl
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#define_import_path forgeax_view::tonemap
|
|
2
2
|
|
|
3
|
-
// @forgeax/engine-shader - Three r184
|
|
3
|
+
// @forgeax/engine-shader - Three r184 Output Transform module.
|
|
4
4
|
//
|
|
5
5
|
// The public modes and formulas are the Three.js r184 oracle. The numeric
|
|
6
6
|
// values are the binding contract mirrored by TONEMAP_SHADER_MODE in tonemap.ts.
|
|
@@ -153,8 +153,7 @@ fn tonemapNeutral(color : vec3<f32>) -> vec3<f32> {
|
|
|
153
153
|
|
|
154
154
|
@fragment
|
|
155
155
|
fn fs_main(in : FullscreenOutput) -> @location(0) vec4<f32> {
|
|
156
|
-
//
|
|
157
|
-
// linearLdrColorDomain: the output pass emits the display-target domain.
|
|
156
|
+
// Output Transform consumes linear HDR and emits display-encoded color.
|
|
158
157
|
let source = textureSample(hdr, samp, in.uv);
|
|
159
158
|
let sample : vec3<f32> = source.rgb;
|
|
160
159
|
let exposed : vec3<f32> = sample * params.exposure;
|
|
@@ -167,11 +166,10 @@ fn fs_main(in : FullscreenOutput) -> @location(0) vec4<f32> {
|
|
|
167
166
|
case 5u: { mapped = tonemapAgx(exposed); }
|
|
168
167
|
case 6u: { mapped = tonemapNeutral(exposed); }
|
|
169
168
|
case 7u: { mapped = tonemapReinhard(exposed); }
|
|
170
|
-
// Mode 0
|
|
171
|
-
|
|
172
|
-
default: { mapped = linearToSrgbOetf(exposed); }
|
|
169
|
+
// Mode 0 skips the tone curve but still performs the output conversion.
|
|
170
|
+
default: { mapped = sample; }
|
|
173
171
|
}
|
|
174
|
-
//
|
|
175
|
-
|
|
176
|
-
return vec4<f32>(
|
|
172
|
+
// Every public mode reaches exactly one shared sRGB OETF.
|
|
173
|
+
let encoded = linearToSrgbOetf(mapped);
|
|
174
|
+
return vec4<f32>(encoded, source.a);
|
|
177
175
|
}
|
package/src/unlit.wgsl
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#define_import_path forgeax_material::unlit
|
|
2
|
-
#import forgeax_view::common::{View, FogViewParams, FogRay, Mesh, InstanceData, view, meshes, instances, sampleMaterialTextureLinear
|
|
2
|
+
#import forgeax_view::common::{View, FogViewParams, FogRay, Mesh, InstanceData, view, meshes, instances, sampleMaterialTextureLinear}
|
|
3
|
+
#import forgeax_scene_temporal::{packSceneTemporalV1}
|
|
3
4
|
#import forgeax_view::fog::{apply_fog}
|
|
4
5
|
|
|
5
6
|
#pragma variant_axis STORAGE_BUFFER_AVAILABLE
|
|
@@ -135,8 +136,8 @@ fn fs_main(in : VsOut) -> @location(0) vec4<f32> {
|
|
|
135
136
|
struct TemporalVsOut {
|
|
136
137
|
@builtin(position) clip : vec4<f32>,
|
|
137
138
|
@location(0) uv : vec2<f32>,
|
|
138
|
-
@location(1) @interpolate(
|
|
139
|
-
@location(2) @interpolate(
|
|
139
|
+
@location(1) @interpolate(perspective) currentClip : vec4<f32>,
|
|
140
|
+
@location(2) @interpolate(perspective) previousClip : vec4<f32>,
|
|
140
141
|
#ifdef VERTEX_COLOR_AVAILABLE
|
|
141
142
|
@location(14) color : vec4<f32>,
|
|
142
143
|
#endif
|
|
@@ -178,9 +179,9 @@ fn fs_temporal(in : TemporalVsOut) -> @location(0) vec4<f32> {
|
|
|
178
179
|
if (material.alphaCutoff > 0.0 && alpha < material.alphaCutoff) {
|
|
179
180
|
discard;
|
|
180
181
|
}
|
|
181
|
-
var reactive =
|
|
182
|
+
var reactive = 0.0;
|
|
182
183
|
#if STORAGE_BUFFER_AVAILABLE == true
|
|
183
184
|
reactive = meshes[0].temporal.x;
|
|
184
185
|
#endif
|
|
185
|
-
return
|
|
186
|
+
return packSceneTemporalV1(in.currentClip, in.previousClip, view.temporalProjection, reactive);
|
|
186
187
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"atmosphere-background.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/atmosphere-background.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"atmosphere-cubemap-orientation.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/atmosphere-cubemap-orientation.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"atmosphere-preetham-numeric.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/atmosphere-preetham-numeric.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"atmosphere-preetham-red.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/atmosphere-preetham-red.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"atmosphere-preetham.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/atmosphere-preetham.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"atmosphere-three-r184-reference.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/atmosphere-three-r184-reference.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bloom-fxaa-tonemap.unit.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/bloom-fxaa-tonemap.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"builtin-texture-sampling-contract.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/builtin-texture-sampling-contract.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"default-standard-pbr-alpha.unit.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/default-standard-pbr-alpha.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deferred-lighting-ssao.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/deferred-lighting-ssao.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fog-oracle.unit.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/fog-oracle.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fog-orthographic-ray.unit.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/fog-orthographic-ray.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fog-producer-matrix.integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/fog-producer-matrix.integration.test.ts"],"names":[],"mappings":""}
|