1use alpha2_gpu::CapabilityTier;
2use alpha2_shaders::{
3 ANGULAR, CHROMA_KEY_BODY, CIRCLE, CIRCLE_CROP, CIRCLE_OPEN, COLOR_CONTROLS, COLOR_PHASE,
4 COLOUR_DISTANCE, CROP, CROSS_WARP, DIRECTIONAL, DIRECTIONAL_WARP, DIRECTIONAL_WIPE, DREAMY,
5 EXPANDED_EFFECT, FADE, FADE_COLOR, FADE_GRAYSCALE, FILM_GRAIN, GAUSSIAN_BLUR, GLOW_COMBINE,
6 GLOW_THRESHOLD, HEART, LUT_3D_BODY, MIRROR, MORPH, PINWHEEL, PIXELATE_EFFECT, PIXELIZE,
7 POLKA_DOTS_CURTAIN, RADIAL, RIPPLE, SHARPEN, SIMPLE_ZOOM, SQUEEZE, TRANSITION_HELPERS,
8 VIGNETTE, WIND, WINDOW_BLINDS, WINDOW_SLICE, WIPE_DOWN, WIPE_LEFT, WIPE_RIGHT, WIPE_UP,
9};
10
11use crate::{
12 EffectDef, EffectId, EffectKind, EffectResourceKind, ParamSpec, ParamType, ParamUnit,
13 ParamValue, Renderer, ResourceSpec, ShaderDiagnostics,
14};
15
16pub const CHROMA_KEY_ID: &str = "alpha2.effect.chroma-key";
18
19pub const GAUSSIAN_BLUR_ID: &str = "alpha2.effect.gaussian-blur";
21
22pub(crate) const GAUSSIAN_BLUR_PASS_ID: &str = "alpha2.internal.gaussian-blur-pass";
24
25pub const GLOW_ID: &str = "alpha2.effect.glow";
27
28pub(crate) const GLOW_THRESHOLD_ID: &str = "alpha2.internal.glow-threshold";
30
31pub(crate) const GLOW_COMBINE_ID: &str = "alpha2.internal.glow-combine";
33
34pub(crate) const ALPHA2_EFFECT_LICENSE: &str = "MIT; Copyright Alpha2 contributors";
36
37pub const LUT_3D_ID: &str = "alpha2.effect.lut-3d";
39
40#[must_use]
42pub fn builtin_effect_definitions() -> Vec<EffectDef> {
43 vec![
44 gaussian_blur_definition(),
45 chroma_key_definition(),
46 lut_3d_definition(),
47 simple_effect(
48 "color-controls",
49 COLOR_CONTROLS,
50 vec![
51 float("exposure", 0.0, -3.0, 3.0),
52 float("contrast", 1.0, 0.0, 2.0),
53 float("saturation", 1.0, 0.0, 2.0),
54 float("temperature", 0.0, -1.0, 1.0),
55 float("tint", 0.0, -1.0, 1.0),
56 ],
57 ),
58 simple_effect(
59 "vignette",
60 VIGNETTE,
61 vec![
62 float("amount", 0.45, 0.0, 1.0),
63 float("radius", 0.72, 0.0, 1.5),
64 float("softness", 0.45, 0.01, 1.0),
65 ],
66 ),
67 simple_effect("sharpen", SHARPEN, vec![float("amount", 0.6, 0.0, 3.0)]),
68 simple_effect(
69 "pixelate",
70 PIXELATE_EFFECT,
71 vec![float("blocks", 48.0, 2.0, 400.0)],
72 ),
73 glow_definition(),
74 simple_effect(
75 "film-grain",
76 FILM_GRAIN,
77 vec![
78 float("amount", 0.18, 0.0, 1.0),
79 float("size", 1.4, 0.5, 6.0),
80 ],
81 ),
82 simple_effect(
83 "crop",
84 CROP,
85 vec![
86 vec2_range("top_left", [0.0, 0.0], 0.0, 1.0, 0.005),
87 vec2_range("bottom_right", [0.0, 0.0], 0.0, 1.0, 0.005),
88 float_step("feather", 0.004, 0.0, 0.2, 0.002),
89 ],
90 ),
91 simple_effect(
92 "mirror",
93 MIRROR,
94 vec![
95 choice("axis", 0, &["horizontal", "vertical"]),
96 boolean("keep_second_half", false),
97 ],
98 ),
99 ]
100}
101
102const GL_TRANSITIONS_BASE: &str = "https://github.com/gl-transitions/gl-transitions/blob/902218a1b63773ac0d0d9f491951da3392365bfe/transitions/";
103
104#[must_use]
106pub fn builtin_transition_definitions() -> Vec<EffectDef> {
107 let mut definitions = classic_transition_definitions();
108 definitions.extend(wipe_transition_definitions());
109 definitions.extend(stylized_transition_definitions());
110 definitions
111}
112
113fn classic_transition_definitions() -> Vec<EffectDef> {
114 vec![
115 transition("fade", FADE, "fade.glsl", "gre", Vec::new()),
116 transition(
117 "directional",
118 DIRECTIONAL,
119 "Directional.glsl",
120 "Gaëtan Renaudeau",
121 vec![vec2_range("direction", [0.0, 1.0], -1.0, 1.0, 0.1)],
122 ),
123 transition(
124 "circle-open",
125 CIRCLE_OPEN,
126 "circleopen.glsl",
127 "gre",
128 vec![float("smoothness", 0.3, 0.0, 1.0), boolean("opening", true)],
129 ),
130 transition(
131 "angular",
132 ANGULAR,
133 "angular.glsl",
134 "Fernando Kuteken",
135 vec![degrees("starting_angle", 90.0, -360.0, 360.0)],
136 ),
137 transition(
138 "pixelize",
139 PIXELIZE,
140 "pixelize.glsl",
141 "gre",
142 vec![
143 vec2_range("squares_min", [20.0, 20.0], 1.0, 100.0, 1.0),
144 float("steps", 50.0, 0.0, 200.0),
145 ],
146 ),
147 transition(
148 "directional-wipe",
149 DIRECTIONAL_WIPE,
150 "directionalwipe.glsl",
151 "gre",
152 vec![
153 vec2_range("direction", [1.0, -1.0], -1.0, 1.0, 0.1),
154 float("smoothness", 0.5, 0.0, 1.0),
155 ],
156 ),
157 transition(
158 "circle",
159 CIRCLE,
160 "circle.glsl",
161 "Fernando Kuteken",
162 vec![
163 vec2_range("center", [0.5, 0.5], 0.0, 1.0, 0.01),
164 color("back_color", [0.1, 0.1, 0.1, 1.0]),
165 ],
166 ),
167 transition(
168 "cross-warp",
169 CROSS_WARP,
170 "crosswarp.glsl",
171 "Eke Péter",
172 Vec::new(),
173 ),
174 transition("dreamy", DREAMY, "Dreamy.glsl", "mikolalysenko", Vec::new()),
175 transition(
176 "radial",
177 RADIAL,
178 "Radial.glsl",
179 "Xaychru",
180 vec![float("smoothness", 1.0, 0.0001, 2.0)],
181 ),
182 ]
183}
184
185fn wipe_transition_definitions() -> Vec<EffectDef> {
186 vec![
187 transition("wipe-up", WIPE_UP, "wipeUp.glsl", "Jake Nelson", Vec::new()),
188 transition(
189 "wipe-down",
190 WIPE_DOWN,
191 "wipeDown.glsl",
192 "Jake Nelson",
193 Vec::new(),
194 ),
195 transition(
196 "wipe-left",
197 WIPE_LEFT,
198 "wipeLeft.glsl",
199 "Jake Nelson",
200 Vec::new(),
201 ),
202 transition(
203 "wipe-right",
204 WIPE_RIGHT,
205 "wipeRight.glsl",
206 "Jake Nelson",
207 Vec::new(),
208 ),
209 transition(
210 "window-blinds",
211 WINDOW_BLINDS,
212 "windowblinds.glsl",
213 "Fabien Benetou",
214 Vec::new(),
215 ),
216 transition(
217 "window-slice",
218 WINDOW_SLICE,
219 "windowslice.glsl",
220 "gre",
221 vec![
222 float("count", 10.0, 1.0, 100.0),
223 float("smoothness", 0.5, 0.0, 1.0),
224 ],
225 ),
226 transition(
227 "wind",
228 WIND,
229 "wind.glsl",
230 "gre",
231 vec![float("size", 0.2, 0.0001, 1.0), boolean("reversed", false)],
232 ),
233 transition(
234 "pinwheel",
235 PINWHEEL,
236 "pinwheel.glsl",
237 "Mr Speaker",
238 vec![float("speed", 2.0, 0.0, 20.0)],
239 ),
240 transition("heart", HEART, "heart.glsl", "gre", Vec::new()),
241 ]
242}
243
244fn stylized_transition_definitions() -> Vec<EffectDef> {
245 vec![
246 transition(
247 "color-phase",
248 COLOR_PHASE,
249 "colorphase.glsl",
250 "gre",
251 vec![
252 thresholds("from_step", [0.0, 0.2, 0.4, 0.0]),
253 thresholds("to_step", [0.6, 0.8, 1.0, 1.0]),
254 ],
255 ),
256 transition(
257 "fade-color",
258 FADE_COLOR,
259 "fadecolor.glsl",
260 "gre",
261 vec![
262 color("color", [0.0, 0.0, 0.0, 1.0]),
263 float("color_phase", 0.4, 0.0, 1.0),
264 ],
265 ),
266 transition(
267 "fade-grayscale",
268 FADE_GRAYSCALE,
269 "fadegrayscale.glsl",
270 "gre",
271 vec![float("intensity", 0.3, 0.0, 1.0)],
272 ),
273 transition(
274 "morph",
275 MORPH,
276 "morph.glsl",
277 "paniq",
278 vec![float("strength", 0.1, 0.0, 1.0)],
279 ),
280 transition(
281 "ripple",
282 RIPPLE,
283 "ripple.glsl",
284 "gre",
285 vec![
286 float("amplitude", 100.0, 0.0, 200.0),
287 float("speed", 50.0, 0.0, 100.0),
288 ],
289 ),
290 transition(
291 "squeeze",
292 SQUEEZE,
293 "squeeze.glsl",
294 "gre",
295 vec![float("color_separation", 0.04, 0.0, 0.5)],
296 ),
297 transition(
298 "simple-zoom",
299 SIMPLE_ZOOM,
300 "SimpleZoom.glsl",
301 "0gust1",
302 vec![float("zoom_quickness", 0.8, 0.2, 1.0)],
303 ),
304 transition(
305 "circle-crop",
306 CIRCLE_CROP,
307 "CircleCrop.glsl",
308 "fkuteken",
309 vec![color("back_color", [0.0, 0.0, 0.0, 1.0])],
310 ),
311 transition(
312 "colour-distance",
313 COLOUR_DISTANCE,
314 "ColourDistance.glsl",
315 "P-Seebauer",
316 vec![float("power", 5.0, 0.0, 20.0)],
317 ),
318 transition(
319 "polka-dots-curtain",
320 POLKA_DOTS_CURTAIN,
321 "PolkaDotsCurtain.glsl",
322 "bobylito",
323 vec![
324 float("dots", 20.0, 1.0, 100.0),
325 vec2_range("center", [0.0, 0.0], 0.0, 1.0, 0.01),
326 ],
327 ),
328 transition(
329 "directional-warp",
330 DIRECTIONAL_WARP,
331 "directionalwarp.glsl",
332 "pschroen",
333 vec![
334 float("smoothness", 0.1, 0.0001, 1.0),
335 vec2_range("direction", [-1.0, 1.0], -1.0, 1.0, 0.1),
336 ],
337 ),
338 ]
339}
340
341#[must_use]
350pub fn chroma_key_definition() -> EffectDef {
351 EffectDef {
352 id: CHROMA_KEY_ID.into(),
353 version: 1,
354 kind: EffectKind::Effect,
355 inputs: Vec::new(),
356 params: vec![
357 color("key_color", [0.0, 1.0, 0.0, 1.0]),
358 float("threshold", 0.35, 0.0, 3.0_f32.sqrt()),
359 float("softness", 0.1, f32::EPSILON, 1.0),
360 float("spill_suppression", 0.2, 0.0, 1.0),
361 choice("distance_mode", 0, &["rgb", "chroma"]),
362 ],
363 resources: Vec::new(),
364 wgsl_body: CHROMA_KEY_BODY.into(),
365 tier_min: CapabilityTier::TierB,
366 license: Some(ALPHA2_EFFECT_LICENSE.into()),
367 }
368}
369
370#[must_use]
376pub fn gaussian_blur_definition() -> EffectDef {
377 EffectDef {
378 id: GAUSSIAN_BLUR_ID.into(),
379 version: 1,
380 kind: EffectKind::Effect,
381 inputs: Vec::new(),
382 params: vec![float_step("radius", 0.012, 0.0, 1.0, 0.001)],
383 resources: Vec::new(),
384 wgsl_body: EXPANDED_EFFECT.into(),
385 tier_min: CapabilityTier::TierB,
386 license: Some(ALPHA2_EFFECT_LICENSE.into()),
387 }
388}
389
390pub(crate) fn gaussian_blur_pass_definition() -> EffectDef {
391 EffectDef {
392 id: GAUSSIAN_BLUR_PASS_ID.into(),
393 version: 1,
394 kind: EffectKind::Effect,
395 inputs: Vec::new(),
396 params: vec![
397 vec2("step", [0.0, 0.0]),
398 float_step(
399 "sigma",
400 1.0,
401 0.0,
402 crate::gaussian_blur::MAX_PASS_SIGMA_TAPS,
403 0.001,
404 ),
405 ],
406 resources: Vec::new(),
407 wgsl_body: GAUSSIAN_BLUR.into(),
408 tier_min: CapabilityTier::TierB,
409 license: Some(ALPHA2_EFFECT_LICENSE.into()),
410 }
411}
412
413#[must_use]
420pub fn glow_definition() -> EffectDef {
421 EffectDef {
422 id: GLOW_ID.into(),
423 version: 1,
424 kind: EffectKind::Effect,
425 inputs: Vec::new(),
426 params: vec![
427 float("threshold", 0.6, 0.0, 1.0),
428 float("intensity", 0.8, 0.0, 3.0),
429 float("radius", 0.012, 0.0, 0.06),
430 ],
431 resources: Vec::new(),
432 wgsl_body: EXPANDED_EFFECT.into(),
433 tier_min: CapabilityTier::TierB,
434 license: Some(ALPHA2_EFFECT_LICENSE.into()),
435 }
436}
437
438fn glow_threshold_definition() -> EffectDef {
439 EffectDef {
440 id: GLOW_THRESHOLD_ID.into(),
441 ..simple_effect(
442 "glow-threshold",
443 GLOW_THRESHOLD,
444 vec![float("threshold", 0.6, 0.0, 1.0)],
445 )
446 }
447}
448
449fn glow_combine_definition() -> EffectDef {
452 EffectDef {
453 id: GLOW_COMBINE_ID.into(),
454 version: 1,
455 kind: EffectKind::Transition,
456 inputs: Vec::new(),
457 params: vec![float("intensity", 0.8, 0.0, 3.0)],
458 resources: Vec::new(),
459 wgsl_body: GLOW_COMBINE.into(),
460 tier_min: CapabilityTier::TierB,
461 license: Some(ALPHA2_EFFECT_LICENSE.into()),
462 }
463}
464
465#[must_use]
467pub fn lut_3d_definition() -> EffectDef {
468 EffectDef {
469 id: LUT_3D_ID.into(),
470 version: 1,
471 kind: EffectKind::Effect,
472 inputs: Vec::new(),
473 params: vec![float("strength", 1.0, 0.0, 1.0)],
474 resources: vec![ResourceSpec {
475 name: "lut".into(),
476 kind: EffectResourceKind::Lut3d,
477 }],
478 wgsl_body: LUT_3D_BODY.into(),
479 tier_min: CapabilityTier::TierB,
480 license: Some(ALPHA2_EFFECT_LICENSE.into()),
481 }
482}
483
484pub(crate) fn internal_effect_definitions() -> Vec<EffectDef> {
486 let mut definitions = vec![
487 gaussian_blur_pass_definition(),
488 glow_threshold_definition(),
489 glow_combine_definition(),
490 crate::masking::track_matte_definition(),
491 crate::generator::generator_definition(),
492 ];
493 definitions.extend(crate::masking::mask_combine_definitions());
494 definitions
495}
496
497impl Renderer {
498 async fn register_builtin_definition(
499 &mut self,
500 definition: EffectDef,
501 ) -> Result<EffectId, ShaderDiagnostics> {
502 let id = self.register_effect(definition).await?;
503 self.effects.mark_builtin(id.clone());
504 Ok(id)
505 }
506
507 pub async fn register_builtins(&mut self) -> Result<Vec<EffectId>, ShaderDiagnostics> {
515 for definition in internal_effect_definitions() {
516 let internal = self.register_builtin_definition(definition).await?;
517 self.effects.mark_internal(internal);
518 }
519 let mut definitions = builtin_effect_definitions();
520 definitions.extend(builtin_transition_definitions());
521 let mut ids = Vec::with_capacity(definitions.len());
522 for definition in definitions {
523 ids.push(self.register_builtin_definition(definition).await?);
524 }
525 Ok(ids)
526 }
527
528 pub async fn register_builtin_chroma_key(&mut self) -> Result<EffectId, ShaderDiagnostics> {
533 self.register_builtin_definition(chroma_key_definition())
534 .await
535 }
536
537 pub async fn register_builtin_gaussian_blur(&mut self) -> Result<EffectId, ShaderDiagnostics> {
542 let internal = self
543 .register_builtin_definition(gaussian_blur_pass_definition())
544 .await?;
545 self.effects.mark_internal(internal);
546 self.register_builtin_definition(gaussian_blur_definition())
547 .await
548 }
549
550 pub async fn register_builtin_lut_3d(&mut self) -> Result<EffectId, ShaderDiagnostics> {
555 self.register_builtin_definition(lut_3d_definition()).await
556 }
557
558 pub async fn register_builtin_transitions(
564 &mut self,
565 ) -> Result<Vec<EffectId>, ShaderDiagnostics> {
566 let definitions = builtin_transition_definitions();
567 let mut ids = Vec::with_capacity(definitions.len());
568 for definition in definitions {
569 ids.push(self.register_builtin_definition(definition).await?);
570 }
571 Ok(ids)
572 }
573}
574
575fn simple_effect(name: &str, body: &str, params: Vec<ParamSpec>) -> EffectDef {
576 EffectDef {
577 id: format!("alpha2.effect.{name}"),
578 version: 1,
579 kind: EffectKind::Effect,
580 inputs: Vec::new(),
581 params,
582 resources: Vec::new(),
583 wgsl_body: body.into(),
584 tier_min: CapabilityTier::TierB,
585 license: Some(ALPHA2_EFFECT_LICENSE.into()),
586 }
587}
588
589fn transition(
590 name: &str,
591 body: &str,
592 source: &str,
593 author: &str,
594 params: Vec<ParamSpec>,
595) -> EffectDef {
596 EffectDef {
597 id: format!("alpha2.transition.{name}"),
598 version: 1,
599 kind: EffectKind::Transition,
600 inputs: Vec::new(),
601 params,
602 resources: Vec::new(),
603 wgsl_body: format!("{TRANSITION_HELPERS}\n{body}"),
604 tier_min: CapabilityTier::TierB,
605 license: Some(format!(
606 "MIT; Author: {author}; Source: {GL_TRANSITIONS_BASE}{source}"
607 )),
608 }
609}
610
611fn float(name: &str, default: f32, min: f32, max: f32) -> ParamSpec {
612 let span = max - min;
613 let step = if span > 50.0 {
614 1.0
615 } else if span <= 0.1 {
616 0.001
617 } else {
618 0.01
619 };
620 float_step(name, default, min, max, step)
621}
622
623fn float_step(name: &str, default: f32, min: f32, max: f32, step: f32) -> ParamSpec {
624 ParamSpec {
625 name: name.into(),
626 param_type: ParamType::Float,
627 default: ParamValue::Float(default),
628 min: Some(min),
629 max: Some(max),
630 step: Some(step),
631 unit: ParamUnit::Normalized,
632 enum_values: Vec::new(),
633 }
634}
635
636fn degrees(name: &str, default: f32, min: f32, max: f32) -> ParamSpec {
637 ParamSpec {
638 unit: ParamUnit::Degrees,
639 step: Some(1.0),
640 ..float(name, default, min, max)
641 }
642}
643
644fn vec2(name: &str, default: [f32; 2]) -> ParamSpec {
645 ParamSpec {
646 name: name.into(),
647 param_type: ParamType::Vec2,
648 default: ParamValue::Vec2(default),
649 min: None,
650 max: None,
651 step: None,
652 unit: ParamUnit::Normalized,
653 enum_values: Vec::new(),
654 }
655}
656
657fn vec2_range(name: &str, default: [f32; 2], min: f32, max: f32, step: f32) -> ParamSpec {
658 ParamSpec {
659 min: Some(min),
660 max: Some(max),
661 step: Some(step),
662 ..vec2(name, default)
663 }
664}
665
666fn color(name: &str, default: [f32; 4]) -> ParamSpec {
667 ParamSpec {
668 name: name.into(),
669 param_type: ParamType::Color,
670 default: ParamValue::Color(default),
671 min: None,
672 max: None,
673 step: None,
674 unit: ParamUnit::Color,
675 enum_values: Vec::new(),
676 }
677}
678
679fn thresholds(name: &str, default: [f32; 4]) -> ParamSpec {
681 ParamSpec {
682 name: name.into(),
683 param_type: ParamType::Vec4,
684 default: ParamValue::Vec4(default),
685 min: Some(0.0),
686 max: Some(1.0),
687 step: Some(0.01),
688 unit: ParamUnit::Normalized,
689 enum_values: Vec::new(),
690 }
691}
692
693fn choice(name: &str, default: u32, values: &[&str]) -> ParamSpec {
694 ParamSpec {
695 name: name.into(),
696 param_type: ParamType::Enum,
697 default: ParamValue::Enum(default),
698 min: None,
699 max: None,
700 step: None,
701 unit: ParamUnit::Enum,
702 enum_values: values.iter().map(|value| (*value).into()).collect(),
703 }
704}
705
706fn boolean(name: &str, default: bool) -> ParamSpec {
707 ParamSpec {
708 name: name.into(),
709 param_type: ParamType::Bool,
710 default: ParamValue::Bool(default),
711 min: None,
712 max: None,
713 step: None,
714 unit: ParamUnit::Bool,
715 enum_values: Vec::new(),
716 }
717}