@flare-engine/postfx 0.2.1 → 0.3.1
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/LICENSE +201 -21
- package/NOTICE +2 -0
- package/README.md +15 -5
- package/dist/index.d.mts +206 -216
- package/dist/index.d.mts.map +1 -0
- package/dist/index.d.ts +206 -216
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +669 -679
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +641 -625
- package/dist/index.mjs.map +1 -1
- package/package.json +16 -8
package/dist/index.js
CHANGED
|
@@ -1,311 +1,106 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/index.ts
|
|
21
|
-
var index_exports = {};
|
|
22
|
-
__export(index_exports, {
|
|
23
|
-
BLOOM_SKSL: () => BLOOM_SKSL,
|
|
24
|
-
CHROMATIC_ABERRATION_SKSL: () => CHROMATIC_ABERRATION_SKSL,
|
|
25
|
-
COLOR_GRADING_LUT_SKSL: () => COLOR_GRADING_LUT_SKSL,
|
|
26
|
-
COMPOSITE_SKSL: () => COMPOSITE_SKSL,
|
|
27
|
-
CRT_SKSL: () => CRT_SKSL,
|
|
28
|
-
EffectChain: () => EffectChain,
|
|
29
|
-
GOD_RAYS_SKSL: () => GOD_RAYS_SKSL,
|
|
30
|
-
GRAYSCALE_FLASH_SKSL: () => GRAYSCALE_FLASH_SKSL,
|
|
31
|
-
VIGNETTE_SKSL: () => VIGNETTE_SKSL,
|
|
32
|
-
WEATHER_CLOUDS_SKSL: () => WEATHER_CLOUDS_SKSL,
|
|
33
|
-
WEATHER_DARK_VIGNETTE_SKSL: () => WEATHER_DARK_VIGNETTE_SKSL,
|
|
34
|
-
WEATHER_RAIN_SKSL: () => WEATHER_RAIN_SKSL,
|
|
35
|
-
WEATHER_SUN_SHAFT_SKSL: () => WEATHER_SUN_SHAFT_SKSL,
|
|
36
|
-
createBloom: () => createBloom,
|
|
37
|
-
createChromaticAberration: () => createChromaticAberration,
|
|
38
|
-
createColorGradingLut: () => createColorGradingLut,
|
|
39
|
-
createCrt: () => createCrt,
|
|
40
|
-
createGodRays: () => createGodRays,
|
|
41
|
-
createGrayscaleFlash: () => createGrayscaleFlash,
|
|
42
|
-
createNeutralLutBytes: () => createNeutralLutBytes,
|
|
43
|
-
createPostFxComposite: () => createPostFxComposite,
|
|
44
|
-
createPostFxController: () => createPostFxController,
|
|
45
|
-
createPostFxRuntime: () => createPostFxRuntime,
|
|
46
|
-
createVignette: () => createVignette,
|
|
47
|
-
createWeatherClouds: () => createWeatherClouds,
|
|
48
|
-
createWeatherDarkVignette: () => createWeatherDarkVignette,
|
|
49
|
-
createWeatherRain: () => createWeatherRain,
|
|
50
|
-
createWeatherSunShaft: () => createWeatherSunShaft
|
|
51
|
-
});
|
|
52
|
-
module.exports = __toCommonJS(index_exports);
|
|
53
|
-
|
|
54
|
-
// src/effect.ts
|
|
55
|
-
function createVignette(id = "vignette", uniforms = {}) {
|
|
56
|
-
return {
|
|
57
|
-
id,
|
|
58
|
-
enabled: true,
|
|
59
|
-
uniforms: {
|
|
60
|
-
kind: "vignette",
|
|
61
|
-
intensity: uniforms.intensity ?? 0.4,
|
|
62
|
-
radius: uniforms.radius ?? 0.8,
|
|
63
|
-
color: uniforms.color ?? [0, 0, 0]
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
function createGrayscaleFlash(id = "grayscale-flash", uniforms = {}) {
|
|
68
|
-
return {
|
|
69
|
-
id,
|
|
70
|
-
enabled: true,
|
|
71
|
-
uniforms: { kind: "grayscale-flash", intensity: uniforms.intensity ?? 0 }
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function createChromaticAberration(id = "chromatic-aberration", uniforms = {}) {
|
|
75
|
-
return {
|
|
76
|
-
id,
|
|
77
|
-
enabled: true,
|
|
78
|
-
uniforms: { kind: "chromatic-aberration", amount: uniforms.amount ?? 5e-3 }
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function createCrt(id = "crt", uniforms = {}) {
|
|
82
|
-
return {
|
|
83
|
-
id,
|
|
84
|
-
enabled: true,
|
|
85
|
-
uniforms: {
|
|
86
|
-
kind: "crt",
|
|
87
|
-
scanlineIntensity: uniforms.scanlineIntensity ?? 0.25,
|
|
88
|
-
curvature: uniforms.curvature ?? 0.12
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
function createBloom(id = "bloom", uniforms = {}) {
|
|
93
|
-
return {
|
|
94
|
-
id,
|
|
95
|
-
enabled: true,
|
|
96
|
-
uniforms: {
|
|
97
|
-
kind: "bloom",
|
|
98
|
-
threshold: uniforms.threshold ?? 0.7,
|
|
99
|
-
intensity: uniforms.intensity ?? 0.6
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
function createGodRays(id = "god-rays", uniforms = {}) {
|
|
104
|
-
return {
|
|
105
|
-
id,
|
|
106
|
-
enabled: true,
|
|
107
|
-
uniforms: {
|
|
108
|
-
kind: "god-rays",
|
|
109
|
-
lightX: uniforms.lightX ?? 0.5,
|
|
110
|
-
lightY: uniforms.lightY ?? 0.1,
|
|
111
|
-
color: uniforms.color ?? [1, 0.95, 0.8],
|
|
112
|
-
intensity: uniforms.intensity ?? 0.6,
|
|
113
|
-
decay: uniforms.decay ?? 0.96,
|
|
114
|
-
density: uniforms.density ?? 0.5,
|
|
115
|
-
weight: uniforms.weight ?? 0.4,
|
|
116
|
-
samples: uniforms.samples ?? 48
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
function createColorGradingLut(uniforms, id = "color-grading-lut") {
|
|
121
|
-
return {
|
|
122
|
-
id,
|
|
123
|
-
enabled: true,
|
|
124
|
-
uniforms: {
|
|
125
|
-
kind: "color-grading-lut",
|
|
126
|
-
lut: uniforms.lut,
|
|
127
|
-
intensity: uniforms.intensity ?? 1
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
function createWeatherRain(id = "weather-rain", uniforms = {}) {
|
|
132
|
-
return {
|
|
133
|
-
id,
|
|
134
|
-
enabled: true,
|
|
135
|
-
uniforms: {
|
|
136
|
-
kind: "weather-rain",
|
|
137
|
-
intensity: uniforms.intensity ?? 0.5,
|
|
138
|
-
direction: uniforms.direction ?? 0,
|
|
139
|
-
speed: uniforms.speed ?? 0,
|
|
140
|
-
scrollOffsetY: uniforms.scrollOffsetY ?? 0,
|
|
141
|
-
color: uniforms.color ?? [1, 1, 1],
|
|
142
|
-
density: uniforms.density ?? 0.5,
|
|
143
|
-
streakLen: uniforms.streakLen ?? 0.15
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
function createWeatherSunShaft(id = "weather-sun-shaft", uniforms = {}) {
|
|
148
|
-
return {
|
|
149
|
-
id,
|
|
150
|
-
enabled: true,
|
|
151
|
-
uniforms: {
|
|
152
|
-
kind: "weather-sun-shaft",
|
|
153
|
-
intensity: uniforms.intensity ?? 0.5,
|
|
154
|
-
direction: uniforms.direction ?? 0
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
function createWeatherDarkVignette(id = "weather-dark-vignette", uniforms = {}) {
|
|
159
|
-
return {
|
|
160
|
-
id,
|
|
161
|
-
enabled: true,
|
|
162
|
-
uniforms: {
|
|
163
|
-
kind: "weather-dark-vignette",
|
|
164
|
-
intensity: uniforms.intensity ?? 0.5
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
function createWeatherClouds(id = "weather-clouds", uniforms = {}) {
|
|
169
|
-
return {
|
|
170
|
-
id,
|
|
171
|
-
enabled: true,
|
|
172
|
-
uniforms: {
|
|
173
|
-
kind: "weather-clouds",
|
|
174
|
-
intensity: uniforms.intensity ?? 0.5,
|
|
175
|
-
direction: uniforms.direction ?? 0,
|
|
176
|
-
speed: uniforms.speed ?? 0,
|
|
177
|
-
scrollOffsetX: uniforms.scrollOffsetX ?? 0
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
function createPostFxComposite(id = "composite", uniforms = {}) {
|
|
182
|
-
return {
|
|
183
|
-
id,
|
|
184
|
-
enabled: true,
|
|
185
|
-
uniforms: {
|
|
186
|
-
kind: "composite",
|
|
187
|
-
vignetteIntensity: uniforms.vignetteIntensity ?? 0,
|
|
188
|
-
vignetteRadius: uniforms.vignetteRadius ?? 0.8,
|
|
189
|
-
vignetteColor: uniforms.vignetteColor ?? [0, 0, 0],
|
|
190
|
-
bloomThreshold: uniforms.bloomThreshold ?? 0.7,
|
|
191
|
-
bloomIntensity: uniforms.bloomIntensity ?? 0,
|
|
192
|
-
flashIntensity: uniforms.flashIntensity ?? 0,
|
|
193
|
-
chromaAmount: uniforms.chromaAmount ?? 0,
|
|
194
|
-
crtScanlineIntensity: uniforms.crtScanlineIntensity ?? 0.25,
|
|
195
|
-
crtCurvature: uniforms.crtCurvature ?? 0.12,
|
|
196
|
-
crtEnabled: uniforms.crtEnabled ?? false,
|
|
197
|
-
weatherKind: uniforms.weatherKind ?? 0,
|
|
198
|
-
weatherIntensity: uniforms.weatherIntensity ?? 0,
|
|
199
|
-
weatherDirection: uniforms.weatherDirection ?? 0,
|
|
200
|
-
weatherScrollX: uniforms.weatherScrollX ?? 0,
|
|
201
|
-
weatherScrollY: uniforms.weatherScrollY ?? 0,
|
|
202
|
-
weatherColor: uniforms.weatherColor ?? [1, 1, 1],
|
|
203
|
-
weatherDensity: uniforms.weatherDensity ?? 0.5,
|
|
204
|
-
weatherStreakLen: uniforms.weatherStreakLen ?? 0.15
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// src/chain.ts
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/chain.ts
|
|
3
|
+
/**
|
|
4
|
+
* Ordered post-processing pipeline. Pure data structure — the runtime reads
|
|
5
|
+
* the enabled entries in order and feeds each through its compiled shader.
|
|
6
|
+
*/
|
|
210
7
|
var EffectChain = class {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
clear() {
|
|
295
|
-
this._effects.length = 0;
|
|
296
|
-
}
|
|
297
|
-
/** Iterate only over enabled effects, in order. */
|
|
298
|
-
*enabled() {
|
|
299
|
-
for (const e of this._effects) {
|
|
300
|
-
if (e.enabled) yield e;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
8
|
+
_effects = [];
|
|
9
|
+
_globalIntensity = 1;
|
|
10
|
+
get length() {
|
|
11
|
+
return this._effects.length;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Global intensity multiplier applied to every enabled effect's
|
|
15
|
+
* "strength" uniform when the pass is built. Default 1 (no-op).
|
|
16
|
+
*
|
|
17
|
+
* Scales the following uniform fields at pass-build time:
|
|
18
|
+
* - vignette.intensity
|
|
19
|
+
* - grayscale-flash.intensity
|
|
20
|
+
* - chromatic-aberration.amount
|
|
21
|
+
* - crt.scanlineIntensity
|
|
22
|
+
* - bloom.intensity
|
|
23
|
+
*
|
|
24
|
+
* Geometry-like uniforms (radius, curvature, threshold) are not scaled.
|
|
25
|
+
* Intended for a11y reduced-motion (set to 0 to null all dampen-able
|
|
26
|
+
* effects in one call) without touching per-effect state.
|
|
27
|
+
*
|
|
28
|
+
* Clamped to `>= 0`. Negative values are clamped to 0.
|
|
29
|
+
*/
|
|
30
|
+
get globalIntensity() {
|
|
31
|
+
return this._globalIntensity;
|
|
32
|
+
}
|
|
33
|
+
set globalIntensity(value) {
|
|
34
|
+
this._globalIntensity = value < 0 ? 0 : value;
|
|
35
|
+
}
|
|
36
|
+
/** Convenience setter — mirrors the getter/setter. */
|
|
37
|
+
setGlobalIntensity(value) {
|
|
38
|
+
this.globalIntensity = value;
|
|
39
|
+
}
|
|
40
|
+
/** Snapshot view of the chain. Do not mutate. */
|
|
41
|
+
all() {
|
|
42
|
+
return this._effects;
|
|
43
|
+
}
|
|
44
|
+
/** Append an effect to the end of the chain. */
|
|
45
|
+
add(effect) {
|
|
46
|
+
if (this._effects.some((e) => e.id === effect.id)) throw new Error(`EffectChain: duplicate id "${effect.id}"`);
|
|
47
|
+
this._effects.push(effect);
|
|
48
|
+
}
|
|
49
|
+
/** Insert an effect at a specific index (clamped to [0, length]). */
|
|
50
|
+
insert(index, effect) {
|
|
51
|
+
if (this._effects.some((e) => e.id === effect.id)) throw new Error(`EffectChain: duplicate id "${effect.id}"`);
|
|
52
|
+
const i = Math.max(0, Math.min(this._effects.length, index));
|
|
53
|
+
this._effects.splice(i, 0, effect);
|
|
54
|
+
}
|
|
55
|
+
remove(id) {
|
|
56
|
+
const i = this._effects.findIndex((e) => e.id === id);
|
|
57
|
+
if (i === -1) return false;
|
|
58
|
+
this._effects.splice(i, 1);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
get(id) {
|
|
62
|
+
return this._effects.find((e) => e.id === id) ?? null;
|
|
63
|
+
}
|
|
64
|
+
setEnabled(id, enabled) {
|
|
65
|
+
const e = this.get(id);
|
|
66
|
+
if (e) e.enabled = enabled;
|
|
67
|
+
}
|
|
68
|
+
/** Update uniforms in place. Kind must match the existing effect's kind. */
|
|
69
|
+
setUniforms(id, uniforms) {
|
|
70
|
+
const e = this.get(id);
|
|
71
|
+
if (!e) return;
|
|
72
|
+
if (e.uniforms.kind !== uniforms.kind) throw new Error(`EffectChain: cannot change kind of "${id}" from "${e.uniforms.kind}" to "${uniforms.kind}"`);
|
|
73
|
+
e.uniforms = uniforms;
|
|
74
|
+
}
|
|
75
|
+
move(id, newIndex) {
|
|
76
|
+
const i = this._effects.findIndex((e) => e.id === id);
|
|
77
|
+
if (i === -1) return;
|
|
78
|
+
const [removed] = this._effects.splice(i, 1);
|
|
79
|
+
if (!removed) return;
|
|
80
|
+
const clamped = Math.max(0, Math.min(this._effects.length, newIndex));
|
|
81
|
+
this._effects.splice(clamped, 0, removed);
|
|
82
|
+
}
|
|
83
|
+
clear() {
|
|
84
|
+
this._effects.length = 0;
|
|
85
|
+
}
|
|
86
|
+
/** Iterate only over enabled effects, in order. */
|
|
87
|
+
*enabled() {
|
|
88
|
+
for (const e of this._effects) if (e.enabled) yield e;
|
|
89
|
+
}
|
|
303
90
|
};
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/shaders.ts
|
|
93
|
+
/**
|
|
94
|
+
* SkSL runtime-effect shaders used by the postfx stack.
|
|
95
|
+
*
|
|
96
|
+
* Each source declares a `uniform shader src;` plus per-effect uniforms. They
|
|
97
|
+
* are sampled as `src.eval(fragCoord)` against the scene's offscreen surface.
|
|
98
|
+
*
|
|
99
|
+
* Shader strings are emitted from the package as-is. Fallback: when
|
|
100
|
+
* `Skia.RuntimeEffect.Make` is unavailable, `runtime.ts` short-circuits the
|
|
101
|
+
* chain to a passthrough.
|
|
102
|
+
*/
|
|
103
|
+
const VIGNETTE_SKSL = `
|
|
309
104
|
uniform shader src;
|
|
310
105
|
uniform float2 resolution;
|
|
311
106
|
uniform float intensity;
|
|
@@ -320,11 +115,8 @@ half4 main(float2 coord) {
|
|
|
320
115
|
float v = smoothstep(radius, radius * 0.5, d);
|
|
321
116
|
return half4(mix(half3(color), c.rgb, mix(1.0 - intensity, 1.0, v)), c.a);
|
|
322
117
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
var GRAYSCALE_FLASH_SKSL = (
|
|
326
|
-
/* sksl */
|
|
327
|
-
`
|
|
118
|
+
`;
|
|
119
|
+
const GRAYSCALE_FLASH_SKSL = `
|
|
328
120
|
uniform shader src;
|
|
329
121
|
uniform float intensity;
|
|
330
122
|
|
|
@@ -335,11 +127,8 @@ half4 main(float2 coord) {
|
|
|
335
127
|
half3 mixed = mix(c.rgb, gray, half(intensity));
|
|
336
128
|
return half4(mixed, c.a);
|
|
337
129
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
var CHROMATIC_ABERRATION_SKSL = (
|
|
341
|
-
/* sksl */
|
|
342
|
-
`
|
|
130
|
+
`;
|
|
131
|
+
const CHROMATIC_ABERRATION_SKSL = `
|
|
343
132
|
uniform shader src;
|
|
344
133
|
uniform float2 resolution;
|
|
345
134
|
uniform float amount;
|
|
@@ -353,11 +142,8 @@ half4 main(float2 coord) {
|
|
|
353
142
|
half a = src.eval(coord).a;
|
|
354
143
|
return half4(r, g, b, a);
|
|
355
144
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
var CRT_SKSL = (
|
|
359
|
-
/* sksl */
|
|
360
|
-
`
|
|
145
|
+
`;
|
|
146
|
+
const CRT_SKSL = `
|
|
361
147
|
uniform shader src;
|
|
362
148
|
uniform float2 resolution;
|
|
363
149
|
uniform float scanlineIntensity;
|
|
@@ -376,11 +162,8 @@ half4 main(float2 coord) {
|
|
|
376
162
|
float dim = mix(1.0, scan, scanlineIntensity);
|
|
377
163
|
return half4(c.rgb * dim, c.a);
|
|
378
164
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
var BLOOM_SKSL = (
|
|
382
|
-
/* sksl */
|
|
383
|
-
`
|
|
165
|
+
`;
|
|
166
|
+
const BLOOM_SKSL = `
|
|
384
167
|
uniform shader src;
|
|
385
168
|
uniform float2 resolution;
|
|
386
169
|
uniform float threshold;
|
|
@@ -393,11 +176,8 @@ half4 main(float2 coord) {
|
|
|
393
176
|
half3 boost = c.rgb * mask * intensity;
|
|
394
177
|
return half4(c.rgb + boost, c.a);
|
|
395
178
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
var COLOR_GRADING_LUT_SKSL = (
|
|
399
|
-
/* sksl */
|
|
400
|
-
`
|
|
179
|
+
`;
|
|
180
|
+
const COLOR_GRADING_LUT_SKSL = `
|
|
401
181
|
uniform shader src;
|
|
402
182
|
uniform shader lut;
|
|
403
183
|
uniform float intensity;
|
|
@@ -426,11 +206,8 @@ half4 main(float2 coord) {
|
|
|
426
206
|
|
|
427
207
|
return half4(result, scene.a);
|
|
428
208
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
var WEATHER_RAIN_SKSL = (
|
|
432
|
-
/* sksl */
|
|
433
|
-
`
|
|
209
|
+
`;
|
|
210
|
+
const WEATHER_RAIN_SKSL = `
|
|
434
211
|
uniform shader src;
|
|
435
212
|
uniform float2 resolution;
|
|
436
213
|
uniform float intensity;
|
|
@@ -458,7 +235,7 @@ half4 main(float2 coord) {
|
|
|
458
235
|
|
|
459
236
|
float coverage = 0.0;
|
|
460
237
|
|
|
461
|
-
// near layer
|
|
238
|
+
// near layer — COLS=45, tiling=6, brightness=1.0
|
|
462
239
|
{
|
|
463
240
|
float COLS = 45.0;
|
|
464
241
|
float tiling = 6.0;
|
|
@@ -477,7 +254,7 @@ half4 main(float2 coord) {
|
|
|
477
254
|
}
|
|
478
255
|
}
|
|
479
256
|
|
|
480
|
-
// far layer
|
|
257
|
+
// far layer — COLS=85, tiling=11, brightness=0.5
|
|
481
258
|
{
|
|
482
259
|
float COLS = 85.0;
|
|
483
260
|
float tiling = 11.0;
|
|
@@ -499,11 +276,8 @@ half4 main(float2 coord) {
|
|
|
499
276
|
half3 mixed = mix(scene.rgb, half3(color), half(coverage * intensity));
|
|
500
277
|
return half4(mixed, scene.a);
|
|
501
278
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
var WEATHER_SUN_SHAFT_SKSL = (
|
|
505
|
-
/* sksl */
|
|
506
|
-
`
|
|
279
|
+
`;
|
|
280
|
+
const WEATHER_SUN_SHAFT_SKSL = `
|
|
507
281
|
uniform shader src;
|
|
508
282
|
uniform float2 resolution;
|
|
509
283
|
uniform float intensity;
|
|
@@ -522,11 +296,8 @@ half4 main(float2 coord) {
|
|
|
522
296
|
half shaft = half(shafts * falloff * intensity * 0.35);
|
|
523
297
|
return half4(scene.rgb + half3(shaft * 0.95, shaft * 0.9, shaft * 0.6), scene.a);
|
|
524
298
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
var WEATHER_DARK_VIGNETTE_SKSL = (
|
|
528
|
-
/* sksl */
|
|
529
|
-
`
|
|
299
|
+
`;
|
|
300
|
+
const WEATHER_DARK_VIGNETTE_SKSL = `
|
|
530
301
|
uniform shader src;
|
|
531
302
|
uniform float2 resolution;
|
|
532
303
|
uniform float intensity;
|
|
@@ -539,15 +310,13 @@ half4 main(float2 coord) {
|
|
|
539
310
|
half dark = half(v * intensity);
|
|
540
311
|
return half4(scene.rgb * (1.0 - dark), scene.a);
|
|
541
312
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
var WEATHER_CLOUDS_SKSL = (
|
|
545
|
-
/* sksl */
|
|
546
|
-
`
|
|
313
|
+
`;
|
|
314
|
+
const WEATHER_CLOUDS_SKSL = `
|
|
547
315
|
uniform shader src;
|
|
548
316
|
uniform float2 resolution;
|
|
549
317
|
uniform float intensity;
|
|
550
318
|
uniform float scrollOffsetX;
|
|
319
|
+
uniform float direction;
|
|
551
320
|
|
|
552
321
|
float hash(float2 p) { return fract(sin(dot(p, float2(127.1, 311.7))) * 43758.5453); }
|
|
553
322
|
float vnoise(float2 p) {
|
|
@@ -561,18 +330,21 @@ float fbm(float2 p) { return vnoise(p) * 0.6 + vnoise(p * 2.3) * 0.4; }
|
|
|
561
330
|
half4 main(float2 coord) {
|
|
562
331
|
half4 scene = src.eval(coord);
|
|
563
332
|
float2 uv = coord / resolution;
|
|
564
|
-
|
|
333
|
+
// direction=0 → pure horizontal scroll (cos=1, sin=0), matching the pre-direction default.
|
|
334
|
+
float rad = direction * 3.14159 / 180.0;
|
|
335
|
+
float2 scrollDir = float2(cos(rad), sin(rad));
|
|
336
|
+
float2 cloudUv = float2(
|
|
337
|
+
uv.x + scrollOffsetX * scrollDir.x / resolution.x,
|
|
338
|
+
uv.y * 0.5 + scrollOffsetX * scrollDir.y / resolution.y
|
|
339
|
+
);
|
|
565
340
|
float cloud = fbm(cloudUv * 3.0);
|
|
566
341
|
float heightFade = smoothstep(0.6, 0.2, uv.y);
|
|
567
342
|
float alpha = cloud * heightFade * intensity * 0.5;
|
|
568
343
|
half3 cloudColor = half3(0.9, 0.9, 0.95);
|
|
569
344
|
return half4(mix(scene.rgb, cloudColor, half(alpha)), scene.a);
|
|
570
345
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
var COMPOSITE_SKSL = (
|
|
574
|
-
/* sksl */
|
|
575
|
-
`
|
|
346
|
+
`;
|
|
347
|
+
const COMPOSITE_SKSL = `
|
|
576
348
|
uniform shader src;
|
|
577
349
|
|
|
578
350
|
uniform float2 resolution;
|
|
@@ -639,7 +411,7 @@ half4 main(float2 coord) {
|
|
|
639
411
|
{
|
|
640
412
|
float wk = weatherKind;
|
|
641
413
|
if (wk >= 0.5 && wk < 1.5) {
|
|
642
|
-
// rain
|
|
414
|
+
// rain — falling sawtooth streaks, 2 parallax layers, alpha-over blend
|
|
643
415
|
// weatherScrollY is normalized [0,1) fall phase (increment speed*dt/screenH each tick)
|
|
644
416
|
float rad = weatherDirection * 3.14159 / 180.0;
|
|
645
417
|
float sinA = sin(rad);
|
|
@@ -650,7 +422,7 @@ half4 main(float2 coord) {
|
|
|
650
422
|
|
|
651
423
|
float coverage = 0.0;
|
|
652
424
|
|
|
653
|
-
// near layer
|
|
425
|
+
// near layer — COLS=45, tiling=6, brightness=1.0
|
|
654
426
|
{
|
|
655
427
|
float COLS = 45.0;
|
|
656
428
|
float tiling = 6.0;
|
|
@@ -669,7 +441,7 @@ half4 main(float2 coord) {
|
|
|
669
441
|
}
|
|
670
442
|
}
|
|
671
443
|
|
|
672
|
-
// far layer
|
|
444
|
+
// far layer — COLS=85, tiling=11, brightness=0.5
|
|
673
445
|
{
|
|
674
446
|
float COLS = 85.0;
|
|
675
447
|
float tiling = 11.0;
|
|
@@ -731,11 +503,8 @@ half4 main(float2 coord) {
|
|
|
731
503
|
|
|
732
504
|
return half4(c, 1.0);
|
|
733
505
|
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
var GOD_RAYS_SKSL = (
|
|
737
|
-
/* sksl */
|
|
738
|
-
`
|
|
506
|
+
`;
|
|
507
|
+
const GOD_RAYS_SKSL = `
|
|
739
508
|
uniform shader src;
|
|
740
509
|
uniform float2 resolution;
|
|
741
510
|
uniform float2 u_lightPos;
|
|
@@ -767,325 +536,546 @@ half4 main(float2 coord) {
|
|
|
767
536
|
half3 rays = accumulated * half3(u_color) * half(u_weight) * half(u_intensity);
|
|
768
537
|
return half4(orig.rgb + rays, orig.a);
|
|
769
538
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
539
|
+
`;
|
|
540
|
+
//#endregion
|
|
541
|
+
//#region src/runtime.ts
|
|
542
|
+
/** Shared frozen sentinel — every pass with no extra children points here. */
|
|
543
|
+
const EMPTY_CHILDREN = Object.freeze([]);
|
|
544
|
+
const SKSL_SOURCE = {
|
|
545
|
+
vignette: VIGNETTE_SKSL,
|
|
546
|
+
"grayscale-flash": GRAYSCALE_FLASH_SKSL,
|
|
547
|
+
"chromatic-aberration": CHROMATIC_ABERRATION_SKSL,
|
|
548
|
+
crt: CRT_SKSL,
|
|
549
|
+
bloom: BLOOM_SKSL,
|
|
550
|
+
"god-rays": GOD_RAYS_SKSL,
|
|
551
|
+
"color-grading-lut": COLOR_GRADING_LUT_SKSL,
|
|
552
|
+
"weather-rain": WEATHER_RAIN_SKSL,
|
|
553
|
+
"weather-sun-shaft": WEATHER_SUN_SHAFT_SKSL,
|
|
554
|
+
"weather-dark-vignette": WEATHER_DARK_VIGNETTE_SKSL,
|
|
555
|
+
"weather-clouds": WEATHER_CLOUDS_SKSL,
|
|
556
|
+
composite: COMPOSITE_SKSL
|
|
788
557
|
};
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
558
|
+
/**
|
|
559
|
+
* Create a runtime against the given Skia-like module. If the module is
|
|
560
|
+
* absent or lacks `RuntimeEffect.Make`, returns a runtime with
|
|
561
|
+
* `supported: false` — callers should fall through to a passthrough pipeline.
|
|
562
|
+
*/
|
|
563
|
+
function createPostFxRuntime(skia, opts) {
|
|
564
|
+
const cache = /* @__PURE__ */ new Map();
|
|
565
|
+
const _loggedCompileFailures = /* @__PURE__ */ new Set();
|
|
566
|
+
const _loggedCompileSuccesses = /* @__PURE__ */ new Set();
|
|
567
|
+
const supported = skia !== null && typeof skia.RuntimeEffect?.Make === "function";
|
|
568
|
+
return {
|
|
569
|
+
supported,
|
|
570
|
+
compile(kind) {
|
|
571
|
+
if (!supported || !skia) return null;
|
|
572
|
+
const cached = cache.get(kind);
|
|
573
|
+
if (cached) return cached;
|
|
574
|
+
const effect = skia.RuntimeEffect.Make(SKSL_SOURCE[kind]);
|
|
575
|
+
if (!effect) {
|
|
576
|
+
if (!_loggedCompileFailures.has(kind)) {
|
|
577
|
+
_loggedCompileFailures.add(kind);
|
|
578
|
+
console.warn(`[postfx] RuntimeEffect.Make returned null for "${kind}" — shader failed to compile. Pass will be silently skipped.`);
|
|
579
|
+
}
|
|
580
|
+
return null;
|
|
581
|
+
}
|
|
582
|
+
if (!_loggedCompileSuccesses.has(kind)) {
|
|
583
|
+
_loggedCompileSuccesses.add(kind);
|
|
584
|
+
if (opts?.verbose === true && typeof effect.getUniformFloatCount === "function") console.info(`[postfx] compiled "${kind}" — getUniformFloatCount=${effect.getUniformFloatCount()}`);
|
|
585
|
+
}
|
|
586
|
+
const compiled = {
|
|
587
|
+
kind,
|
|
588
|
+
effect
|
|
589
|
+
};
|
|
590
|
+
cache.set(kind, compiled);
|
|
591
|
+
return compiled;
|
|
592
|
+
},
|
|
593
|
+
packUniforms(effect, width, height) {
|
|
594
|
+
return packUniforms(effect.uniforms, width, height);
|
|
595
|
+
},
|
|
596
|
+
buildPass(chain, width, height) {
|
|
597
|
+
const out = [];
|
|
598
|
+
if (!supported) return out;
|
|
599
|
+
const scale = chain.globalIntensity;
|
|
600
|
+
for (const effect of chain.enabled()) {
|
|
601
|
+
const compiled = this.compile(effect.uniforms.kind);
|
|
602
|
+
if (!compiled) continue;
|
|
603
|
+
const uniforms = packUniforms(effect.uniforms, width, height);
|
|
604
|
+
if (scale !== 1) scaleIntensity(effect.uniforms.kind, uniforms, scale);
|
|
605
|
+
const children = effect.uniforms.kind === "color-grading-lut" ? [effect.uniforms.lut] : EMPTY_CHILDREN;
|
|
606
|
+
out.push({
|
|
607
|
+
id: effect.id,
|
|
608
|
+
kind: effect.uniforms.kind,
|
|
609
|
+
shader: compiled.effect,
|
|
610
|
+
uniforms,
|
|
611
|
+
children
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
return out;
|
|
615
|
+
},
|
|
616
|
+
reset() {
|
|
617
|
+
cache.clear();
|
|
618
|
+
}
|
|
619
|
+
};
|
|
849
620
|
}
|
|
850
621
|
function packUniforms(u, width, height) {
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
622
|
+
switch (u.kind) {
|
|
623
|
+
case "vignette": {
|
|
624
|
+
const col = u.color ?? [
|
|
625
|
+
0,
|
|
626
|
+
0,
|
|
627
|
+
0
|
|
628
|
+
];
|
|
629
|
+
return new Float32Array([
|
|
630
|
+
width,
|
|
631
|
+
height,
|
|
632
|
+
u.intensity,
|
|
633
|
+
u.radius,
|
|
634
|
+
col[0],
|
|
635
|
+
col[1],
|
|
636
|
+
col[2]
|
|
637
|
+
]);
|
|
638
|
+
}
|
|
639
|
+
case "grayscale-flash": return new Float32Array([u.intensity]);
|
|
640
|
+
case "chromatic-aberration": return new Float32Array([
|
|
641
|
+
width,
|
|
642
|
+
height,
|
|
643
|
+
u.amount
|
|
644
|
+
]);
|
|
645
|
+
case "crt": return new Float32Array([
|
|
646
|
+
width,
|
|
647
|
+
height,
|
|
648
|
+
u.scanlineIntensity,
|
|
649
|
+
u.curvature
|
|
650
|
+
]);
|
|
651
|
+
case "bloom": return new Float32Array([
|
|
652
|
+
width,
|
|
653
|
+
height,
|
|
654
|
+
u.threshold,
|
|
655
|
+
u.intensity
|
|
656
|
+
]);
|
|
657
|
+
case "god-rays": {
|
|
658
|
+
const samples = Math.max(1, Math.min(64, Math.round(u.samples)));
|
|
659
|
+
return new Float32Array([
|
|
660
|
+
width,
|
|
661
|
+
height,
|
|
662
|
+
u.lightX,
|
|
663
|
+
u.lightY,
|
|
664
|
+
u.color[0],
|
|
665
|
+
u.color[1],
|
|
666
|
+
u.color[2],
|
|
667
|
+
u.intensity,
|
|
668
|
+
u.decay,
|
|
669
|
+
u.density,
|
|
670
|
+
u.weight,
|
|
671
|
+
samples
|
|
672
|
+
]);
|
|
673
|
+
}
|
|
674
|
+
case "color-grading-lut": return new Float32Array([u.intensity]);
|
|
675
|
+
case "weather-rain": {
|
|
676
|
+
const col = u.color ?? [
|
|
677
|
+
1,
|
|
678
|
+
1,
|
|
679
|
+
1
|
|
680
|
+
];
|
|
681
|
+
return new Float32Array([
|
|
682
|
+
width,
|
|
683
|
+
height,
|
|
684
|
+
u.intensity,
|
|
685
|
+
u.direction,
|
|
686
|
+
u.scrollOffsetY,
|
|
687
|
+
col[0],
|
|
688
|
+
col[1],
|
|
689
|
+
col[2],
|
|
690
|
+
u.density ?? .5,
|
|
691
|
+
u.streakLen ?? .15
|
|
692
|
+
]);
|
|
693
|
+
}
|
|
694
|
+
case "weather-sun-shaft": return new Float32Array([
|
|
695
|
+
width,
|
|
696
|
+
height,
|
|
697
|
+
u.intensity,
|
|
698
|
+
u.direction
|
|
699
|
+
]);
|
|
700
|
+
case "weather-dark-vignette": return new Float32Array([
|
|
701
|
+
width,
|
|
702
|
+
height,
|
|
703
|
+
u.intensity
|
|
704
|
+
]);
|
|
705
|
+
case "weather-clouds": return new Float32Array([
|
|
706
|
+
width,
|
|
707
|
+
height,
|
|
708
|
+
u.intensity,
|
|
709
|
+
u.scrollOffsetX,
|
|
710
|
+
u.direction
|
|
711
|
+
]);
|
|
712
|
+
case "composite": return new Float32Array([
|
|
713
|
+
width,
|
|
714
|
+
height,
|
|
715
|
+
u.vignetteIntensity,
|
|
716
|
+
u.vignetteRadius,
|
|
717
|
+
u.vignetteColor[0],
|
|
718
|
+
u.vignetteColor[1],
|
|
719
|
+
u.vignetteColor[2],
|
|
720
|
+
u.bloomThreshold,
|
|
721
|
+
u.bloomIntensity,
|
|
722
|
+
u.flashIntensity,
|
|
723
|
+
u.chromaAmount,
|
|
724
|
+
u.crtScanlineIntensity,
|
|
725
|
+
u.crtCurvature,
|
|
726
|
+
u.crtEnabled ? 1 : 0,
|
|
727
|
+
u.weatherKind,
|
|
728
|
+
u.weatherIntensity,
|
|
729
|
+
u.weatherDirection,
|
|
730
|
+
u.weatherScrollX,
|
|
731
|
+
u.weatherScrollY,
|
|
732
|
+
u.weatherColor[0],
|
|
733
|
+
u.weatherColor[1],
|
|
734
|
+
u.weatherColor[2],
|
|
735
|
+
u.weatherDensity,
|
|
736
|
+
u.weatherStreakLen
|
|
737
|
+
]);
|
|
738
|
+
}
|
|
934
739
|
}
|
|
740
|
+
/**
|
|
741
|
+
* Scale the "strength" slot of a packed uniform buffer in place. Geometry
|
|
742
|
+
* fields (radius, curvature, threshold) are intentionally left alone.
|
|
743
|
+
*/
|
|
935
744
|
function scaleIntensity(kind, packed, scale) {
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
745
|
+
switch (kind) {
|
|
746
|
+
case "vignette":
|
|
747
|
+
packed[2] = (packed[2] ?? 0) * scale;
|
|
748
|
+
return;
|
|
749
|
+
case "grayscale-flash":
|
|
750
|
+
packed[0] = (packed[0] ?? 0) * scale;
|
|
751
|
+
return;
|
|
752
|
+
case "chromatic-aberration":
|
|
753
|
+
packed[2] = (packed[2] ?? 0) * scale;
|
|
754
|
+
return;
|
|
755
|
+
case "crt":
|
|
756
|
+
packed[2] = (packed[2] ?? 0) * scale;
|
|
757
|
+
return;
|
|
758
|
+
case "bloom":
|
|
759
|
+
packed[3] = (packed[3] ?? 0) * scale;
|
|
760
|
+
return;
|
|
761
|
+
case "god-rays":
|
|
762
|
+
packed[7] = (packed[7] ?? 0) * scale;
|
|
763
|
+
return;
|
|
764
|
+
case "color-grading-lut":
|
|
765
|
+
packed[0] = (packed[0] ?? 0) * scale;
|
|
766
|
+
return;
|
|
767
|
+
case "weather-rain":
|
|
768
|
+
case "weather-sun-shaft":
|
|
769
|
+
case "weather-dark-vignette":
|
|
770
|
+
case "weather-clouds":
|
|
771
|
+
packed[2] = (packed[2] ?? 0) * scale;
|
|
772
|
+
return;
|
|
773
|
+
case "composite":
|
|
774
|
+
packed[2] = (packed[2] ?? 0) * scale;
|
|
775
|
+
packed[8] = (packed[8] ?? 0) * scale;
|
|
776
|
+
packed[9] = (packed[9] ?? 0) * scale;
|
|
777
|
+
packed[10] = (packed[10] ?? 0) * scale;
|
|
778
|
+
packed[15] = (packed[15] ?? 0) * scale;
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
972
781
|
}
|
|
973
|
-
|
|
974
|
-
|
|
782
|
+
//#endregion
|
|
783
|
+
//#region src/controller.ts
|
|
784
|
+
/**
|
|
785
|
+
* PostFxController — a reusable bridge that owns an EffectChain + PostFxRuntime
|
|
786
|
+
* and adds two game-common conveniences:
|
|
787
|
+
*
|
|
788
|
+
* - hit-flash: spike a grayscale-flash effect to 1 and auto-decay it.
|
|
789
|
+
* - motion scale: forward a single scalar to `EffectChain.setGlobalIntensity`
|
|
790
|
+
* so a11y reduced-motion can null the whole stack at once.
|
|
791
|
+
*
|
|
792
|
+
* Games can instantiate multiple controllers and swap implementations freely.
|
|
793
|
+
*/
|
|
794
|
+
const DEFAULT_FLASH_DECAY = 4.2;
|
|
795
|
+
function createPostFxController(options) {
|
|
796
|
+
const chain = new EffectChain();
|
|
797
|
+
for (let i = 0; i < options.effects.length; i++) {
|
|
798
|
+
const e = options.effects[i];
|
|
799
|
+
if (e) chain.add(e);
|
|
800
|
+
}
|
|
801
|
+
const runtime = createPostFxRuntime(options.skia ?? null);
|
|
802
|
+
const flashCfg = options.flash;
|
|
803
|
+
const flashDecay = flashCfg?.decay ?? DEFAULT_FLASH_DECAY;
|
|
804
|
+
let flashIntensity = 0;
|
|
805
|
+
const timedRemaining = /* @__PURE__ */ new Map();
|
|
806
|
+
const timedRestore = /* @__PURE__ */ new Map();
|
|
807
|
+
return {
|
|
808
|
+
chain,
|
|
809
|
+
runtime,
|
|
810
|
+
triggerHitFlash() {
|
|
811
|
+
if (!flashCfg) return;
|
|
812
|
+
flashIntensity = 1;
|
|
813
|
+
chain.setUniforms(flashCfg.id, {
|
|
814
|
+
kind: "grayscale-flash",
|
|
815
|
+
intensity: 1
|
|
816
|
+
});
|
|
817
|
+
},
|
|
818
|
+
triggerTimed(effectId, decayMs) {
|
|
819
|
+
const effect = chain.get(effectId);
|
|
820
|
+
if (!effect) return;
|
|
821
|
+
timedRestore.set(effectId, { ...effect.uniforms });
|
|
822
|
+
chain.setEnabled(effectId, true);
|
|
823
|
+
timedRemaining.set(effectId, decayMs);
|
|
824
|
+
},
|
|
825
|
+
update(dt) {
|
|
826
|
+
if (flashCfg && flashIntensity > 0) {
|
|
827
|
+
flashIntensity = Math.max(0, flashIntensity - dt * flashDecay);
|
|
828
|
+
chain.setUniforms(flashCfg.id, {
|
|
829
|
+
kind: "grayscale-flash",
|
|
830
|
+
intensity: flashIntensity
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
if (timedRemaining.size > 0) {
|
|
834
|
+
const dtMs = dt * 1e3;
|
|
835
|
+
const expired = [];
|
|
836
|
+
for (const [id, rem] of timedRemaining) {
|
|
837
|
+
const next = rem - dtMs;
|
|
838
|
+
if (next <= 0) expired.push(id);
|
|
839
|
+
else timedRemaining.set(id, next);
|
|
840
|
+
}
|
|
841
|
+
if (expired.length > 0) for (const id of expired) {
|
|
842
|
+
const snapshot = timedRestore.get(id);
|
|
843
|
+
if (snapshot !== void 0) chain.setUniforms(id, snapshot);
|
|
844
|
+
chain.setEnabled(id, false);
|
|
845
|
+
timedRemaining.delete(id);
|
|
846
|
+
timedRestore.delete(id);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
},
|
|
850
|
+
buildPass(width, height) {
|
|
851
|
+
return runtime.buildPass(chain, width, height);
|
|
852
|
+
},
|
|
853
|
+
setMotionScale(k) {
|
|
854
|
+
chain.setGlobalIntensity(k);
|
|
855
|
+
}
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
//#endregion
|
|
859
|
+
//#region src/effect.ts
|
|
860
|
+
function createVignette(id = "vignette", uniforms = {}) {
|
|
861
|
+
return {
|
|
862
|
+
id,
|
|
863
|
+
enabled: true,
|
|
864
|
+
uniforms: {
|
|
865
|
+
kind: "vignette",
|
|
866
|
+
intensity: uniforms.intensity ?? .4,
|
|
867
|
+
radius: uniforms.radius ?? .8,
|
|
868
|
+
color: uniforms.color ?? [
|
|
869
|
+
0,
|
|
870
|
+
0,
|
|
871
|
+
0
|
|
872
|
+
]
|
|
873
|
+
}
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function createGrayscaleFlash(id = "grayscale-flash", uniforms = {}) {
|
|
877
|
+
return {
|
|
878
|
+
id,
|
|
879
|
+
enabled: true,
|
|
880
|
+
uniforms: {
|
|
881
|
+
kind: "grayscale-flash",
|
|
882
|
+
intensity: uniforms.intensity ?? 0
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
function createChromaticAberration(id = "chromatic-aberration", uniforms = {}) {
|
|
887
|
+
return {
|
|
888
|
+
id,
|
|
889
|
+
enabled: true,
|
|
890
|
+
uniforms: {
|
|
891
|
+
kind: "chromatic-aberration",
|
|
892
|
+
amount: uniforms.amount ?? .005
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
function createCrt(id = "crt", uniforms = {}) {
|
|
897
|
+
return {
|
|
898
|
+
id,
|
|
899
|
+
enabled: true,
|
|
900
|
+
uniforms: {
|
|
901
|
+
kind: "crt",
|
|
902
|
+
scanlineIntensity: uniforms.scanlineIntensity ?? .25,
|
|
903
|
+
curvature: uniforms.curvature ?? .12
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
function createBloom(id = "bloom", uniforms = {}) {
|
|
908
|
+
return {
|
|
909
|
+
id,
|
|
910
|
+
enabled: true,
|
|
911
|
+
uniforms: {
|
|
912
|
+
kind: "bloom",
|
|
913
|
+
threshold: uniforms.threshold ?? .7,
|
|
914
|
+
intensity: uniforms.intensity ?? .6
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
function createGodRays(id = "god-rays", uniforms = {}) {
|
|
919
|
+
return {
|
|
920
|
+
id,
|
|
921
|
+
enabled: true,
|
|
922
|
+
uniforms: {
|
|
923
|
+
kind: "god-rays",
|
|
924
|
+
lightX: uniforms.lightX ?? .5,
|
|
925
|
+
lightY: uniforms.lightY ?? .1,
|
|
926
|
+
color: uniforms.color ?? [
|
|
927
|
+
1,
|
|
928
|
+
.95,
|
|
929
|
+
.8
|
|
930
|
+
],
|
|
931
|
+
intensity: uniforms.intensity ?? .6,
|
|
932
|
+
decay: uniforms.decay ?? .96,
|
|
933
|
+
density: uniforms.density ?? .5,
|
|
934
|
+
weight: uniforms.weight ?? .4,
|
|
935
|
+
samples: uniforms.samples ?? 48
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
function createColorGradingLut(id, uniforms) {
|
|
940
|
+
return {
|
|
941
|
+
id,
|
|
942
|
+
enabled: true,
|
|
943
|
+
uniforms: {
|
|
944
|
+
kind: "color-grading-lut",
|
|
945
|
+
lut: uniforms.lut,
|
|
946
|
+
intensity: uniforms.intensity ?? 1
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
function createWeatherRain(id = "weather-rain", uniforms = {}) {
|
|
951
|
+
return {
|
|
952
|
+
id,
|
|
953
|
+
enabled: true,
|
|
954
|
+
uniforms: {
|
|
955
|
+
kind: "weather-rain",
|
|
956
|
+
intensity: uniforms.intensity ?? .5,
|
|
957
|
+
direction: uniforms.direction ?? 0,
|
|
958
|
+
scrollOffsetY: uniforms.scrollOffsetY ?? 0,
|
|
959
|
+
color: uniforms.color ?? [
|
|
960
|
+
1,
|
|
961
|
+
1,
|
|
962
|
+
1
|
|
963
|
+
],
|
|
964
|
+
density: uniforms.density ?? .5,
|
|
965
|
+
streakLen: uniforms.streakLen ?? .15
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
function createWeatherSunShaft(id = "weather-sun-shaft", uniforms = {}) {
|
|
970
|
+
return {
|
|
971
|
+
id,
|
|
972
|
+
enabled: true,
|
|
973
|
+
uniforms: {
|
|
974
|
+
kind: "weather-sun-shaft",
|
|
975
|
+
intensity: uniforms.intensity ?? .5,
|
|
976
|
+
direction: uniforms.direction ?? 0
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
function createWeatherDarkVignette(id = "weather-dark-vignette", uniforms = {}) {
|
|
981
|
+
return {
|
|
982
|
+
id,
|
|
983
|
+
enabled: true,
|
|
984
|
+
uniforms: {
|
|
985
|
+
kind: "weather-dark-vignette",
|
|
986
|
+
intensity: uniforms.intensity ?? .5
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
function createWeatherClouds(id = "weather-clouds", uniforms = {}) {
|
|
991
|
+
return {
|
|
992
|
+
id,
|
|
993
|
+
enabled: true,
|
|
994
|
+
uniforms: {
|
|
995
|
+
kind: "weather-clouds",
|
|
996
|
+
intensity: uniforms.intensity ?? .5,
|
|
997
|
+
direction: uniforms.direction ?? 0,
|
|
998
|
+
scrollOffsetX: uniforms.scrollOffsetX ?? 0
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
function createPostFxComposite(id = "composite", uniforms = {}) {
|
|
1003
|
+
return {
|
|
1004
|
+
id,
|
|
1005
|
+
enabled: true,
|
|
1006
|
+
uniforms: {
|
|
1007
|
+
kind: "composite",
|
|
1008
|
+
vignetteIntensity: uniforms.vignetteIntensity ?? 0,
|
|
1009
|
+
vignetteRadius: uniforms.vignetteRadius ?? .8,
|
|
1010
|
+
vignetteColor: uniforms.vignetteColor ?? [
|
|
1011
|
+
0,
|
|
1012
|
+
0,
|
|
1013
|
+
0
|
|
1014
|
+
],
|
|
1015
|
+
bloomThreshold: uniforms.bloomThreshold ?? .7,
|
|
1016
|
+
bloomIntensity: uniforms.bloomIntensity ?? 0,
|
|
1017
|
+
flashIntensity: uniforms.flashIntensity ?? 0,
|
|
1018
|
+
chromaAmount: uniforms.chromaAmount ?? 0,
|
|
1019
|
+
crtScanlineIntensity: uniforms.crtScanlineIntensity ?? .25,
|
|
1020
|
+
crtCurvature: uniforms.crtCurvature ?? .12,
|
|
1021
|
+
crtEnabled: uniforms.crtEnabled ?? false,
|
|
1022
|
+
weatherKind: uniforms.weatherKind ?? 0,
|
|
1023
|
+
weatherIntensity: uniforms.weatherIntensity ?? 0,
|
|
1024
|
+
weatherDirection: uniforms.weatherDirection ?? 0,
|
|
1025
|
+
weatherScrollX: uniforms.weatherScrollX ?? 0,
|
|
1026
|
+
weatherScrollY: uniforms.weatherScrollY ?? 0,
|
|
1027
|
+
weatherColor: uniforms.weatherColor ?? [
|
|
1028
|
+
1,
|
|
1029
|
+
1,
|
|
1030
|
+
1
|
|
1031
|
+
],
|
|
1032
|
+
weatherDensity: uniforms.weatherDensity ?? .5,
|
|
1033
|
+
weatherStreakLen: uniforms.weatherStreakLen ?? .15
|
|
1034
|
+
}
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
//#endregion
|
|
1038
|
+
//#region src/lut.ts
|
|
975
1039
|
function createNeutralLutBytes() {
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
buf[off + 3] = 255;
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
return buf;
|
|
1040
|
+
const buf = new Uint8Array(4096 * 64 * 4);
|
|
1041
|
+
for (let b = 0; b < 64; b++) for (let g = 0; g < 64; g++) for (let r = 0; r < 64; r++) {
|
|
1042
|
+
const x = b * 64 + r;
|
|
1043
|
+
const off = (g * 4096 + x) * 4;
|
|
1044
|
+
buf[off] = Math.round(r / 63 * 255);
|
|
1045
|
+
buf[off + 1] = Math.round(g / 63 * 255);
|
|
1046
|
+
buf[off + 2] = Math.round(b / 63 * 255);
|
|
1047
|
+
buf[off + 3] = 255;
|
|
1048
|
+
}
|
|
1049
|
+
return buf;
|
|
991
1050
|
}
|
|
1051
|
+
//#endregion
|
|
1052
|
+
exports.BLOOM_SKSL = BLOOM_SKSL;
|
|
1053
|
+
exports.CHROMATIC_ABERRATION_SKSL = CHROMATIC_ABERRATION_SKSL;
|
|
1054
|
+
exports.COLOR_GRADING_LUT_SKSL = COLOR_GRADING_LUT_SKSL;
|
|
1055
|
+
exports.COMPOSITE_SKSL = COMPOSITE_SKSL;
|
|
1056
|
+
exports.CRT_SKSL = CRT_SKSL;
|
|
1057
|
+
exports.EffectChain = EffectChain;
|
|
1058
|
+
exports.GOD_RAYS_SKSL = GOD_RAYS_SKSL;
|
|
1059
|
+
exports.GRAYSCALE_FLASH_SKSL = GRAYSCALE_FLASH_SKSL;
|
|
1060
|
+
exports.VIGNETTE_SKSL = VIGNETTE_SKSL;
|
|
1061
|
+
exports.WEATHER_CLOUDS_SKSL = WEATHER_CLOUDS_SKSL;
|
|
1062
|
+
exports.WEATHER_DARK_VIGNETTE_SKSL = WEATHER_DARK_VIGNETTE_SKSL;
|
|
1063
|
+
exports.WEATHER_RAIN_SKSL = WEATHER_RAIN_SKSL;
|
|
1064
|
+
exports.WEATHER_SUN_SHAFT_SKSL = WEATHER_SUN_SHAFT_SKSL;
|
|
1065
|
+
exports.createBloom = createBloom;
|
|
1066
|
+
exports.createChromaticAberration = createChromaticAberration;
|
|
1067
|
+
exports.createColorGradingLut = createColorGradingLut;
|
|
1068
|
+
exports.createCrt = createCrt;
|
|
1069
|
+
exports.createGodRays = createGodRays;
|
|
1070
|
+
exports.createGrayscaleFlash = createGrayscaleFlash;
|
|
1071
|
+
exports.createNeutralLutBytes = createNeutralLutBytes;
|
|
1072
|
+
exports.createPostFxComposite = createPostFxComposite;
|
|
1073
|
+
exports.createPostFxController = createPostFxController;
|
|
1074
|
+
exports.createPostFxRuntime = createPostFxRuntime;
|
|
1075
|
+
exports.createVignette = createVignette;
|
|
1076
|
+
exports.createWeatherClouds = createWeatherClouds;
|
|
1077
|
+
exports.createWeatherDarkVignette = createWeatherDarkVignette;
|
|
1078
|
+
exports.createWeatherRain = createWeatherRain;
|
|
1079
|
+
exports.createWeatherSunShaft = createWeatherSunShaft;
|
|
992
1080
|
|
|
993
|
-
// src/controller.ts
|
|
994
|
-
var DEFAULT_FLASH_DECAY = 4.2;
|
|
995
|
-
function createPostFxController(options) {
|
|
996
|
-
const chain = new EffectChain();
|
|
997
|
-
for (let i = 0; i < options.effects.length; i++) {
|
|
998
|
-
const e = options.effects[i];
|
|
999
|
-
if (e) chain.add(e);
|
|
1000
|
-
}
|
|
1001
|
-
const runtime = createPostFxRuntime(options.skia ?? null);
|
|
1002
|
-
const flashCfg = options.flash;
|
|
1003
|
-
const flashDecay = flashCfg?.decay ?? DEFAULT_FLASH_DECAY;
|
|
1004
|
-
let flashIntensity = 0;
|
|
1005
|
-
const timedRemaining = /* @__PURE__ */ new Map();
|
|
1006
|
-
const timedRestore = /* @__PURE__ */ new Map();
|
|
1007
|
-
return {
|
|
1008
|
-
chain,
|
|
1009
|
-
runtime,
|
|
1010
|
-
triggerHitFlash() {
|
|
1011
|
-
if (!flashCfg) return;
|
|
1012
|
-
flashIntensity = 1;
|
|
1013
|
-
chain.setUniforms(flashCfg.id, { kind: "grayscale-flash", intensity: 1 });
|
|
1014
|
-
},
|
|
1015
|
-
triggerTimed(effectId, decayMs) {
|
|
1016
|
-
const effect = chain.get(effectId);
|
|
1017
|
-
if (!effect) return;
|
|
1018
|
-
timedRestore.set(effectId, { ...effect.uniforms });
|
|
1019
|
-
chain.setEnabled(effectId, true);
|
|
1020
|
-
timedRemaining.set(effectId, decayMs);
|
|
1021
|
-
},
|
|
1022
|
-
update(dt) {
|
|
1023
|
-
if (flashCfg && flashIntensity > 0) {
|
|
1024
|
-
flashIntensity = Math.max(0, flashIntensity - dt * flashDecay);
|
|
1025
|
-
chain.setUniforms(flashCfg.id, {
|
|
1026
|
-
kind: "grayscale-flash",
|
|
1027
|
-
intensity: flashIntensity
|
|
1028
|
-
});
|
|
1029
|
-
}
|
|
1030
|
-
if (timedRemaining.size > 0) {
|
|
1031
|
-
const dtMs = dt * 1e3;
|
|
1032
|
-
const expired = [];
|
|
1033
|
-
for (const [id, rem] of timedRemaining) {
|
|
1034
|
-
const next = rem - dtMs;
|
|
1035
|
-
if (next <= 0) {
|
|
1036
|
-
expired.push(id);
|
|
1037
|
-
} else {
|
|
1038
|
-
timedRemaining.set(id, next);
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
if (expired.length > 0) {
|
|
1042
|
-
for (const id of expired) {
|
|
1043
|
-
const snapshot = timedRestore.get(id);
|
|
1044
|
-
if (snapshot !== void 0) chain.setUniforms(id, snapshot);
|
|
1045
|
-
chain.setEnabled(id, false);
|
|
1046
|
-
timedRemaining.delete(id);
|
|
1047
|
-
timedRestore.delete(id);
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
},
|
|
1052
|
-
buildPass(width, height) {
|
|
1053
|
-
return runtime.buildPass(chain, width, height);
|
|
1054
|
-
},
|
|
1055
|
-
setMotionScale(k) {
|
|
1056
|
-
chain.setGlobalIntensity(k);
|
|
1057
|
-
}
|
|
1058
|
-
};
|
|
1059
|
-
}
|
|
1060
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1061
|
-
0 && (module.exports = {
|
|
1062
|
-
BLOOM_SKSL,
|
|
1063
|
-
CHROMATIC_ABERRATION_SKSL,
|
|
1064
|
-
COLOR_GRADING_LUT_SKSL,
|
|
1065
|
-
COMPOSITE_SKSL,
|
|
1066
|
-
CRT_SKSL,
|
|
1067
|
-
EffectChain,
|
|
1068
|
-
GOD_RAYS_SKSL,
|
|
1069
|
-
GRAYSCALE_FLASH_SKSL,
|
|
1070
|
-
VIGNETTE_SKSL,
|
|
1071
|
-
WEATHER_CLOUDS_SKSL,
|
|
1072
|
-
WEATHER_DARK_VIGNETTE_SKSL,
|
|
1073
|
-
WEATHER_RAIN_SKSL,
|
|
1074
|
-
WEATHER_SUN_SHAFT_SKSL,
|
|
1075
|
-
createBloom,
|
|
1076
|
-
createChromaticAberration,
|
|
1077
|
-
createColorGradingLut,
|
|
1078
|
-
createCrt,
|
|
1079
|
-
createGodRays,
|
|
1080
|
-
createGrayscaleFlash,
|
|
1081
|
-
createNeutralLutBytes,
|
|
1082
|
-
createPostFxComposite,
|
|
1083
|
-
createPostFxController,
|
|
1084
|
-
createPostFxRuntime,
|
|
1085
|
-
createVignette,
|
|
1086
|
-
createWeatherClouds,
|
|
1087
|
-
createWeatherDarkVignette,
|
|
1088
|
-
createWeatherRain,
|
|
1089
|
-
createWeatherSunShaft
|
|
1090
|
-
});
|
|
1091
1081
|
//# sourceMappingURL=index.js.map
|