reze-engine 0.63.0 → 0.65.0
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/dist/engine.d.ts +226 -96
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +1187 -732
- package/dist/graph/compile.d.ts.map +1 -1
- package/dist/graph/compile.js +4 -1
- package/dist/graph/registry.d.ts +3 -0
- package/dist/graph/registry.d.ts.map +1 -1
- package/dist/graph/registry.js +133 -0
- package/dist/graph/schema.d.ts +12 -0
- package/dist/graph/schema.d.ts.map +1 -1
- package/dist/graph/slots.d.ts.map +1 -1
- package/dist/graph/slots.js +15 -18
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/model.d.ts +10 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +15 -1
- package/dist/outline-normals.d.ts +12 -0
- package/dist/outline-normals.d.ts.map +1 -0
- package/dist/outline-normals.js +100 -0
- package/dist/pmx-loader.d.ts +3 -0
- package/dist/pmx-loader.d.ts.map +1 -1
- package/dist/pmx-loader.js +7 -2
- package/dist/shaders/lights.d.ts +3 -2
- package/dist/shaders/lights.d.ts.map +1 -1
- package/dist/shaders/lights.js +16 -3
- package/dist/shaders/materials/common.d.ts.map +1 -1
- package/dist/shaders/materials/common.js +65 -56
- package/dist/shaders/materials/nodes.d.ts +1 -1
- package/dist/shaders/materials/nodes.d.ts.map +1 -1
- package/dist/shaders/materials/nodes.js +210 -0
- package/dist/shaders/passes/bloom.d.ts +4 -3
- package/dist/shaders/passes/bloom.d.ts.map +1 -1
- package/dist/shaders/passes/bloom.js +58 -87
- package/dist/shaders/passes/composite.d.ts.map +1 -1
- package/dist/shaders/passes/composite.js +122 -64
- package/dist/shaders/passes/cull.d.ts +1 -1
- package/dist/shaders/passes/cull.d.ts.map +1 -1
- package/dist/shaders/passes/cull.js +7 -3
- package/dist/shaders/passes/ground.d.ts.map +1 -1
- package/dist/shaders/passes/ground.js +32 -55
- package/dist/shaders/passes/mirror.d.ts +18 -12
- package/dist/shaders/passes/mirror.d.ts.map +1 -1
- package/dist/shaders/passes/mirror.js +59 -13
- package/dist/shaders/passes/outline.d.ts.map +1 -1
- package/dist/shaders/passes/outline.js +71 -14
- package/dist/shaders/passes/particles.d.ts +1 -1
- package/dist/shaders/passes/particles.js +1 -1
- package/dist/shaders/scene-light-api.d.ts +18 -10
- package/dist/shaders/scene-light-api.d.ts.map +1 -1
- package/dist/shaders/scene-light-api.js +58 -46
- package/dist/shadow-cascades.d.ts +47 -20
- package/dist/shadow-cascades.d.ts.map +1 -1
- package/dist/shadow-cascades.js +36 -13
- package/dist/unity/character.d.ts +59 -0
- package/dist/unity/character.d.ts.map +1 -0
- package/dist/unity/character.js +120 -0
- package/dist/unity/globals.d.ts +93 -0
- package/dist/unity/globals.d.ts.map +1 -0
- package/dist/unity/globals.js +340 -0
- package/dist/unity/host.d.ts +152 -0
- package/dist/unity/host.d.ts.map +1 -0
- package/dist/unity/host.js +529 -0
- package/dist/unity/looks.d.ts +93 -0
- package/dist/unity/looks.d.ts.map +1 -0
- package/dist/unity/looks.js +312 -0
- package/dist/unity/skin.d.ts +9 -0
- package/dist/unity/skin.d.ts.map +1 -0
- package/dist/unity/skin.js +120 -0
- package/dist/unity/stage.d.ts +155 -0
- package/dist/unity/stage.d.ts.map +1 -0
- package/dist/unity/stage.js +410 -0
- package/dist/unity/state.d.ts +22 -0
- package/dist/unity/state.d.ts.map +1 -0
- package/dist/unity/state.js +170 -0
- package/package.json +1 -1
- package/src/engine.ts +1535 -964
- package/src/graph/compile.ts +3 -1
- package/src/graph/registry.ts +142 -0
- package/src/graph/schema.ts +9 -0
- package/src/graph/slots.ts +15 -18
- package/src/index.ts +12 -0
- package/src/model.ts +18 -1
- package/src/outline-normals.ts +110 -0
- package/src/pmx-loader.ts +8 -2
- package/src/shaders/lights.ts +16 -3
- package/src/shaders/materials/common.ts +65 -57
- package/src/shaders/materials/nodes.ts +210 -0
- package/src/shaders/passes/bloom.ts +59 -87
- package/src/shaders/passes/composite.ts +122 -64
- package/src/shaders/passes/cull.ts +7 -3
- package/src/shaders/passes/ground.ts +32 -55
- package/src/shaders/passes/mirror.ts +60 -13
- package/src/shaders/passes/outline.ts +71 -14
- package/src/shaders/passes/particles.ts +1 -1
- package/src/shaders/scene-light-api.ts +59 -46
- package/src/shadow-cascades.ts +61 -32
- package/src/unity/character.ts +169 -0
- package/src/unity/globals.ts +443 -0
- package/src/unity/host.ts +630 -0
- package/src/unity/looks.ts +391 -0
- package/src/unity/skin.ts +131 -0
- package/src/unity/stage.ts +604 -0
- package/src/unity/state.ts +172 -0
- package/src/shaders/agx-lut.ts +0 -30
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
1
|
+
// Bloom — Aether Gazer's own chain (AGSimPostFX.Bloom, URP-style): a soft-knee
|
|
2
|
+
// prefilter to half size, a separable Gaussian down to a few pixels, then back
|
|
3
|
+
// up level by level, each blend leaning toward the coarser level by `scatter`.
|
|
4
|
+
// The composite adds the result × tint × intensity before the view transform.
|
|
5
5
|
const FULLSCREEN_VS = /* wgsl */ `
|
|
6
6
|
@vertex fn vs(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
|
|
7
7
|
let x = f32((vi & 1u) << 2u) - 1.0;
|
|
@@ -9,112 +9,83 @@ const FULLSCREEN_VS = /* wgsl */ `
|
|
|
9
9
|
return vec4f(x, y, 0.0, 1.0);
|
|
10
10
|
}
|
|
11
11
|
`;
|
|
12
|
-
// Full-res HDR → half-res.
|
|
13
|
-
|
|
12
|
+
// Full-res HDR → half-res. The game takes ONE bilinear sample at the half-res
|
|
13
|
+
// pixel's centre, which is the 2×2 box of the block under it; here the four
|
|
14
|
+
// texels are loaded so each can be un-premultiplied (the scene target is
|
|
15
|
+
// premultiplied; its alpha lives in the aux target's .g) and gated by the bloom
|
|
16
|
+
// mask (.r) first.
|
|
17
|
+
export const BLOOM_PREFILTER_SHADER_WGSL = `${FULLSCREEN_VS}
|
|
14
18
|
@group(0) @binding(0) var hdrTex: texture_2d<f32>;
|
|
15
|
-
@group(0) @binding(1) var<uniform>
|
|
19
|
+
@group(0) @binding(1) var<uniform> params: vec4<f32>; // threshold, knee, clamp, scatter
|
|
16
20
|
@group(0) @binding(2) var maskTex: texture_2d<f32>;
|
|
17
21
|
|
|
18
|
-
fn
|
|
19
|
-
return dot(max(c, vec3f(0.0)), vec3f(0.2126, 0.7152, 0.0722));
|
|
20
|
-
}
|
|
21
|
-
fn fetch(c: vec2<i32>, clampV: f32) -> vec3f {
|
|
22
|
+
fn fetch(c: vec2<i32>) -> vec3f {
|
|
22
23
|
let d = vec2<i32>(textureDimensions(hdrTex));
|
|
23
24
|
let cc = clamp(c, vec2<i32>(0), d - vec2<i32>(1));
|
|
24
|
-
let s = textureLoad(hdrTex, cc, 0);
|
|
25
|
-
// hdrTex is rg11b10ufloat (no alpha channel). Alpha lives in maskTex.g, written
|
|
26
|
-
// alongside the bloom mask in .r by the scene pass. Scene uses src-alpha blend
|
|
27
|
-
// with clear alpha 0 → hdr rgb is premultiplied; divide by the aux alpha to
|
|
28
|
-
// recover straight color before the Karis luminance weighting.
|
|
29
25
|
let aux = textureLoad(maskTex, cc, 0);
|
|
30
|
-
|
|
31
|
-
let rgb = max(s.rgb / max(aux.g, 1e-6), vec3f(0.0));
|
|
32
|
-
let masked = rgb * mask;
|
|
33
|
-
// Blender clamps each tap BEFORE Karis average (eevee_bloom: color = min(clampIntensity, color)).
|
|
34
|
-
return select(masked, min(masked, vec3f(clampV)), clampV > 0.0);
|
|
26
|
+
return max(textureLoad(hdrTex, cc, 0).rgb / max(aux.g, 1e-6), vec3f(0.0)) * aux.r;
|
|
35
27
|
}
|
|
36
28
|
|
|
37
29
|
@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f {
|
|
38
|
-
let
|
|
39
|
-
let
|
|
40
|
-
let
|
|
41
|
-
|
|
42
|
-
let
|
|
43
|
-
let
|
|
44
|
-
let
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
let wb = 1.0 / (1.0 + luminance(b));
|
|
48
|
-
let wc = 1.0 / (1.0 + luminance(c));
|
|
49
|
-
let wd = 1.0 / (1.0 + luminance(d));
|
|
50
|
-
let avg = (a * wa + b * wb + c * wc + d * wd) / max(wa + wb + wc + wd, 1e-6);
|
|
51
|
-
// EEVEE quadratic threshold (brightness = max-channel, then soft-knee curve).
|
|
52
|
-
let bright = max(avg.r, max(avg.g, avg.b));
|
|
53
|
-
let soft = clamp(bright - prefilter.x + prefilter.y, 0.0, 2.0 * prefilter.y);
|
|
54
|
-
let q = (soft * soft) / (4.0 * max(prefilter.y, 1e-4) + 1e-6);
|
|
55
|
-
let contrib = max(q, bright - prefilter.x) / max(bright, 1e-4);
|
|
56
|
-
return vec4f(max(avg * contrib, vec3f(0.0)), 1.0);
|
|
30
|
+
let base = vec2<i32>(p.xy - vec2f(0.5)) * 2;
|
|
31
|
+
let avg = (fetch(base) + fetch(base + vec2<i32>(1, 0)) + fetch(base + vec2<i32>(0, 1)) + fetch(base + vec2<i32>(1, 1))) * 0.25;
|
|
32
|
+
let c = min(avg, vec3f(params.z));
|
|
33
|
+
// URP's quadratic soft knee on the brightest channel.
|
|
34
|
+
let bright = max(c.r, max(c.g, c.b));
|
|
35
|
+
let knee = params.y;
|
|
36
|
+
let soft = clamp(bright - params.x + knee, 0.0, 2.0 * knee);
|
|
37
|
+
let curve = soft * soft / (4.0 * knee + 1e-4);
|
|
38
|
+
return vec4f(c * (max(curve, bright - params.x) / max(bright, 1e-4)), 1.0);
|
|
57
39
|
}
|
|
58
40
|
`;
|
|
59
|
-
//
|
|
60
|
-
|
|
41
|
+
// Horizontal 9-tap Gaussian (σ ≈ 2 at the destination's scale), its taps two
|
|
42
|
+
// SOURCE texels apart: the destination is half the source, so this both halves
|
|
43
|
+
// and blurs. Each tap lands on a texel boundary, so bilinear averages pairs.
|
|
44
|
+
export const BLOOM_BLUR_H_SHADER_WGSL = `${FULLSCREEN_VS}
|
|
61
45
|
@group(0) @binding(0) var srcTex: texture_2d<f32>;
|
|
62
46
|
@group(0) @binding(1) var srcSamp: sampler;
|
|
63
47
|
|
|
64
|
-
fn
|
|
65
|
-
|
|
48
|
+
@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f {
|
|
49
|
+
let srcDims = vec2f(textureDimensions(srcTex));
|
|
50
|
+
let dstDims = max(floor(srcDims * 0.5), vec2f(1.0));
|
|
51
|
+
let uv = p.xy / dstDims;
|
|
52
|
+
let dx = vec2f(2.0 / srcDims.x, 0.0);
|
|
53
|
+
var w = array<f32, 5>(0.22702703, 0.19459459, 0.12162162, 0.05405405, 0.01621622);
|
|
54
|
+
var o = textureSampleLevel(srcTex, srcSamp, uv, 0.0).rgb * w[0];
|
|
55
|
+
for (var i = 1; i < 5; i++) {
|
|
56
|
+
let off = dx * f32(i);
|
|
57
|
+
o += (textureSampleLevel(srcTex, srcSamp, uv - off, 0.0).rgb + textureSampleLevel(srcTex, srcSamp, uv + off, 0.0).rgb) * w[i];
|
|
58
|
+
}
|
|
59
|
+
return vec4f(o, 1.0);
|
|
66
60
|
}
|
|
61
|
+
`;
|
|
62
|
+
// Vertical pass at the same size: the same 9-tap Gaussian as five bilinear taps.
|
|
63
|
+
export const BLOOM_BLUR_V_SHADER_WGSL = `${FULLSCREEN_VS}
|
|
64
|
+
@group(0) @binding(0) var srcTex: texture_2d<f32>;
|
|
65
|
+
@group(0) @binding(1) var srcSamp: sampler;
|
|
67
66
|
|
|
68
67
|
@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f {
|
|
69
|
-
let
|
|
70
|
-
let
|
|
71
|
-
|
|
72
|
-
let
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
let C = samp(uv, t * vec2f( 2.0, -2.0));
|
|
77
|
-
let D = samp(uv, t * vec2f(-1.0, -1.0));
|
|
78
|
-
let E = samp(uv, t * vec2f( 1.0, -1.0));
|
|
79
|
-
let F = samp(uv, t * vec2f(-2.0, 0.0));
|
|
80
|
-
let G = samp(uv, t * vec2f( 0.0, 0.0));
|
|
81
|
-
let H = samp(uv, t * vec2f( 2.0, 0.0));
|
|
82
|
-
let I = samp(uv, t * vec2f(-1.0, 1.0));
|
|
83
|
-
let J = samp(uv, t * vec2f( 1.0, 1.0));
|
|
84
|
-
let K = samp(uv, t * vec2f(-2.0, 2.0));
|
|
85
|
-
let L = samp(uv, t * vec2f( 0.0, 2.0));
|
|
86
|
-
let M = samp(uv, t * vec2f( 2.0, 2.0));
|
|
87
|
-
var o = (D + E + I + J) * (0.5 / 4.0);
|
|
88
|
-
o = o + (A + B + G + F) * (0.125 / 4.0);
|
|
89
|
-
o = o + (B + C + H + G) * (0.125 / 4.0);
|
|
90
|
-
o = o + (F + G + L + K) * (0.125 / 4.0);
|
|
91
|
-
o = o + (G + H + M + L) * (0.125 / 4.0);
|
|
68
|
+
let dims = vec2f(textureDimensions(srcTex));
|
|
69
|
+
let uv = p.xy / dims;
|
|
70
|
+
let near = vec2f(0.0, 1.3846154 / dims.y);
|
|
71
|
+
let far = vec2f(0.0, 3.2307692 / dims.y);
|
|
72
|
+
var o = textureSampleLevel(srcTex, srcSamp, uv, 0.0).rgb * 0.22702703;
|
|
73
|
+
o += (textureSampleLevel(srcTex, srcSamp, uv - near, 0.0).rgb + textureSampleLevel(srcTex, srcSamp, uv + near, 0.0).rgb) * 0.31621623;
|
|
74
|
+
o += (textureSampleLevel(srcTex, srcSamp, uv - far, 0.0).rgb + textureSampleLevel(srcTex, srcSamp, uv + far, 0.0).rgb) * 0.07027027;
|
|
92
75
|
return vec4f(o, 1.0);
|
|
93
76
|
}
|
|
94
77
|
`;
|
|
95
|
-
//
|
|
78
|
+
// One level up: this level's blur leaned toward the coarser one by scatter.
|
|
96
79
|
export const BLOOM_UPSAMPLE_SHADER_WGSL = `${FULLSCREEN_VS}
|
|
97
|
-
@group(0) @binding(0) var
|
|
98
|
-
@group(0) @binding(1) var
|
|
80
|
+
@group(0) @binding(0) var highTex: texture_2d<f32>; // down[i]
|
|
81
|
+
@group(0) @binding(1) var lowTex: texture_2d<f32>; // coarser: down[i+1] at the top, up[i+1] after
|
|
99
82
|
@group(0) @binding(2) var srcSamp: sampler;
|
|
100
|
-
@group(0) @binding(3) var<uniform>
|
|
83
|
+
@group(0) @binding(3) var<uniform> params: vec4<f32>; // threshold, knee, clamp, scatter
|
|
101
84
|
|
|
102
85
|
@fragment fn fs(@builtin(position) p: vec4f) -> @location(0) vec4f {
|
|
103
|
-
let
|
|
104
|
-
let
|
|
105
|
-
let
|
|
106
|
-
|
|
107
|
-
var o = textureSampleLevel(srcTex, srcSamp, uv + t * vec2f(-1.0, -1.0), 0.0).rgb * 1.0;
|
|
108
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 0.0, -1.0), 0.0).rgb * 2.0;
|
|
109
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 1.0, -1.0), 0.0).rgb * 1.0;
|
|
110
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f(-1.0, 0.0), 0.0).rgb * 2.0;
|
|
111
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 0.0, 0.0), 0.0).rgb * 4.0;
|
|
112
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 1.0, 0.0), 0.0).rgb * 2.0;
|
|
113
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f(-1.0, 1.0), 0.0).rgb * 1.0;
|
|
114
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 0.0, 1.0), 0.0).rgb * 2.0;
|
|
115
|
-
o = o + textureSampleLevel(srcTex, srcSamp, uv + t * vec2f( 1.0, 1.0), 0.0).rgb * 1.0;
|
|
116
|
-
o = o * (1.0 / 16.0);
|
|
117
|
-
let base = textureSampleLevel(baseTex, srcSamp, uv, 0.0).rgb;
|
|
118
|
-
return vec4f(o + base, 1.0);
|
|
86
|
+
let uv = p.xy / vec2f(textureDimensions(highTex));
|
|
87
|
+
let high = textureSampleLevel(highTex, srcSamp, uv, 0.0).rgb;
|
|
88
|
+
let low = textureSampleLevel(lowTex, srcSamp, uv, 0.0).rgb;
|
|
89
|
+
return vec4f(mix(high, low, params.w), 1.0);
|
|
119
90
|
}
|
|
120
91
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composite.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/composite.ts"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAgC+B;AAC/B;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,EAAE,CAIhG;AAID,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAEzG,KAAK,qBAAqB,GAAG;IAC3B,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAA;IACZ,kFAAkF;IAClF,UAAU,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,aAAa,EAAE,OAAO,CAAA;IACtB,oEAAoE;IACpE,aAAa,EAAE,OAAO,CAAA;IACtB;6DACyD;IACzD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,gFAAgF;IAChF,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAA;IAChB;mFAC+E;IAC/E,GAAG,CAAC,EAAE,OAAO,CAAA;IACb;;;yEAGqE;IACrE,UAAU,EAAE,MAAM,CAAA;IAClB;;8EAE0E;IAC1E,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;
|
|
1
|
+
{"version":3,"file":"composite.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/composite.ts"],"names":[],"mappings":"AA8BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAgC+B;AAC/B;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,EAAE,CAIhG;AAID,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAEzG,KAAK,qBAAqB,GAAG;IAC3B,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAA;IACZ,kFAAkF;IAClF,UAAU,EAAE,MAAM,CAAA;IAClB,4DAA4D;IAC5D,aAAa,EAAE,OAAO,CAAA;IACtB,oEAAoE;IACpE,aAAa,EAAE,OAAO,CAAA;IACtB;6DACyD;IACzD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,gFAAgF;IAChF,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAA;IAChB;mFAC+E;IAC/E,GAAG,CAAC,EAAE,OAAO,CAAA;IACb;;;yEAGqE;IACrE,UAAU,EAAE,MAAM,CAAA;IAClB;;8EAE0E;IAC1E,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CACjB,CAAA;AAwRD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,ivfAuI5B,CAAA;AA8MD,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,qBAAqB,GAAG,IAAI,GAAG,MAAM,CAclF;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CA2GtE;AAED,iFAAiF;AACjF,eAAO,MAAM,qBAAqB,QAA6B,CAAA"}
|
|
@@ -12,7 +12,7 @@ import { castDistanceApi, CAST_FIELD_DIV } from "./cast-distance";
|
|
|
12
12
|
import { lyricsApi, lyricsTextApi } from "../lyrics-api";
|
|
13
13
|
import { midiApi } from "../midi-api";
|
|
14
14
|
import { gridReadApi } from "./grid";
|
|
15
|
-
// Composite: HDR scene + bloom
|
|
15
|
+
// Composite: HDR scene + bloom → view transform (soft / neutral / aces / none) → gamma → swapchain.
|
|
16
16
|
// Bloom tint/intensity applied at combine (EEVEE treats them as combine-stage params, not prefilter).
|
|
17
17
|
//
|
|
18
18
|
// The shader is a TEMPLATE: buildCompositeShader() emits either the base pass or
|
|
@@ -104,14 +104,6 @@ override APPLY_GAMMA: bool = true;
|
|
|
104
104
|
// re-premultiply the tonemapped color for output so the premultiplied canvas
|
|
105
105
|
// alphaMode composites the WebGPU surface over the page background correctly.
|
|
106
106
|
@group(0) @binding(4) var maskTex: texture_2d<f32>;
|
|
107
|
-
// Filmic tone curve baked to a WIDTH×1 r16float LUT (bakeFilmicLut on the CPU side).
|
|
108
|
-
// Domain: log2(linear) mapped to [0,13]. Replaces the old array<f32,14> that was indexed
|
|
109
|
-
// by a runtime u32 (a Metal-backend smell — dynamic local-array indexing lowers to a
|
|
110
|
-
// per-invocation copy/switch) and interpolated piecewise-linearly (C0, so segment slope
|
|
111
|
-
// discontinuities showed as Mach bands in smooth skin/shadow gradients). The LUT is a
|
|
112
|
-
// monotone-cubic (Fritsch–Carlson) fit through the same 14 anchors — same values, C1
|
|
113
|
-
// continuity kills the banding — sampled with hardware linear filtering.
|
|
114
|
-
@group(0) @binding(5) var filmicLut: texture_2d<f32>;
|
|
115
107
|
// viewU[0] = (exposure, invGamma, grain amount, grain seed); viewU[1] = (tint.rgb, intensity)
|
|
116
108
|
// viewU[2] = (background.rgb, mode) — display-space sRGB, composited UNDER the
|
|
117
109
|
// scene post-tonemap. BASE-layer mode: 0 transparent (DOM shows),
|
|
@@ -143,9 +135,6 @@ override APPLY_GAMMA: bool = true;
|
|
|
143
135
|
// track the camera radius. Cleared depth (1.0) inverts to the far
|
|
144
136
|
// plane, so empty sky reads as maximally defocused background.
|
|
145
137
|
@group(0) @binding(9) var<uniform> dofU: array<vec4<f32>, 3>;
|
|
146
|
-
// Blender's AgX, as the 57³ lookup it ships as rather than a reconstruction of
|
|
147
|
-
// it. Sampled in the log-encoded E-Gamut space the cube expects — see agxTransform.
|
|
148
|
-
@group(0) @binding(10) var agxLut: texture_3d<f32>;
|
|
149
138
|
// The scene's own colour grade, as a cube — see _rzStageGrade. sRGB-encoded
|
|
150
139
|
// storage, so what a sample returns is already linear.
|
|
151
140
|
@group(0) @binding(12) var stageGradeLut: texture_3d<f32>;
|
|
@@ -181,8 +170,6 @@ override APPLY_GAMMA: bool = true;
|
|
|
181
170
|
fn rzFieldMerge(top: vec4f, bot: vec4f) -> vec4f {
|
|
182
171
|
return vec4f(top.rgb + bot.rgb * (1.0 - top.a), top.a + bot.a * (1.0 - top.a));
|
|
183
172
|
}
|
|
184
|
-
// Must match FILMIC_LUT_WIDTH in engine.ts (bakeFilmicLut).
|
|
185
|
-
const FILMIC_LUT_W: f32 = 256.0;
|
|
186
173
|
|
|
187
174
|
fn linearDepth(coord: vec2<i32>) -> f32 {
|
|
188
175
|
let z = textureLoad(depthTex, coord, 0);
|
|
@@ -215,67 +202,138 @@ fn sceneSample(coord: vec2<i32>, fullSzI: vec2<i32>, fullSz: vec2f) -> vec4f {
|
|
|
215
202
|
return vec4f((sHdr + sBloom) * sAlpha, sAlpha);
|
|
216
203
|
}
|
|
217
204
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
// NOTE: version-pinned to Blender 3.6 — 4.x defaults to AgX, not Filmic.
|
|
222
|
-
let t = clamp(log2(max(x, 1e-10)) + 10.0, 0.0, 13.0);
|
|
223
|
-
// Map t∈[0,13] to the texel-center of baked sample j = t·(W-1)/13.
|
|
224
|
-
let u = (t * (FILMIC_LUT_W - 1.0) / 13.0 + 0.5) / FILMIC_LUT_W;
|
|
225
|
-
// textureSampleLevel (explicit LOD, no derivatives) is legal in non-uniform flow.
|
|
226
|
-
return textureSampleLevel(filmicLut, bloomSamp, vec2f(u, 0.5), 0.0).r;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/** The sRGB display encoding — Blender's "Standard" view transform, which is what
|
|
230
|
-
* NPR work uses: no film curve at all, so the colours a graph computes are the
|
|
231
|
-
* colours that land. Not a 2.2 power law; sRGB has a linear toe. */
|
|
205
|
+
/** The sRGB display encoding — "none": no tone curve at all, so the colours a
|
|
206
|
+
* graph computes are the colours that land. Not a 2.2 power law; sRGB has a
|
|
207
|
+
* linear toe. Every transform below ends in it. */
|
|
232
208
|
fn srgb_encode(x: f32) -> f32 {
|
|
233
209
|
let c = max(x, 0.0);
|
|
234
210
|
return select(1.055 * pow(c, 1.0 / 2.4) - 0.055, c * 12.92, c <= 0.0031308);
|
|
235
211
|
}
|
|
236
212
|
|
|
237
213
|
/**
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
*
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
214
|
+
* "soft" — Aether Gazer's own curve, from its final pass
|
|
215
|
+
* (Hidden/SimPipeline/Final, decompiled): (1 - e^(-exposure x))^contrast, at the
|
|
216
|
+
* 2.5 and 1.4 its scenes set, into a linear target the swapchain then encodes.
|
|
217
|
+
* The game's ACES branch is off in every scene measured.
|
|
218
|
+
*/
|
|
219
|
+
fn softTransform(c: vec3f) -> vec3f {
|
|
220
|
+
let t = pow(max(vec3f(1.0) - exp(-2.5 * max(c, vec3f(0.0))), vec3f(0.0)), vec3f(1.4));
|
|
221
|
+
return vec3f(srgb_encode(t.r), srgb_encode(t.g), srgb_encode(t.b));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fn _rzSrgbEncode3(c: vec3f) -> vec3f {
|
|
225
|
+
return vec3f(srgb_encode(c.r), srgb_encode(c.g), srgb_encode(c.b));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* "neutral" — Unity URP's Neutral tonemapper as the Khronos PBR Neutral
|
|
230
|
+
* operator, to the published formula: below the start of compression colour
|
|
231
|
+
* passes through (less a small toe offset); above it the peak rolls off toward
|
|
232
|
+
* 1 and desaturates in proportion to how much it lost.
|
|
251
233
|
*/
|
|
252
|
-
fn
|
|
253
|
-
let
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
);
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
234
|
+
fn neutralTransform(color: vec3f) -> vec3f {
|
|
235
|
+
let startCompression = 0.8 - 0.04;
|
|
236
|
+
let desaturation = 0.15;
|
|
237
|
+
var c = max(color, vec3f(0.0));
|
|
238
|
+
let x = min(c.r, min(c.g, c.b));
|
|
239
|
+
let offset = select(0.04, x - 6.25 * x * x, x < 0.08);
|
|
240
|
+
c = c - vec3f(offset);
|
|
241
|
+
let peak = max(c.r, max(c.g, c.b));
|
|
242
|
+
if (peak >= startCompression) {
|
|
243
|
+
let d = 1.0 - startCompression;
|
|
244
|
+
let newPeak = 1.0 - d * d / (peak + d - startCompression);
|
|
245
|
+
c = c * (newPeak / peak);
|
|
246
|
+
let g = 1.0 - 1.0 / (desaturation * (peak - newPeak) + 1.0);
|
|
247
|
+
c = mix(c, vec3f(newPeak), g);
|
|
248
|
+
}
|
|
249
|
+
return _rzSrgbEncode3(clamp(c, vec3f(0.0), vec3f(1.0)));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// "aces" — Unity URP's ACES: unity_to_ACES then AcesTonemap, both from the Core
|
|
253
|
+
// RP's Color.hlsl (the branch URP compiles, not full ACES): the RRT's glow and
|
|
254
|
+
// red modifier and global desaturation, the luminance fit of RRT +
|
|
255
|
+
// ODT.Academy.RGBmonitor_100nits_dim, the dim-surround gamma, the ODT
|
|
256
|
+
// desaturation, then AP1 → XYZ → D60→D65 → Rec.709, saturated as URP's
|
|
257
|
+
// ApplyTonemap does. HLSL's row-major matrices are written as rows here.
|
|
258
|
+
fn _rzRows(r0: vec3f, r1: vec3f, r2: vec3f, v: vec3f) -> vec3f {
|
|
259
|
+
return vec3f(dot(r0, v), dot(r1, v), dot(r2, v));
|
|
260
|
+
}
|
|
261
|
+
fn _rzAp1ToXyz(v: vec3f) -> vec3f {
|
|
262
|
+
return _rzRows(vec3f(0.6624541811, 0.1340042065, 0.1561876870),
|
|
263
|
+
vec3f(0.2722287168, 0.6740817658, 0.0536895174),
|
|
264
|
+
vec3f(-0.0055746495, 0.0040607335, 1.0103391003), v);
|
|
265
|
+
}
|
|
266
|
+
fn _rzAp1Luma(v: vec3f) -> f32 {
|
|
267
|
+
return dot(v, vec3f(0.272229, 0.674082, 0.0536895));
|
|
268
|
+
}
|
|
269
|
+
fn acesTransform(color: vec3f) -> vec3f {
|
|
270
|
+
// unity_to_ACES: linear sRGB → AP0.
|
|
271
|
+
var aces = _rzRows(vec3f(0.4397010, 0.3829780, 0.1773350),
|
|
272
|
+
vec3f(0.0897923, 0.8134230, 0.0967616),
|
|
273
|
+
vec3f(0.0175440, 0.1115440, 0.8707040), max(color, vec3f(0.0)));
|
|
274
|
+
// Glow module.
|
|
275
|
+
let mi = min(aces.r, min(aces.g, aces.b));
|
|
276
|
+
let ma = max(aces.r, max(aces.g, aces.b));
|
|
277
|
+
let saturation = (max(ma, 1e-4) - max(mi, 1e-4)) / max(ma, 1e-2);
|
|
278
|
+
let k = max(aces.b * (aces.b - aces.g) + aces.g * (aces.g - aces.r) + aces.r * (aces.r - aces.b), 0.0);
|
|
279
|
+
let ycIn = (aces.b + aces.g + aces.r + 1.75 * sqrt(k)) / 3.0;
|
|
280
|
+
let sx = (saturation - 0.4) / 0.2;
|
|
281
|
+
let st = max(1.0 - abs(sx / 2.0), 0.0);
|
|
282
|
+
let s = (1.0 + select(-1.0, 1.0, sx >= 0.0) * (1.0 - st * st)) / 2.0;
|
|
283
|
+
let glowGain = 0.05 * s;
|
|
284
|
+
let glowMid = 0.08;
|
|
285
|
+
var glow = glowGain * (glowMid / max(ycIn, 1e-6) - 0.5);
|
|
286
|
+
if (ycIn <= 2.0 / 3.0 * glowMid) { glow = glowGain; }
|
|
287
|
+
if (ycIn >= 2.0 * glowMid) { glow = 0.0; }
|
|
288
|
+
aces = aces * (1.0 + glow);
|
|
289
|
+
// Red modifier.
|
|
290
|
+
var hue = 0.0;
|
|
291
|
+
if (!(aces.r == aces.g && aces.g == aces.b)) {
|
|
292
|
+
hue = degrees(atan2(sqrt(3.0) * (aces.g - aces.b), 2.0 * aces.r - aces.g - aces.b));
|
|
293
|
+
}
|
|
294
|
+
if (hue < 0.0) { hue = hue + 360.0; }
|
|
295
|
+
var centered = hue;
|
|
296
|
+
if (centered < -180.0) { centered = centered + 360.0; } else if (centered > 180.0) { centered = centered - 360.0; }
|
|
297
|
+
var hueWeight = smoothstep(0.0, 1.0, 1.0 - abs(2.0 * centered / 135.0));
|
|
298
|
+
hueWeight = hueWeight * hueWeight;
|
|
299
|
+
aces.r = aces.r + hueWeight * saturation * (0.03 - aces.r) * (1.0 - 0.82);
|
|
300
|
+
// AP0 → AP1 (ACEScg), then the RRT's global desaturation.
|
|
301
|
+
var cg = max(_rzRows(vec3f(1.4514393161, -0.2365107469, -0.2149285693),
|
|
302
|
+
vec3f(-0.0765537734, 1.1762296998, -0.0996759264),
|
|
303
|
+
vec3f(0.0083161484, -0.0060324498, 0.9977163014), aces), vec3f(0.0));
|
|
304
|
+
cg = mix(vec3f(_rzAp1Luma(cg)), cg, vec3f(0.96));
|
|
305
|
+
// The luminance fit of RRT + ODT.Academy.RGBmonitor_100nits_dim.
|
|
306
|
+
let post = (cg * (2.785085 * cg + 0.107772)) / (cg * (2.936045 * cg + 0.887122) + 0.806889);
|
|
307
|
+
// Dark → dim surround: Y^0.9811, in xyY.
|
|
308
|
+
var xyz = _rzAp1ToXyz(post);
|
|
309
|
+
let div = max(xyz.x + xyz.y + xyz.z, 1e-4);
|
|
310
|
+
let xyY = vec3f(xyz.x / div, xyz.y / div, pow(clamp(xyz.y, 0.0, 65504.0), 0.9811));
|
|
311
|
+
let m = xyY.z / max(xyY.y, 1e-4);
|
|
312
|
+
xyz = vec3f(xyY.x * m, xyY.z, (1.0 - xyY.x - xyY.y) * m);
|
|
313
|
+
var lin = _rzRows(vec3f(1.6410233797, -0.3248032942, -0.2364246952),
|
|
314
|
+
vec3f(-0.6636628587, 1.6153315917, 0.0167563477),
|
|
315
|
+
vec3f(0.0117218943, -0.0082844420, 0.9883948585), xyz);
|
|
316
|
+
// The ODT's desaturation, then to the display's primaries.
|
|
317
|
+
lin = mix(vec3f(_rzAp1Luma(lin)), lin, vec3f(0.93));
|
|
318
|
+
xyz = _rzRows(vec3f(0.98722400, -0.00611327, 0.0159533),
|
|
319
|
+
vec3f(-0.00759836, 1.00186000, 0.0053302),
|
|
320
|
+
vec3f(0.00307257, -0.00509595, 1.0816800), _rzAp1ToXyz(lin));
|
|
321
|
+
let rec709 = _rzRows(vec3f(3.2409699419, -1.5373831776, -0.4986107603),
|
|
322
|
+
vec3f(-0.9692436363, 1.8759675015, 0.0415550574),
|
|
323
|
+
vec3f(0.0556300797, -0.2039769589, 1.0569715142), xyz);
|
|
324
|
+
return _rzSrgbEncode3(clamp(rec709, vec3f(0.0), vec3f(1.0)));
|
|
269
325
|
}
|
|
270
326
|
|
|
271
|
-
/** Which display transform, chosen per frame at viewU[6].y (0
|
|
272
|
-
* A uniform branch rather than a pipeline variant: switching
|
|
273
|
-
*
|
|
327
|
+
/** Which display transform, chosen per frame at viewU[6].y (0 soft, 1 none,
|
|
328
|
+
* 2 neutral, 3 aces). A uniform branch rather than a pipeline variant: switching
|
|
329
|
+
* is rare, and every arm is cheap enough that specialising the pipeline would
|
|
330
|
+
* buy nothing. */
|
|
274
331
|
fn viewTransform(c: vec3f) -> vec3f {
|
|
275
332
|
let mode = viewU[6].y;
|
|
276
|
-
if (mode >
|
|
277
|
-
if (mode >
|
|
278
|
-
|
|
333
|
+
if (mode > 2.5) { return acesTransform(c); }
|
|
334
|
+
if (mode > 1.5) { return neutralTransform(c); }
|
|
335
|
+
if (mode > 0.5) { return _rzSrgbEncode3(c); }
|
|
336
|
+
return softTransform(c);
|
|
279
337
|
}
|
|
280
338
|
|
|
281
339
|
fn _rzSrgbDecode(x: f32) -> f32 {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CULL_COMPUTE_WGSL = "\nstruct DrawMeta {\n lo: vec3f, // model-space AABB min \u2014 read only for RIGID models\n model: u32, // index into models[]\n hi: vec3f, // model-space AABB max\n flags: u32, // bit0 = casts shadow\n};\n\nstruct ModelRec {\n xform: mat4x4f, // model space -> world; meaningful only when RIGID\n sphere: vec4f, // world-space centre.xyz + radius.w; meaningful when SKINNED\n flags: u32, // bit0 = visible, bit1 = rigid\n pad0: u32,\n pad1: u32,\n pad2: u32,\n};\n\n// Camera planes at 0..5, light planes at 6..11, mirror planes at 12..17.\n// counts.x = draw count, counts.y = 0 to pass everything (setCullEnabled).\nstruct Frusta {\n planes: array<vec4f, 18>,\n counts: vec4u,\n};\n\n@group(0) @binding(0) var<storage, read> metas: array<DrawMeta>;\n@group(0) @binding(1) var<storage, read> models: array<ModelRec>;\n@group(0) @binding(2) var<uniform> frusta: Frusta;\n@group(0) @binding(3) var<storage, read_write> cameraArgs: array<u32>;\n@group(0) @binding(4) var<storage, read_write> shadowArgs: array<u32>;\n// 1 = the user hid this material, or a material morph drove its alpha to zero.\n// Here rather than in the encode loop because a render bundle bakes its draw\n// list: a face VMD rewrites the morph-hidden set every frame, so a bundle that\n// invalidated on it would re-record every frame and cost more than it saves.\n@group(0) @binding(5) var<storage, read> hidden: array<u32>;\n@group(0) @binding(6) var<storage, read_write> mirrorArgs: array<u32>;\n\nconst DRAW_CASTS_SHADOW: u32 = 1u;\nconst MODEL_VISIBLE: u32 = 1u;\nconst MODEL_RIGID: u32 = 2u;\n\n// Signed distance of the AABB's most-positive corner: the centre distance plus\n// the extent projected onto |n|. Negative means every corner is behind the\n// plane, which is the only case that can reject.\nfn aabbVisible(base: u32, c: vec3f, e: vec3f) -> bool {\n for (var i = 0u; i < 6u; i = i + 1u) {\n let p = frusta.planes[base + i];\n if (dot(p.xyz, c) + p.w + dot(e, abs(p.xyz)) < 0.0) { return false; }\n }\n return true;\n}\n\nfn sphereVisible(base: u32, s: vec4f) -> bool {\n for (var i = 0u; i < 6u; i = i + 1u) {\n let p = frusta.planes[base + i];\n if (dot(p.xyz, s.xyz) + p.w + s.w < 0.0) { return false; }\n }\n return true;\n}\n\n@compute @workgroup_size(64)\nfn cs(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x;\n if (i >= frusta.counts.x) { return; }\n\n // Named dm, not meta: \"meta\" is a WGSL reserved word (reserved for future use,\n // so it parses nowhere). No backticks anywhere in this string either \u2014 it is a\n // template literal, and one would end the shader mid-comment.\n let dm = metas[i];\n let m = models[dm.model];\n let visible = (m.flags & MODEL_VISIBLE) != 0u;\n\n var inCamera = false;\n var inLight = false;\n var inMirror = false;\n if (visible) {\n if ((m.flags & MODEL_RIGID) != 0u) {\n let c = (dm.lo + dm.hi) * 0.5;\n let e = (dm.hi - dm.lo) * 0.5;\n let wc = (m.xform * vec4f(c, 1.0)).xyz;\n // Extent of the transformed box: the abs of the linear part applied to the\n // half-extent. Exact for the axis-aligned case, conservative for a rotation.\n let a = mat3x3f(abs(m.xform[0].xyz), abs(m.xform[1].xyz), abs(m.xform[2].xyz));\n let we = a * e;\n inCamera = aabbVisible(0u, wc, we);\n inLight = aabbVisible(6u, wc, we);\n inMirror = aabbVisible(12u, wc, we);\n } else {\n inCamera = sphereVisible(0u, m.sphere);\n inLight = sphereVisible(6u, m.sphere);\n inMirror = sphereVisible(12u, m.sphere);\n }\n }\n\n // Culling off (setCullEnabled) passes everything, so a scene can be A/B'd\n // against its own unculled self without rebuilding anything. The pass still\n // runs, so the diagnostics keep reporting. It bypasses the FRUSTUM only \u2014\n // a hidden material is not culled, it is switched off, and answering \"is this\n // gone because of culling?\" requires the two to stay separable.\n let off = frusta.counts.y == 0u;\n let shown = hidden[i] == 0u;\n let castsShadow = (dm.flags & DRAW_CASTS_SHADOW) != 0u;\n cameraArgs[i * 5u + 1u] = select(0u, 1u, (inCamera || off) && shown);\n shadowArgs[i * 5u + 1u] = select(0u, 1u, ((inLight && castsShadow) || off) && shown);\n mirrorArgs[i * 5u + 1u] = select(0u, 1u, (inMirror || off) && shown);\n}\n";
|
|
1
|
+
export declare const CULL_COMPUTE_WGSL = "\nstruct DrawMeta {\n lo: vec3f, // model-space AABB min \u2014 read only for RIGID models\n model: u32, // index into models[]\n hi: vec3f, // model-space AABB max\n flags: u32, // bit0 = casts shadow\n};\n\nstruct ModelRec {\n xform: mat4x4f, // model space -> world; meaningful only when RIGID\n sphere: vec4f, // world-space centre.xyz + radius.w; meaningful when SKINNED\n flags: u32, // bit0 = visible, bit1 = rigid\n pad0: u32,\n pad1: u32,\n pad2: u32,\n};\n\n// Camera planes at 0..5, light planes at 6..11, mirror planes at 12..17.\n// counts.x = draw count, counts.y = 0 to pass everything (setCullEnabled).\nstruct Frusta {\n planes: array<vec4f, 18>,\n counts: vec4u,\n};\n\n@group(0) @binding(0) var<storage, read> metas: array<DrawMeta>;\n@group(0) @binding(1) var<storage, read> models: array<ModelRec>;\n@group(0) @binding(2) var<uniform> frusta: Frusta;\n@group(0) @binding(3) var<storage, read_write> cameraArgs: array<u32>;\n@group(0) @binding(4) var<storage, read_write> shadowArgs: array<u32>;\n// 1 = the user hid this material, or a material morph drove its alpha to zero.\n// Here rather than in the encode loop because a render bundle bakes its draw\n// list: a face VMD rewrites the morph-hidden set every frame, so a bundle that\n// invalidated on it would re-record every frame and cost more than it saves.\n@group(0) @binding(5) var<storage, read> hidden: array<u32>;\n@group(0) @binding(6) var<storage, read_write> mirrorArgs: array<u32>;\n\nconst DRAW_CASTS_SHADOW: u32 = 1u;\nconst MODEL_VISIBLE: u32 = 1u;\nconst MODEL_RIGID: u32 = 2u;\n\n// Signed distance of the AABB's most-positive corner: the centre distance plus\n// the extent projected onto |n|. Negative means every corner is behind the\n// plane, which is the only case that can reject.\nfn aabbVisible(base: u32, c: vec3f, e: vec3f) -> bool {\n for (var i = 0u; i < 6u; i = i + 1u) {\n let p = frusta.planes[base + i];\n if (dot(p.xyz, c) + p.w + dot(e, abs(p.xyz)) < 0.0) { return false; }\n }\n return true;\n}\n\nfn sphereVisible(base: u32, s: vec4f) -> bool {\n for (var i = 0u; i < 6u; i = i + 1u) {\n let p = frusta.planes[base + i];\n if (dot(p.xyz, s.xyz) + p.w + s.w < 0.0) { return false; }\n }\n return true;\n}\n\n@compute @workgroup_size(64)\nfn cs(@builtin(global_invocation_id) gid: vec3<u32>) {\n let i = gid.x;\n if (i >= frusta.counts.x) { return; }\n\n // Named dm, not meta: \"meta\" is a WGSL reserved word (reserved for future use,\n // so it parses nowhere). No backticks anywhere in this string either \u2014 it is a\n // template literal, and one would end the shader mid-comment.\n let dm = metas[i];\n let m = models[dm.model];\n let visible = (m.flags & MODEL_VISIBLE) != 0u;\n\n var inCamera = false;\n var inLight = false;\n var inMirror = false;\n if (visible) {\n if ((m.flags & MODEL_RIGID) != 0u) {\n let c = (dm.lo + dm.hi) * 0.5;\n let e = (dm.hi - dm.lo) * 0.5;\n let wc = (m.xform * vec4f(c, 1.0)).xyz;\n // Extent of the transformed box: the abs of the linear part applied to the\n // half-extent. Exact for the axis-aligned case, conservative for a rotation.\n let a = mat3x3f(abs(m.xform[0].xyz), abs(m.xform[1].xyz), abs(m.xform[2].xyz));\n let we = a * e;\n inCamera = aabbVisible(0u, wc, we);\n inLight = aabbVisible(6u, wc, we);\n inMirror = aabbVisible(12u, wc, we);\n } else {\n inCamera = sphereVisible(0u, m.sphere);\n inLight = sphereVisible(6u, m.sphere);\n inMirror = sphereVisible(12u, m.sphere);\n }\n }\n\n // Culling off (setCullEnabled) passes everything, so a scene can be A/B'd\n // against its own unculled self without rebuilding anything. The pass still\n // runs, so the diagnostics keep reporting. It bypasses the FRUSTUM only \u2014\n // a hidden material is not culled, it is switched off, and answering \"is this\n // gone because of culling?\" requires the two to stay separable.\n let off = frusta.counts.y == 0u;\n // hidden: bit 0 switched off; bit 1 drawn by the native-material host\n // (unity/host.ts) rather than by its graph \u2014 no camera or mirror draw here,\n // and still its shadow, which the engine's pass casts for every material.\n let shown = (hidden[i] & 1u) == 0u;\n let graph = (hidden[i] & 2u) == 0u;\n let castsShadow = (dm.flags & DRAW_CASTS_SHADOW) != 0u;\n cameraArgs[i * 5u + 1u] = select(0u, 1u, (inCamera || off) && shown && graph);\n shadowArgs[i * 5u + 1u] = select(0u, 1u, ((inLight && castsShadow) || off) && shown);\n mirrorArgs[i * 5u + 1u] = select(0u, 1u, (inMirror || off) && shown && graph);\n}\n";
|
|
2
2
|
//# sourceMappingURL=cull.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cull.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/cull.ts"],"names":[],"mappings":"AAiCA,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"cull.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/cull.ts"],"names":[],"mappings":"AAiCA,eAAO,MAAM,iBAAiB,ojJA6G7B,CAAA"}
|
|
@@ -129,10 +129,14 @@ fn cs(@builtin(global_invocation_id) gid: vec3<u32>) {
|
|
|
129
129
|
// a hidden material is not culled, it is switched off, and answering "is this
|
|
130
130
|
// gone because of culling?" requires the two to stay separable.
|
|
131
131
|
let off = frusta.counts.y == 0u;
|
|
132
|
-
|
|
132
|
+
// hidden: bit 0 switched off; bit 1 drawn by the native-material host
|
|
133
|
+
// (unity/host.ts) rather than by its graph — no camera or mirror draw here,
|
|
134
|
+
// and still its shadow, which the engine's pass casts for every material.
|
|
135
|
+
let shown = (hidden[i] & 1u) == 0u;
|
|
136
|
+
let graph = (hidden[i] & 2u) == 0u;
|
|
133
137
|
let castsShadow = (dm.flags & DRAW_CASTS_SHADOW) != 0u;
|
|
134
|
-
cameraArgs[i * 5u + 1u] = select(0u, 1u, (inCamera || off) && shown);
|
|
138
|
+
cameraArgs[i * 5u + 1u] = select(0u, 1u, (inCamera || off) && shown && graph);
|
|
135
139
|
shadowArgs[i * 5u + 1u] = select(0u, 1u, ((inLight && castsShadow) || off) && shown);
|
|
136
|
-
mirrorArgs[i * 5u + 1u] = select(0u, 1u, (inMirror || off) && shown);
|
|
140
|
+
mirrorArgs[i * 5u + 1u] = select(0u, 1u, (inMirror || off) && shown && graph);
|
|
137
141
|
}
|
|
138
142
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ground.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/ground.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ground.d.ts","sourceRoot":"","sources":["../../../src/shaders/passes/ground.ts"],"names":[],"mappings":"AAoFA;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,UAAQ,GAAG,MAAM,CAyUrD;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,QAAS,CAAA;AACxC,eAAO,MAAM,gBAAgB,QAAS,CAAA;AAEtC;;;oEAGoE;AACpE,eAAO,MAAM,iBAAiB,OAAO,CAAA;AAErC;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,+oCAiClC,CAAA"}
|