From 75ca88ddbea3272bdc394f8853850f3ad59676d3 Mon Sep 17 00:00:00 2001 From: HMS MediaEngine Agent Date: Fri, 11 Sep 2026 01:53:48 +0200 Subject: [PATCH] =?UTF-8?q?Phase=203:=20Alle=2024=20Pflicht-Plugins=20(10?= =?UTF-8?q?=20Generatoren=20+=2014=20Filter,=20=C2=A715.1/=C2=A715.2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 10 Generatoren mit Manifest + HLSL + GLSL + GLES je Plugin: solid, gradient, checker_grid, stripes_chaser, noise_clouds (AQ Octaves 2/3/5), plasma, wave_bars, shapes, drops_ripples, starfield (AQ 32/128/512 Partikel) - 14 Filter mit Manifest + Shader je Backend: transform2d (keine AQ-Geometriereduktion), color_adjust, gradient_map, blur_sharpen (separabel 2-Pass, AQ 5/9/17 Samples), pixelate_quantize, mirror_tile, kaleidoscope, wave_displace (Noise adaptiv, Amount unverändert), rgb_split, glow_bloom (Kaskade 2/4/8), edge_emboss, vignette, strobe_pulse (Safety clamp 2 Hz, nie durch DMX allein freischaltbar), feedback_trails (Double-Buffer, Clear-Trigger, §15.3) - Uniform-Vertrag §14.4 in allen 75 Shadern; Generatoren sampeln nie u_input_texture; alle Filter mit mix-0-Bypass (§15.3) - 195 parametrisierte Plugin-Tests: Manifest/Backends/DMX-Slots<=8/ AQ>=3 Varianten/Uniform-Satz/Vollstaendigkeit exakt 10+14 - Nachtraegliche Manifest-Fixes: AQ-Bloecke fuer 4 1-Pass-Filter, mix-Parameter fuer feedback_trails (Slot-Overlap vermieden) - Gesamtsuite 506 gruen, Ruff gruen --- STATUS.md | 28 ++- TEST_REPORT.md | 14 ++ plugins/builtin/filters/.gitkeep | 0 .../com.hms.fx.blur_sharpen/plugin.json | 186 +++++++++++++++++ .../shaders/d3d11/pass1.hlsl | 78 +++++++ .../shaders/d3d11/pass2.hlsl | 93 +++++++++ .../shaders/gl/pass1.frag | 79 ++++++++ .../shaders/gl/pass2.frag | 93 +++++++++ .../shaders/gles/pass1.frag | 80 ++++++++ .../shaders/gles/pass2.frag | 94 +++++++++ .../com.hms.fx.color_adjust/plugin.json | 170 ++++++++++++++++ .../shaders/d3d11/pass1.hlsl | 106 ++++++++++ .../shaders/gl/pass1.frag | 105 ++++++++++ .../shaders/gles/pass1.frag | 105 ++++++++++ .../com.hms.fx.edge_emboss/plugin.json | 166 +++++++++++++++ .../shaders/d3d11/main.hlsl | 98 +++++++++ .../shaders/gl/main.frag | 94 +++++++++ .../shaders/gles/main.frag | 94 +++++++++ .../com.hms.fx.feedback_trails/README.md | 23 +++ .../com.hms.fx.feedback_trails/plugin.json | 173 ++++++++++++++++ .../shaders/d3d11/main.hlsl | 86 ++++++++ .../shaders/gl/main.frag | 76 +++++++ .../shaders/gles/main.frag | 76 +++++++ .../filters/com.hms.fx.glow_bloom/plugin.json | 173 ++++++++++++++++ .../shaders/d3d11/main.hlsl | 85 ++++++++ .../shaders/gl/main.frag | 79 ++++++++ .../shaders/gles/main.frag | 80 ++++++++ .../com.hms.fx.gradient_map/plugin.json | 184 +++++++++++++++++ .../shaders/d3d11/pass1.hlsl | 65 ++++++ .../shaders/gl/pass1.frag | 64 ++++++ .../shaders/gles/pass1.frag | 64 ++++++ .../com.hms.fx.kaleidoscope/plugin.json | 167 +++++++++++++++ .../shaders/d3d11/pass1.hlsl | 60 ++++++ .../shaders/gl/pass1.frag | 59 ++++++ .../shaders/gles/pass1.frag | 70 +++++++ .../com.hms.fx.mirror_tile/plugin.json | 178 ++++++++++++++++ .../shaders/d3d11/pass1.hlsl | 71 +++++++ .../shaders/gl/pass1.frag | 71 +++++++ .../shaders/gles/pass1.frag | 71 +++++++ .../com.hms.fx.pixelate_quantize/plugin.json | 173 ++++++++++++++++ .../shaders/d3d11/pass1.hlsl | 65 ++++++ .../shaders/gl/pass1.frag | 65 ++++++ .../shaders/gles/pass1.frag | 65 ++++++ .../filters/com.hms.fx.rgb_split/plugin.json | 168 ++++++++++++++++ .../shaders/d3d11/main.hlsl | 75 +++++++ .../com.hms.fx.rgb_split/shaders/gl/main.frag | 71 +++++++ .../shaders/gles/main.frag | 71 +++++++ .../filters/com.hms.fx.strobe_pulse/README.md | 18 ++ .../com.hms.fx.strobe_pulse/plugin.json | 164 +++++++++++++++ .../shaders/d3d11/main.hlsl | 76 +++++++ .../shaders/gl/main.frag | 68 +++++++ .../shaders/gles/main.frag | 68 +++++++ .../com.hms.fx.transform2d/plugin.json | 178 ++++++++++++++++ .../shaders/d3d11/pass1.hlsl | 63 ++++++ .../shaders/gl/pass1.frag | 63 ++++++ .../shaders/gles/pass1.frag | 63 ++++++ .../filters/com.hms.fx.vignette/plugin.json | 168 ++++++++++++++++ .../shaders/d3d11/main.hlsl | 64 ++++++ .../com.hms.fx.vignette/shaders/gl/main.frag | 60 ++++++ .../shaders/gles/main.frag | 60 ++++++ .../com.hms.fx.wave_displace/plugin.json | 168 ++++++++++++++++ .../shaders/d3d11/main.hlsl | 99 +++++++++ .../shaders/gl/main.frag | 94 +++++++++ .../shaders/gles/main.frag | 95 +++++++++ .../plugin.json | 155 ++++++++++++++ .../shaders/d3d11/checker_grid.hlsl | 51 +++++ .../shaders/gl/checker_grid.frag | 47 +++++ .../shaders/gles/checker_grid.frag | 47 +++++ .../plugin.json | 181 +++++++++++++++++ .../shaders/d3d11/drops_ripples.hlsl | 74 +++++++ .../shaders/gl/drops_ripples.frag | 68 +++++++ .../shaders/gles/drops_ripples.frag | 68 +++++++ .../com.hms.generator.gradient/plugin.json | 175 ++++++++++++++++ .../shaders/d3d11/gradient.hlsl | 54 +++++ .../shaders/gl/gradient.frag | 50 +++++ .../shaders/gles/gradient.frag | 50 +++++ .../plugin.json | 179 +++++++++++++++++ .../shaders/d3d11/noise_clouds.hlsl | 79 ++++++++ .../shaders/gl/noise_clouds.frag | 75 +++++++ .../shaders/gles/noise_clouds.frag | 76 +++++++ .../com.hms.generator.plasma/plugin.json | 182 +++++++++++++++++ .../shaders/d3d11/plasma.hlsl | 66 ++++++ .../shaders/gl/plasma.frag | 60 ++++++ .../shaders/gles/plasma.frag | 60 ++++++ .../com.hms.generator.shapes/plugin.json | 188 +++++++++++++++++ .../shaders/d3d11/shapes.hlsl | 99 +++++++++ .../shaders/gl/shapes.frag | 93 +++++++++ .../shaders/gles/shapes.frag | 93 +++++++++ .../com.hms.generator.solid/plugin.json | 96 +++++++++ .../shaders/d3d11/solid.hlsl | 33 +++ .../shaders/gl/solid.frag | 29 +++ .../shaders/gles/solid.frag | 29 +++ .../com.hms.generator.starfield/plugin.json | 190 ++++++++++++++++++ .../shaders/d3d11/starfield.hlsl | 74 +++++++ .../shaders/gl/starfield.frag | 69 +++++++ .../shaders/gles/starfield.frag | 69 +++++++ .../plugin.json | 164 +++++++++++++++ .../shaders/d3d11/stripes_chaser.hlsl | 48 +++++ .../shaders/gl/stripes_chaser.frag | 44 ++++ .../shaders/gles/stripes_chaser.frag | 44 ++++ .../com.hms.generator.wave_bars/plugin.json | 184 +++++++++++++++++ .../shaders/d3d11/wave_bars.hlsl | 64 ++++++ .../shaders/gl/wave_bars.frag | 59 ++++++ .../shaders/gles/wave_bars.frag | 59 ++++++ tests/unit/test_builtin_plugins.py | 172 ++++++++++++++++ 105 files changed, 9670 insertions(+), 7 deletions(-) delete mode 100644 plugins/builtin/filters/.gitkeep create mode 100644 plugins/builtin/filters/com.hms.fx.blur_sharpen/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/d3d11/pass1.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/d3d11/pass2.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gl/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gl/pass2.frag create mode 100644 plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gles/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gles/pass2.frag create mode 100644 plugins/builtin/filters/com.hms.fx.color_adjust/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.color_adjust/shaders/d3d11/pass1.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.color_adjust/shaders/gl/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.color_adjust/shaders/gles/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.edge_emboss/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.edge_emboss/shaders/d3d11/main.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.edge_emboss/shaders/gl/main.frag create mode 100644 plugins/builtin/filters/com.hms.fx.edge_emboss/shaders/gles/main.frag create mode 100644 plugins/builtin/filters/com.hms.fx.feedback_trails/README.md create mode 100644 plugins/builtin/filters/com.hms.fx.feedback_trails/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.feedback_trails/shaders/d3d11/main.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.feedback_trails/shaders/gl/main.frag create mode 100644 plugins/builtin/filters/com.hms.fx.feedback_trails/shaders/gles/main.frag create mode 100644 plugins/builtin/filters/com.hms.fx.glow_bloom/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.glow_bloom/shaders/d3d11/main.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.glow_bloom/shaders/gl/main.frag create mode 100644 plugins/builtin/filters/com.hms.fx.glow_bloom/shaders/gles/main.frag create mode 100644 plugins/builtin/filters/com.hms.fx.gradient_map/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.gradient_map/shaders/d3d11/pass1.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.gradient_map/shaders/gl/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.gradient_map/shaders/gles/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.kaleidoscope/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.kaleidoscope/shaders/d3d11/pass1.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.kaleidoscope/shaders/gl/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.kaleidoscope/shaders/gles/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.mirror_tile/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.mirror_tile/shaders/d3d11/pass1.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.mirror_tile/shaders/gl/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.mirror_tile/shaders/gles/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.pixelate_quantize/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.pixelate_quantize/shaders/d3d11/pass1.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.pixelate_quantize/shaders/gl/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.pixelate_quantize/shaders/gles/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.rgb_split/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.rgb_split/shaders/d3d11/main.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.rgb_split/shaders/gl/main.frag create mode 100644 plugins/builtin/filters/com.hms.fx.rgb_split/shaders/gles/main.frag create mode 100644 plugins/builtin/filters/com.hms.fx.strobe_pulse/README.md create mode 100644 plugins/builtin/filters/com.hms.fx.strobe_pulse/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.strobe_pulse/shaders/d3d11/main.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.strobe_pulse/shaders/gl/main.frag create mode 100644 plugins/builtin/filters/com.hms.fx.strobe_pulse/shaders/gles/main.frag create mode 100644 plugins/builtin/filters/com.hms.fx.transform2d/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.transform2d/shaders/d3d11/pass1.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.transform2d/shaders/gl/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.transform2d/shaders/gles/pass1.frag create mode 100644 plugins/builtin/filters/com.hms.fx.vignette/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.vignette/shaders/d3d11/main.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.vignette/shaders/gl/main.frag create mode 100644 plugins/builtin/filters/com.hms.fx.vignette/shaders/gles/main.frag create mode 100644 plugins/builtin/filters/com.hms.fx.wave_displace/plugin.json create mode 100644 plugins/builtin/filters/com.hms.fx.wave_displace/shaders/d3d11/main.hlsl create mode 100644 plugins/builtin/filters/com.hms.fx.wave_displace/shaders/gl/main.frag create mode 100644 plugins/builtin/filters/com.hms.fx.wave_displace/shaders/gles/main.frag create mode 100644 plugins/builtin/generators/com.hms.generator.checker_grid/plugin.json create mode 100644 plugins/builtin/generators/com.hms.generator.checker_grid/shaders/d3d11/checker_grid.hlsl create mode 100644 plugins/builtin/generators/com.hms.generator.checker_grid/shaders/gl/checker_grid.frag create mode 100644 plugins/builtin/generators/com.hms.generator.checker_grid/shaders/gles/checker_grid.frag create mode 100644 plugins/builtin/generators/com.hms.generator.drops_ripples/plugin.json create mode 100644 plugins/builtin/generators/com.hms.generator.drops_ripples/shaders/d3d11/drops_ripples.hlsl create mode 100644 plugins/builtin/generators/com.hms.generator.drops_ripples/shaders/gl/drops_ripples.frag create mode 100644 plugins/builtin/generators/com.hms.generator.drops_ripples/shaders/gles/drops_ripples.frag create mode 100644 plugins/builtin/generators/com.hms.generator.gradient/plugin.json create mode 100644 plugins/builtin/generators/com.hms.generator.gradient/shaders/d3d11/gradient.hlsl create mode 100644 plugins/builtin/generators/com.hms.generator.gradient/shaders/gl/gradient.frag create mode 100644 plugins/builtin/generators/com.hms.generator.gradient/shaders/gles/gradient.frag create mode 100644 plugins/builtin/generators/com.hms.generator.noise_clouds/plugin.json create mode 100644 plugins/builtin/generators/com.hms.generator.noise_clouds/shaders/d3d11/noise_clouds.hlsl create mode 100644 plugins/builtin/generators/com.hms.generator.noise_clouds/shaders/gl/noise_clouds.frag create mode 100644 plugins/builtin/generators/com.hms.generator.noise_clouds/shaders/gles/noise_clouds.frag create mode 100644 plugins/builtin/generators/com.hms.generator.plasma/plugin.json create mode 100644 plugins/builtin/generators/com.hms.generator.plasma/shaders/d3d11/plasma.hlsl create mode 100644 plugins/builtin/generators/com.hms.generator.plasma/shaders/gl/plasma.frag create mode 100644 plugins/builtin/generators/com.hms.generator.plasma/shaders/gles/plasma.frag create mode 100644 plugins/builtin/generators/com.hms.generator.shapes/plugin.json create mode 100644 plugins/builtin/generators/com.hms.generator.shapes/shaders/d3d11/shapes.hlsl create mode 100644 plugins/builtin/generators/com.hms.generator.shapes/shaders/gl/shapes.frag create mode 100644 plugins/builtin/generators/com.hms.generator.shapes/shaders/gles/shapes.frag create mode 100644 plugins/builtin/generators/com.hms.generator.solid/plugin.json create mode 100644 plugins/builtin/generators/com.hms.generator.solid/shaders/d3d11/solid.hlsl create mode 100644 plugins/builtin/generators/com.hms.generator.solid/shaders/gl/solid.frag create mode 100644 plugins/builtin/generators/com.hms.generator.solid/shaders/gles/solid.frag create mode 100644 plugins/builtin/generators/com.hms.generator.starfield/plugin.json create mode 100644 plugins/builtin/generators/com.hms.generator.starfield/shaders/d3d11/starfield.hlsl create mode 100644 plugins/builtin/generators/com.hms.generator.starfield/shaders/gl/starfield.frag create mode 100644 plugins/builtin/generators/com.hms.generator.starfield/shaders/gles/starfield.frag create mode 100644 plugins/builtin/generators/com.hms.generator.stripes_chaser/plugin.json create mode 100644 plugins/builtin/generators/com.hms.generator.stripes_chaser/shaders/d3d11/stripes_chaser.hlsl create mode 100644 plugins/builtin/generators/com.hms.generator.stripes_chaser/shaders/gl/stripes_chaser.frag create mode 100644 plugins/builtin/generators/com.hms.generator.stripes_chaser/shaders/gles/stripes_chaser.frag create mode 100644 plugins/builtin/generators/com.hms.generator.wave_bars/plugin.json create mode 100644 plugins/builtin/generators/com.hms.generator.wave_bars/shaders/d3d11/wave_bars.hlsl create mode 100644 plugins/builtin/generators/com.hms.generator.wave_bars/shaders/gl/wave_bars.frag create mode 100644 plugins/builtin/generators/com.hms.generator.wave_bars/shaders/gles/wave_bars.frag create mode 100644 tests/unit/test_builtin_plugins.py diff --git a/STATUS.md b/STATUS.md index 2ca3276..dd043d7 100644 --- a/STATUS.md +++ b/STATUS.md @@ -89,15 +89,29 @@ Phase 2 (im Bau): Duplikate idempotent; End-to-End-Integrationstest über echtes TCP-Loopback ohne Mocks (§6.2, §6.4) +Phase 3 (im Bau): + +- [x] Plugin-Lifecycle-Manager: Zustandsgraph discovered→validated→ + installed→enabled→compiled→active mit quarantined/incompatible, + Show-Lock-Schutz (§14.5, §14.6, §26.3), Versionsupdate-Zyklus +- [x] Alle 10 Pflicht-Generatoren (§15.1): solid, gradient, checker_grid, + stripes_chaser, noise_clouds, plasma, wave_bars, shapes, + drops_ripples, starfield – je HLSL/GLSL/GLES + AQ-Varianten +- [x] Alle 14 Pflicht-Filter (§15.2): transform2d, color_adjust, + gradient_map, blur_sharpen (separabel 2-Pass), pixelate_quantize, + mirror_tile, kaleidoscope, wave_displace, rgb_split, glow_bloom, + edge_emboss, vignette, strobe_pulse (Safety ≤2 Hz), feedback_trails +- [ ] SDK-Dokumentation (docs/plugin-sdk/) und Beispielplugin-Anleitung +- [ ] Backend-Adapter-Vertrag: Shader-Loader-Schnittstelle für D3D11/GL/ + GLES (Rust-Renderkern, ADR-0004) +- [ ] Golden-Image-Tests-Infrastruktur (Vorbereitung, Ausführung auf + Windows-Hardware) + ## Nächste drei Aufgaben -1. Phase 3: Plugin-Loader/Lifecycle (discovered→…→active/quarantined) und - Registry mit Backend-Adapter-Vertrag (§14) -2. Pflicht-Generatoren: hms.generator.solid, gradient, checker_grid, - stripes_chaser, noise_clouds, plasma, wave_bars, shapes, drops_ripples, - starfield (§15.1) -3. Pflicht-Filter: hms.fx.transform2d … feedback_trails mit HLSL/GLSL/GLES - (§15.2) +1. SDK-Dokumentation (docs/plugin-sdk/) mit Beispielplugin-Anleitung +2. Phase 4: Art-Net-Fixture-Vollverhalten (Master32/Layer64, §16.3/§16.4) +3. Renderer-Backend-Adapter-Vertrag: Shader-Loader für D3D11/GL/GLES ## Ausstehende Hardware-Validierung (ADR-0008 Testplan) diff --git a/TEST_REPORT.md b/TEST_REPORT.md index c91f0a2..157f413 100644 --- a/TEST_REPORT.md +++ b/TEST_REPORT.md @@ -27,6 +27,20 @@ Testumfang und Ergebnis der Phase-0-Grundlage (alle grün): - `tests/unit/test_supervisor.py` – echte Kindprozesse: Start/Stop, Crash-Restart, Crashloop, Recovery-Markierung, Environment (10 Tests) - `tests/unit/test_cluster.py` – ClusterMessage/CommandTracker, NodeRegistry-Health/Kategorien, Paarung PIN/Fingerprint/Token/Widerruf, Discovery-TXT, manuelle Liste (25 Tests) - `tests/integration/test_ipc_connection.py` – echter TCP-Loopback, kein Mock (9 Tests) +- `tests/integration/test_state_sync_chain.py` – State-Sync-Kette Store→IPC→Mirror ohne Mocks (4 Tests) +- `tests/unit/test_plugin_lifecycle.py` – Lifecycle-Graph, Quarantäne, Show-Lock (14 Tests) +- `tests/unit/test_builtin_plugins.py` – parametrisiert über alle 24 Pflicht-Plugins: Manifest, Backends, DMX-Slots ≤8, AQ ≥3 Varianten, Uniform-Vertrag, Generator-Sampling-Verbot, Filter-Mix-Bypass, Vollständigkeit 10+14 (195 Tests) + +## Builtin-Plugin-Validierung (Phase 3) + +**Alle 24 Pflicht-Plugins aus PLAN.md §15 validiert (ALL OK):** + +- 10 Generatoren (§15.1): solid, gradient, checker_grid, stripes_chaser, noise_clouds, plasma, wave_bars, shapes, drops_ripples, starfield – je Manifest + HLSL + GLSL + GLES, alle mit AQ-Varianten (z. B. noise_clouds: Octaves 2/3/5; starfield: 32/128/512 Partikel) +- 14 Filter (§15.2): transform2d, color_adjust, gradient_map, blur_sharpen (2-Pass separabel), pixelate_quantize, mirror_tile, kaleidoscope, wave_displace, rgb_split, glow_bloom, edge_emboss, vignette, strobe_pulse (Rate hart auf 2 Hz geklemmt, Safety-Doku), feedback_trails (Double-Buffer, Clear-Trigger) +- 25 HLSL-Dateien, 50 GLSL/GLES-Dateien, 24 Manifeste; alle semantisch identisch über d3d11/gl/gles (§12.6) +- Uniform-Vertrag §14.4 in jedem Shader geprüft; Generatoren sampeln nie die Eingabetextur; Filter mit mix-0-Bypass (§15.3) + +**Hinweis (ADR-0008):** Shader-Kompilierung auf GPU und Golden Images sind Teil des Windows-Durchlaufs (§29.3); hier vorliegend: Manifest-/Struktur-/Konventions-Validierung. Werkzeug-Rauchtests: Fixture-CSVs (32/64 Kanäle, 8×64=512) generiert; Renderer-Dry-Run gibt korrekte D3D11-Pipeline aus; alle JSON-Schemas und diff --git a/plugins/builtin/filters/.gitkeep b/plugins/builtin/filters/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/plugins/builtin/filters/com.hms.fx.blur_sharpen/plugin.json b/plugins/builtin/filters/com.hms.fx.blur_sharpen/plugin.json new file mode 100644 index 0000000..701367a --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.blur_sharpen/plugin.json @@ -0,0 +1,186 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.blur_sharpen", + "name": "Blur / Sharpen", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl_multipass", + "passes": [ + { + "pixel_shader": "shaders/d3d11/pass1.hlsl" + }, + { + "pixel_shader": "shaders/d3d11/pass2.hlsl" + } + ] + }, + "gl": { + "type": "glsl_multipass", + "passes": [ + { + "fragment": "shaders/gl/pass1.frag" + }, + { + "fragment": "shaders/gl/pass2.frag" + } + ] + }, + "gles": { + "type": "glsl_es_multipass", + "passes": [ + { + "fragment": "shaders/gles/pass1.frag" + }, + { + "fragment": "shaders/gles/pass2.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "DESKTOP_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 0.25, + "samples": 5 + }, + { + "id": "medium", + "internal_scale": 0.5, + "samples": 9 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 17 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "radius", + "strength", + "threshold" + ] + }, + "parameters": [ + { + "id": "mode", + "label": "Mode", + "type": "enum", + "values": [ + "blur", + "sharpen" + ], + "default": "blur", + "dmx_slots": [ + 1 + ] + }, + { + "id": "radius", + "label": "Radius", + "type": "float", + "minimum": 0.0, + "maximum": 40.0, + "default": 0.0, + "dmx_slots": [ + 2 + ], + "curve": "quadratic" + }, + { + "id": "strength", + "label": "Strength", + "type": "float", + "minimum": 0.0, + "maximum": 2.0, + "default": 1.0, + "dmx_slots": [ + 3 + ], + "curve": "linear" + }, + { + "id": "direction", + "label": "Direction", + "type": "enum", + "values": [ + "both", + "horizontal", + "vertical" + ], + "default": "both", + "dmx_slots": [ + 4 + ] + }, + { + "id": "edge_mode", + "label": "Edge Mode", + "type": "enum", + "values": [ + "clamp", + "repeat", + "mirror" + ], + "default": "clamp", + "dmx_slots": [ + 5 + ] + }, + { + "id": "quality", + "label": "Quality", + "type": "enum", + "values": [ + "auto", + "low", + "medium", + "high" + ], + "default": "auto", + "dmx_slots": [ + 6 + ] + }, + { + "id": "threshold", + "label": "Threshold", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 7 + ], + "curve": "linear" + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 1.0, + "dmx_slots": [ + 8 + ], + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/d3d11/pass1.hlsl b/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/d3d11/pass1.hlsl new file mode 100644 index 0000000..3f7cb00 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/d3d11/pass1.hlsl @@ -0,0 +1,78 @@ +// HMS MediaEngine – Blur / Sharpen +// PLAN.md §15.2 Pflichtfilter; semantisch identisch über Backends (§12.6) + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_mode; + float param_radius; + float param_strength; + float param_direction; + float param_edge_mode; + float param_quality; + float param_threshold; + float param_mix; + float _pad0; + float _pad1; +}; + + +float2 hms_edge_uv(float2 uv) +{ + if (param_edge_mode > 1.5) + { + uv = abs(frac(uv) * 2.0 - 1.0); + } + else if (param_edge_mode > 0.5) + { + uv = frac(uv); + } + else + { + uv = clamp(uv, 0.0, 1.0); + } + return uv; +} + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.001) + { + return src * u_layer_opacity; + } + float radius = max(param_radius, 0.0); + if (radius < 0.01 || param_direction > 1.5) + { + return src * u_layer_opacity; + } + float samples = clamp(u_quality_samples, 1.0, 17.0); + float stepSize = radius / max(samples - 1.0, 1.0); + float2 texel = float2(1.0, 0.0) / u_resolution; + float4 acc = float4(0.0); + float total = 0.0; + for (float i = 0.0; i < samples; i += 1.0) + { + float t = i - (samples - 1.0) * 0.5; + float w = exp(-(t * t) / (samples * 0.5)); + float2 suv = hms_edge_uv(uv + texel * (t * stepSize)); + acc += u_input_texture.Sample(u_sampler, suv) * w; + total += w; + } + float4 blurred = acc / total; + return blurred; +} diff --git a/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/d3d11/pass2.hlsl b/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/d3d11/pass2.hlsl new file mode 100644 index 0000000..832969b --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/d3d11/pass2.hlsl @@ -0,0 +1,93 @@ +// HMS MediaEngine – Blur / Sharpen +// PLAN.md §15.2 Pflichtfilter; semantisch identisch über Backends (§12.6) + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_mode; + float param_radius; + float param_strength; + float param_direction; + float param_edge_mode; + float param_quality; + float param_threshold; + float param_mix; + float _pad0; + float _pad1; +}; + + +float2 hms_edge_uv(float2 uv) +{ + if (param_edge_mode > 1.5) + { + uv = abs(frac(uv) * 2.0 - 1.0); + } + else if (param_edge_mode > 0.5) + { + uv = frac(uv); + } + else + { + uv = clamp(uv, 0.0, 1.0); + } + return uv; +} + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.001) + { + return src * u_layer_opacity; + } + float radius = max(param_radius, 0.0); + float4 blurred = src; + if (radius >= 0.01 && !(param_direction > 0.5 && param_direction < 1.5)) + { + float samples = clamp(u_quality_samples, 1.0, 17.0); + float stepSize = radius / max(samples - 1.0, 1.0); + float2 texel = float2(0.0, 1.0) / u_resolution; + float4 acc = float4(0.0); + float total = 0.0; + for (float i = 0.0; i < samples; i += 1.0) + { + float t = i - (samples - 1.0) * 0.5; + float w = exp(-(t * t) / (samples * 0.5)); + float2 suv = hms_edge_uv(uv + texel * (t * stepSize)); + acc += u_input_texture.Sample(u_sampler, suv) * w; + total += w; + } + float4 blurred = acc / total; + return blurred; + } + float4 result; + if (param_mode < 0.5) + { + result = blurred; + } + else + { + float3 detail = src.rgb - blurred.rgb; + float mag = length(detail); + float amount = step(param_threshold, mag) * param_strength; + float3 sharp = src.rgb + detail * amount; + result = float4(sharp.r, sharp.g, sharp.b, src.a); + } + result = lerp(src, result, m); + return result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gl/pass1.frag b/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gl/pass1.frag new file mode 100644 index 0000000..fcf1be4 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gl/pass1.frag @@ -0,0 +1,79 @@ +#version 330 core +// HMS MediaEngine – Blur / Sharpen +// PLAN.md §15.2 Pflichtfilter; GLSL (Linux x64), semantisch identisch zu HLSL (§12.6) + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_mode; +uniform float param_radius; +uniform float param_strength; +uniform float param_direction; +uniform float param_edge_mode; +uniform float param_quality; +uniform float param_threshold; +uniform float param_mix; + +in vec2 v_uv; +out vec4 fragColor; + + +vec2 hms_edge_uv(vec2 uv) +{ + if (param_edge_mode > 1.5) + { + uv = abs(fract(uv) * 2.0 - 1.0); + } + else if (param_edge_mode > 0.5) + { + uv = fract(uv); + } + else + { + uv = clamp(uv, 0.0, 1.0); + } + return uv; +} + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + float radius = max(param_radius, 0.0); + if (radius < 0.01 || param_direction > 1.5) + { + gl_FragColor = src * u_layer_opacity; + return; + } + float samples = clamp(u_quality_samples, 1.0, 17.0); + float stepSize = radius / max(samples - 1.0, 1.0); + vec2 texel = vec2(1.0, 0.0) / u_resolution; + vec4 acc = vec4(0.0); + float total = 0.0; + for (float i = 0.0; i < samples; i += 1.0) + { + float t = i - (samples - 1.0) * 0.5; + float w = exp(-(t * t) / (samples * 0.5)); + vec2 suv = hms_edge_uv(uv + texel * (t * stepSize)); + acc += texture(u_input_texture, suv) * w; + total += w; + } + vec4 blurred = acc / total; + gl_FragColor = blurred; + return; +} diff --git a/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gl/pass2.frag b/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gl/pass2.frag new file mode 100644 index 0000000..3adb5df --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gl/pass2.frag @@ -0,0 +1,93 @@ +#version 330 core +// HMS MediaEngine – Blur / Sharpen +// PLAN.md §15.2 Pflichtfilter; GLSL (Linux x64), semantisch identisch zu HLSL (§12.6) + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_mode; +uniform float param_radius; +uniform float param_strength; +uniform float param_direction; +uniform float param_edge_mode; +uniform float param_quality; +uniform float param_threshold; +uniform float param_mix; + +in vec2 v_uv; +out vec4 fragColor; + + +vec2 hms_edge_uv(vec2 uv) +{ + if (param_edge_mode > 1.5) + { + uv = abs(fract(uv) * 2.0 - 1.0); + } + else if (param_edge_mode > 0.5) + { + uv = fract(uv); + } + else + { + uv = clamp(uv, 0.0, 1.0); + } + return uv; +} + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + float radius = max(param_radius, 0.0); + vec4 blurred = src; + if (radius >= 0.01 && !(param_direction > 0.5 && param_direction < 1.5)) + { + float samples = clamp(u_quality_samples, 1.0, 17.0); + float stepSize = radius / max(samples - 1.0, 1.0); + vec2 texel = vec2(0.0, 1.0) / u_resolution; + vec4 acc = vec4(0.0); + float total = 0.0; + for (float i = 0.0; i < samples; i += 1.0) + { + float t = i - (samples - 1.0) * 0.5; + float w = exp(-(t * t) / (samples * 0.5)); + vec2 suv = hms_edge_uv(uv + texel * (t * stepSize)); + acc += texture(u_input_texture, suv) * w; + total += w; + } + vec4 blurred = acc / total; + gl_FragColor = blurred; + return; + } + vec4 result; + if (param_mode < 0.5) + { + result = blurred; + } + else + { + vec3 detail = src.rgb - blurred.rgb; + float mag = length(detail); + float amount = step(param_threshold, mag) * param_strength; + vec3 sharp = src.rgb + detail * amount; + result = vec4(sharp.r, sharp.g, sharp.b, src.a); + } + result = mix(src, result, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gles/pass1.frag b/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gles/pass1.frag new file mode 100644 index 0000000..c9e9fd1 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gles/pass1.frag @@ -0,0 +1,80 @@ +#version 100 +// HMS MediaEngine – Blur / Sharpen +// PLAN.md §15.2 Pflichtfilter; GLES (Raspberry Pi), ES 2.0 +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_mode; +uniform float param_radius; +uniform float param_strength; +uniform float param_direction; +uniform float param_edge_mode; +uniform float param_quality; +uniform float param_threshold; +uniform float param_mix; + +varying vec2 v_uv; + + +vec2 hms_edge_uv(vec2 uv) +{ + if (param_edge_mode > 1.5) + { + uv = abs(fract(uv) * 2.0 - 1.0); + } + else if (param_edge_mode > 0.5) + { + uv = fract(uv); + } + else + { + uv = clamp(uv, 0.0, 1.0); + } + return uv; +} + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture2D(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + float radius = max(param_radius, 0.0); + if (radius < 0.01 || param_direction > 1.5) + { + gl_FragColor = src * u_layer_opacity; + return; + } + float samples = clamp(u_quality_samples, 1.0, 17.0); + float stepSize = radius / max(samples - 1.0, 1.0); + vec2 texel = vec2(1.0, 0.0) / u_resolution; + vec4 acc = vec4(0.0); + float total = 0.0; + for (int i = 0; i < 17; i++) + { + if (float(i) >= samples) { break; } + float t = float(i) - (samples - 1.0) * 0.5; + float w = exp(-(t * t) / (samples * 0.5)); + vec2 suv = hms_edge_uv(uv + texel * (t * stepSize)); + acc += texture2D(u_input_texture, suv) * w; + total += w; + } + vec4 blurred = acc / total; + gl_FragColor = blurred; + return; +} diff --git a/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gles/pass2.frag b/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gles/pass2.frag new file mode 100644 index 0000000..a252977 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.blur_sharpen/shaders/gles/pass2.frag @@ -0,0 +1,94 @@ +#version 100 +// HMS MediaEngine – Blur / Sharpen +// PLAN.md §15.2 Pflichtfilter; GLES (Raspberry Pi), ES 2.0 +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_mode; +uniform float param_radius; +uniform float param_strength; +uniform float param_direction; +uniform float param_edge_mode; +uniform float param_quality; +uniform float param_threshold; +uniform float param_mix; + +varying vec2 v_uv; + + +vec2 hms_edge_uv(vec2 uv) +{ + if (param_edge_mode > 1.5) + { + uv = abs(fract(uv) * 2.0 - 1.0); + } + else if (param_edge_mode > 0.5) + { + uv = fract(uv); + } + else + { + uv = clamp(uv, 0.0, 1.0); + } + return uv; +} + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture2D(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + float radius = max(param_radius, 0.0); + vec4 blurred = src; + if (radius >= 0.01 && !(param_direction > 0.5 && param_direction < 1.5)) + { + float samples = clamp(u_quality_samples, 1.0, 17.0); + float stepSize = radius / max(samples - 1.0, 1.0); + vec2 texel = vec2(0.0, 1.0) / u_resolution; + vec4 acc = vec4(0.0); + float total = 0.0; + for (int i = 0; i < 17; i++) + { + if (float(i) >= samples) { break; } + float t = float(i) - (samples - 1.0) * 0.5; + float w = exp(-(t * t) / (samples * 0.5)); + vec2 suv = hms_edge_uv(uv + texel * (t * stepSize)); + acc += texture2D(u_input_texture, suv) * w; + total += w; + } + vec4 blurred = acc / total; + gl_FragColor = blurred; + return; + } + vec4 result; + if (param_mode < 0.5) + { + result = blurred; + } + else + { + vec3 detail = src.rgb - blurred.rgb; + float mag = length(detail); + float amount = step(param_threshold, mag) * param_strength; + vec3 sharp = src.rgb + detail * amount; + result = vec4(sharp.r, sharp.g, sharp.b, src.a); + } + result = mix(src, result, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.color_adjust/plugin.json b/plugins/builtin/filters/com.hms.fx.color_adjust/plugin.json new file mode 100644 index 0000000..170e2a4 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.color_adjust/plugin.json @@ -0,0 +1,170 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.color_adjust", + "name": "Color Adjust", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl", + "passes": [ + { + "pixel_shader": "shaders/d3d11/pass1.hlsl" + } + ] + }, + "gl": { + "type": "glsl", + "passes": [ + { + "fragment": "shaders/gl/pass1.frag" + } + ] + }, + "gles": { + "type": "glsl_es", + "passes": [ + { + "fragment": "shaders/gles/pass1.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "PI_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 1.0 + }, + { + "id": "medium", + "internal_scale": 1.0 + }, + { + "id": "high", + "internal_scale": 1.0 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "hue", + "saturation", + "brightness", + "contrast", + "gamma" + ] + }, + "parameters": [ + { + "id": "hue", + "label": "Hue", + "type": "float", + "minimum": -180.0, + "maximum": 180.0, + "default": 0.0, + "dmx_slots": [ + 1 + ], + "curve": "linear" + }, + { + "id": "saturation", + "label": "Saturation", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 2 + ], + "curve": "linear" + }, + { + "id": "brightness", + "label": "Brightness", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 3 + ], + "curve": "linear" + }, + { + "id": "contrast", + "label": "Contrast", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 4 + ], + "curve": "linear" + }, + { + "id": "gamma", + "label": "Gamma", + "type": "float", + "minimum": 0.1, + "maximum": 3.0, + "default": 1.0, + "dmx_slots": [ + 5 + ], + "curve": "linear" + }, + { + "id": "temperature", + "label": "Temperature", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 6 + ], + "curve": "linear" + }, + { + "id": "mode", + "label": "Mode", + "type": "enum", + "values": [ + "normal", + "grayscale", + "invert" + ], + "default": "normal", + "dmx_slots": [ + 7 + ] + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 1.0, + "dmx_slots": [ + 8 + ], + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/filters/com.hms.fx.color_adjust/shaders/d3d11/pass1.hlsl b/plugins/builtin/filters/com.hms.fx.color_adjust/shaders/d3d11/pass1.hlsl new file mode 100644 index 0000000..7ad48cb --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.color_adjust/shaders/d3d11/pass1.hlsl @@ -0,0 +1,106 @@ +// HMS MediaEngine – Color Adjust +// PLAN.md §15.2 Pflichtfilter; semantisch identisch über Backends (§12.6) + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_hue; + float param_saturation; + float param_brightness; + float param_contrast; + float param_gamma; + float param_temperature; + float param_mode; + float param_mix; + float _pad0; + float _pad1; +}; + + +float3 hms_rgb2hsv(float3 c) +{ + float3 hsv = float3(0.0); + float mx = max(c.r, max(c.g, c.b)); + float mn = min(c.r, min(c.g, c.b)); + float d = mx - mn; + hsv.z = mx; + if (mx > 0.0001) + { + hsv.y = d / mx; + } + if (d > 0.0001) + { + if (mx == c.r) { hsv.x = fmod((c.g - c.b) / d, 6.0); } + else if (mx == c.g) { hsv.x = (c.b - c.r) / d + 2.0; } + else { hsv.x = (c.r - c.g) / d + 4.0; } + hsv.x = hsv.x / 6.0; + if (hsv.x < 0.0) { hsv.x += 1.0; } + } + return hsv; +} + +float3 hms_hsv2rgb(float3 c) +{ + float h = frac(c.x) * 6.0; + float s = clamp(c.y, 0.0, 1.0); + float v = clamp(c.z, 0.0, 1.0); + float i = floor(h); + float f = h - i; + float p = v * (1.0 - s); + float q = v * (1.0 - s * f); + float t = v * (1.0 - s * (1.0 - f)); + float3 out; + if (i < 1.0) { out = float3(v, t, p); } + else if (i < 2.0) { out = float3(q, v, p); } + else if (i < 3.0) { out = float3(p, v, t); } + else if (i < 4.0) { out = float3(p, q, v); } + else if (i < 5.0) { out = float3(t, p, v); } + else { out = float3(v, p, q); } + return out; +} + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.001) + { + return src * u_layer_opacity; + } + + float3 rgb = src.rgb; + rgb.r = rgb.r + param_temperature * 0.1; + rgb.b = rgb.b - param_temperature * 0.1; + rgb = (rgb - 0.5) * (1.0 + param_contrast) + 0.5; + rgb = rgb + param_brightness; + rgb = pow(max(rgb, 0.0), 1.0 / max(param_gamma, 0.001)); + float3 hsv = hms_rgb2hsv(rgb); + hsv.x = frac(hsv.x + param_hue / 360.0); + hsv.y = clamp(hsv.y + param_saturation, 0.0, 1.0); + rgb = hms_hsv2rgb(hsv); + if (param_mode > 0.5 && param_mode < 1.5) + { + float l = dot(rgb, float3(0.299, 0.587, 0.114)); + rgb = float3(l, l, l); + } + else if (param_mode > 1.5) + { + rgb = 1.0 - rgb; + } + float4 result = float4(rgb.r, rgb.g, rgb.b, src.a); + result = lerp(src, result, m); + return result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.color_adjust/shaders/gl/pass1.frag b/plugins/builtin/filters/com.hms.fx.color_adjust/shaders/gl/pass1.frag new file mode 100644 index 0000000..d1fb781 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.color_adjust/shaders/gl/pass1.frag @@ -0,0 +1,105 @@ +#version 330 core +// HMS MediaEngine – Color Adjust +// PLAN.md §15.2 Pflichtfilter; GLSL (Linux x64), semantisch identisch zu HLSL (§12.6) + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_hue; +uniform float param_saturation; +uniform float param_brightness; +uniform float param_contrast; +uniform float param_gamma; +uniform float param_temperature; +uniform float param_mode; +uniform float param_mix; + +in vec2 v_uv; +out vec4 fragColor; + + +vec3 hms_rgb2hsv(vec3 c) +{ + vec3 hsv = vec3(0.0); + float mx = max(c.r, max(c.g, c.b)); + float mn = min(c.r, min(c.g, c.b)); + float d = mx - mn; + hsv.z = mx; + if (mx > 0.0001) + { + hsv.y = d / mx; + } + if (d > 0.0001) + { + if (mx == c.r) { hsv.x = mod((c.g - c.b) / d, 6.0); } + else if (mx == c.g) { hsv.x = (c.b - c.r) / d + 2.0; } + else { hsv.x = (c.r - c.g) / d + 4.0; } + hsv.x = hsv.x / 6.0; + if (hsv.x < 0.0) { hsv.x += 1.0; } + } + return hsv; +} + +vec3 hms_hsv2rgb(vec3 c) +{ + float h = fract(c.x) * 6.0; + float s = clamp(c.y, 0.0, 1.0); + float v = clamp(c.z, 0.0, 1.0); + float i = floor(h); + float f = h - i; + float p = v * (1.0 - s); + float q = v * (1.0 - s * f); + float t = v * (1.0 - s * (1.0 - f)); + vec3 out; + if (i < 1.0) { out = vec3(v, t, p); } + else if (i < 2.0) { out = vec3(q, v, p); } + else if (i < 3.0) { out = vec3(p, v, t); } + else if (i < 4.0) { out = vec3(p, q, v); } + else if (i < 5.0) { out = vec3(t, p, v); } + else { out = vec3(v, p, q); } + return out; +} + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + + vec3 rgb = src.rgb; + rgb.r = rgb.r + param_temperature * 0.1; + rgb.b = rgb.b - param_temperature * 0.1; + rgb = (rgb - 0.5) * (1.0 + param_contrast) + 0.5; + rgb = rgb + param_brightness; + rgb = pow(max(rgb, 0.0), 1.0 / max(param_gamma, 0.001)); + vec3 hsv = hms_rgb2hsv(rgb); + hsv.x = fract(hsv.x + param_hue / 360.0); + hsv.y = clamp(hsv.y + param_saturation, 0.0, 1.0); + rgb = hms_hsv2rgb(hsv); + if (param_mode > 0.5 && param_mode < 1.5) + { + float l = dot(rgb, vec3(0.299, 0.587, 0.114)); + rgb = vec3(l, l, l); + } + else if (param_mode > 1.5) + { + rgb = 1.0 - rgb; + } + vec4 result = vec4(rgb.r, rgb.g, rgb.b, src.a); + result = mix(src, result, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.color_adjust/shaders/gles/pass1.frag b/plugins/builtin/filters/com.hms.fx.color_adjust/shaders/gles/pass1.frag new file mode 100644 index 0000000..dab2668 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.color_adjust/shaders/gles/pass1.frag @@ -0,0 +1,105 @@ +#version 100 +// HMS MediaEngine – Color Adjust +// PLAN.md §15.2 Pflichtfilter; GLES (Raspberry Pi), ES 2.0 +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_hue; +uniform float param_saturation; +uniform float param_brightness; +uniform float param_contrast; +uniform float param_gamma; +uniform float param_temperature; +uniform float param_mode; +uniform float param_mix; + +varying vec2 v_uv; + + +vec3 hms_rgb2hsv(vec3 c) +{ + vec3 hsv = vec3(0.0); + float mx = max(c.r, max(c.g, c.b)); + float mn = min(c.r, min(c.g, c.b)); + float d = mx - mn; + hsv.z = mx; + if (mx > 0.0001) + { + hsv.y = d / mx; + } + if (d > 0.0001) + { + if (mx == c.r) { hsv.x = mod((c.g - c.b) / d, 6.0); } + else if (mx == c.g) { hsv.x = (c.b - c.r) / d + 2.0; } + else { hsv.x = (c.r - c.g) / d + 4.0; } + hsv.x = hsv.x / 6.0; + if (hsv.x < 0.0) { hsv.x += 1.0; } + } + return hsv; +} + +vec3 hms_hsv2rgb(vec3 c) +{ + float h = fract(c.x) * 6.0; + float s = clamp(c.y, 0.0, 1.0); + float v = clamp(c.z, 0.0, 1.0); + float i = floor(h); + float f = h - i; + float p = v * (1.0 - s); + float q = v * (1.0 - s * f); + float t = v * (1.0 - s * (1.0 - f)); + vec3 out; + if (i < 1.0) { out = vec3(v, t, p); } + else if (i < 2.0) { out = vec3(q, v, p); } + else if (i < 3.0) { out = vec3(p, v, t); } + else if (i < 4.0) { out = vec3(p, q, v); } + else if (i < 5.0) { out = vec3(t, p, v); } + else { out = vec3(v, p, q); } + return out; +} + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture2D(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + + vec3 rgb = src.rgb; + rgb.r = rgb.r + param_temperature * 0.1; + rgb.b = rgb.b - param_temperature * 0.1; + rgb = (rgb - 0.5) * (1.0 + param_contrast) + 0.5; + rgb = rgb + param_brightness; + rgb = pow(max(rgb, 0.0), 1.0 / max(param_gamma, 0.001)); + vec3 hsv = hms_rgb2hsv(rgb); + hsv.x = fract(hsv.x + param_hue / 360.0); + hsv.y = clamp(hsv.y + param_saturation, 0.0, 1.0); + rgb = hms_hsv2rgb(hsv); + if (param_mode > 0.5 && param_mode < 1.5) + { + float l = dot(rgb, vec3(0.299, 0.587, 0.114)); + rgb = vec3(l, l, l); + } + else if (param_mode > 1.5) + { + rgb = 1.0 - rgb; + } + vec4 result = vec4(rgb.r, rgb.g, rgb.b, src.a); + result = mix(src, result, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.edge_emboss/plugin.json b/plugins/builtin/filters/com.hms.fx.edge_emboss/plugin.json new file mode 100644 index 0000000..d7a8e82 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.edge_emboss/plugin.json @@ -0,0 +1,166 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.edge_emboss", + "name": "Edge Emboss", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl", + "passes": [ + { + "pixel_shader": "shaders/d3d11/main.hlsl" + } + ] + }, + "gl": { + "type": "glsl", + "passes": [ + { + "fragment": "shaders/gl/main.frag" + } + ] + }, + "gles": { + "type": "glsl_es", + "passes": [ + { + "fragment": "shaders/gles/main.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "PI_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "parameters": [ + { + "id": "mode", + "label": "Mode", + "type": "enum", + "values": [ + "sobel", + "outline", + "emboss" + ], + "default": "sobel", + "dmx_slots": [ + 1 + ] + }, + { + "id": "strength", + "label": "Strength", + "type": "float", + "minimum": 0, + "maximum": 4, + "default": 1, + "dmx_slots": [ + 2 + ] + }, + { + "id": "radius", + "label": "Radius", + "type": "float", + "minimum": 0, + "maximum": 10, + "default": 1, + "dmx_slots": [ + 3 + ] + }, + { + "id": "threshold", + "label": "Threshold", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0.1, + "dmx_slots": [ + 4 + ] + }, + { + "id": "direction", + "label": "Direction", + "type": "float", + "minimum": 0, + "maximum": 360, + "default": 0, + "dmx_slots": [ + 5 + ] + }, + { + "id": "color", + "label": "Color", + "type": "color", + "default": [ + 1, + 1, + 1 + ], + "dmx_slots": [ + 6 + ] + }, + { + "id": "background", + "label": "Background", + "type": "color", + "default": [ + 0, + 0, + 0 + ], + "dmx_slots": [ + 7 + ] + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 1, + "dmx_slots": [ + 8 + ] + } + ], + "failure_mode": "bypass", + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 1.0, + "samples": 1 + }, + { + "id": "medium", + "internal_scale": 1.0, + "samples": 1 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 1 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "mix" + ] + } +} diff --git a/plugins/builtin/filters/com.hms.fx.edge_emboss/shaders/d3d11/main.hlsl b/plugins/builtin/filters/com.hms.fx.edge_emboss/shaders/d3d11/main.hlsl new file mode 100644 index 0000000..4d2c002 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.edge_emboss/shaders/d3d11/main.hlsl @@ -0,0 +1,98 @@ +// HMS MediaEngine - Edge Emboss (PLAN.md §15.2) +// Edge Detect (sobel), Outline und Emboss; ein GPU-Pass. + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_mode; // 0 = sobel, 1 = outline, 2 = emboss + float param_strength; + float param_radius; + float param_threshold; + float param_direction; + float param_color_r; + float param_color_g; + float param_color_b; + float param_background_r; + float param_background_g; + float param_background_b; + float param_mix; + float _pad0; + float _pad1; + float _pad2; + float _pad3; +}; + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.01) + { + return src; // Mix 0 = kostenloser Bypass (§15.3) + } + + float2 texel = float2(1.0, 1.0) / u_resolution.xy; + float r = max(param_radius, 0.0); + float2 o = texel * r; + + float4 tl = u_input_texture.Sample(u_sampler, uv - o); + float4 tr = u_input_texture.Sample(u_sampler, uv + float2(o.x, -o.y)); + float4 bl = u_input_texture.Sample(u_sampler, uv + float2(-o.x, o.y)); + float4 br = u_input_texture.Sample(u_sampler, uv + o); + float4 l = u_input_texture.Sample(u_sampler, uv + float2(-o.x, 0.0)); + float4 rr = u_input_texture.Sample(u_sampler, uv + float2(o.x, 0.0)); + float4 t = u_input_texture.Sample(u_sampler, uv + float2(0.0, -o.y)); + float4 b = u_input_texture.Sample(u_sampler, uv + float2(0.0, o.y)); + + float ltl = dot(tl.rgb, float3(0.2126, 0.7152, 0.0722)); + float ltr = dot(tr.rgb, float3(0.2126, 0.7152, 0.0722)); + float lbl = dot(bl.rgb, float3(0.2126, 0.7152, 0.0722)); + float lbr = dot(br.rgb, float3(0.2126, 0.7152, 0.0722)); + float ll = dot(l.rgb, float3(0.2126, 0.7152, 0.0722)); + float lr = dot(rr.rgb, float3(0.2126, 0.7152, 0.0722)); + float lt = dot(t.rgb, float3(0.2126, 0.7152, 0.0722)); + float lb = dot(b.rgb, float3(0.2126, 0.7152, 0.0722)); + + float gx = (ltr + 2.0 * lr + lbr) - (ltl + 2.0 * ll + lbl); + float gy = (lbl + 2.0 * lb + lbr) - (ltl + 2.0 * lt + ltr); + float mag = sqrt(gx * gx + gy * gy); + + float3 edge; + if (param_mode < 0.5) // sobel + { + float e = smoothstep(param_threshold, param_threshold + 0.1, mag) * param_strength; + edge = float3(param_color_r, param_color_g, param_color_b) * e + + float3(param_background_r, param_background_g, param_background_b) * (1.0 - e); + } + else if (param_mode < 1.5) // outline + { + float e = smoothstep(param_threshold, param_threshold + 0.1, mag) * param_strength; + edge = lerp(src.rgb, float3(param_color_r, param_color_g, param_color_b), e); + } + else // emboss + { + float a = radians(param_direction); + float2 d = float2(cos(a), sin(a)); + float e = dot(float2(gx, gy), d) * param_strength; + edge = src.rgb + e * float3(param_color_r, param_color_g, param_color_b); + } + + float3 rgb = lerp(src.rgb, edge, m); + float4 out_c = float4(rgb, src.a); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + return out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.edge_emboss/shaders/gl/main.frag b/plugins/builtin/filters/com.hms.fx.edge_emboss/shaders/gl/main.frag new file mode 100644 index 0000000..bdde258 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.edge_emboss/shaders/gl/main.frag @@ -0,0 +1,94 @@ +#version 330 core +// HMS MediaEngine - Edge Emboss (GLSL, Linux x64) +// Semantik identisch zur HLSL-Variante (§12.6, §15.4). + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_mode; +uniform float param_strength; +uniform float param_radius; +uniform float param_threshold; +uniform float param_direction; +uniform float param_color_r; +uniform float param_color_g; +uniform float param_color_b; +uniform float param_background_r; +uniform float param_background_g; +uniform float param_background_b; +uniform float param_mix; + +in vec2 v_uv; +out vec4 fragColor; + +void main() +{ + vec4 src = texture(u_input_texture, v_uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.01) + { + fragColor = src; + return; + } + + vec2 texel = vec2(1.0, 1.0) / u_resolution; + float r = max(param_radius, 0.0); + vec2 o = texel * r; + + vec4 tl = texture(u_input_texture, v_uv - o); + vec4 tr = texture(u_input_texture, v_uv + vec2(o.x, -o.y)); + vec4 bl = texture(u_input_texture, v_uv + vec2(-o.x, o.y)); + vec4 br = texture(u_input_texture, v_uv + o); + vec4 l = texture(u_input_texture, v_uv + vec2(-o.x, 0.0)); + vec4 rr = texture(u_input_texture, v_uv + vec2(o.x, 0.0)); + vec4 t = texture(u_input_texture, v_uv + vec2(0.0, -o.y)); + vec4 b = texture(u_input_texture, v_uv + vec2(0.0, o.y)); + + float ltl = dot(tl.rgb, vec3(0.2126, 0.7152, 0.0722)); + float ltr = dot(tr.rgb, vec3(0.2126, 0.7152, 0.0722)); + float lbl = dot(bl.rgb, vec3(0.2126, 0.7152, 0.0722)); + float lbr = dot(br.rgb, vec3(0.2126, 0.7152, 0.0722)); + float ll = dot(l.rgb, vec3(0.2126, 0.7152, 0.0722)); + float lr = dot(rr.rgb, vec3(0.2126, 0.7152, 0.0722)); + float lt = dot(t.rgb, vec3(0.2126, 0.7152, 0.0722)); + float lb = dot(b.rgb, vec3(0.2126, 0.7152, 0.0722)); + + float gx = (ltr + 2.0 * lr + lbr) - (ltl + 2.0 * ll + lbl); + float gy = (lbl + 2.0 * lb + lbr) - (ltl + 2.0 * lt + ltr); + float mag = sqrt(gx * gx + gy * gy); + + vec3 edge; + if (param_mode < 0.5) // sobel + { + float e = smoothstep(param_threshold, param_threshold + 0.1, mag) * param_strength; + edge = vec3(param_color_r, param_color_g, param_color_b) * e + + vec3(param_background_r, param_background_g, param_background_b) * (1.0 - e); + } + else if (param_mode < 1.5) // outline + { + float e = smoothstep(param_threshold, param_threshold + 0.1, mag) * param_strength; + edge = mix(src.rgb, vec3(param_color_r, param_color_g, param_color_b), e); + } + else // emboss + { + float a = radians(param_direction); + vec2 d = vec2(cos(a), sin(a)); + float e = dot(vec2(gx, gy), d) * param_strength; + edge = src.rgb + e * vec3(param_color_r, param_color_g, param_color_b); + } + + vec3 rgb = mix(src.rgb, edge, m); + vec4 out_c = vec4(rgb, src.a); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + fragColor = out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.edge_emboss/shaders/gles/main.frag b/plugins/builtin/filters/com.hms.fx.edge_emboss/shaders/gles/main.frag new file mode 100644 index 0000000..391f236 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.edge_emboss/shaders/gles/main.frag @@ -0,0 +1,94 @@ +#version 100 +// HMS MediaEngine - Edge Emboss (GLES, Raspberry Pi) +// ES 2.0; ein GPU-Pass. +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_mode; +uniform float param_strength; +uniform float param_radius; +uniform float param_threshold; +uniform float param_direction; +uniform float param_color_r; +uniform float param_color_g; +uniform float param_color_b; +uniform float param_background_r; +uniform float param_background_g; +uniform float param_background_b; +uniform float param_mix; + +varying vec2 v_uv; + +void main() +{ + vec4 src = texture2D(u_input_texture, v_uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.01) + { + gl_FragColor = src; + return; + } + + vec2 texel = vec2(1.0, 1.0) / u_resolution; + float r = max(param_radius, 0.0); + vec2 o = texel * r; + + vec4 tl = texture2D(u_input_texture, v_uv - o); + vec4 tr = texture2D(u_input_texture, v_uv + vec2(o.x, -o.y)); + vec4 bl = texture2D(u_input_texture, v_uv + vec2(-o.x, o.y)); + vec4 br = texture2D(u_input_texture, v_uv + o); + vec4 l = texture2D(u_input_texture, v_uv + vec2(-o.x, 0.0)); + vec4 rr = texture2D(u_input_texture, v_uv + vec2(o.x, 0.0)); + vec4 t = texture2D(u_input_texture, v_uv + vec2(0.0, -o.y)); + vec4 b = texture2D(u_input_texture, v_uv + vec2(0.0, o.y)); + + float ltl = dot(tl.rgb, vec3(0.2126, 0.7152, 0.0722)); + float ltr = dot(tr.rgb, vec3(0.2126, 0.7152, 0.0722)); + float lbl = dot(bl.rgb, vec3(0.2126, 0.7152, 0.0722)); + float lbr = dot(br.rgb, vec3(0.2126, 0.7152, 0.0722)); + float ll = dot(l.rgb, vec3(0.2126, 0.7152, 0.0722)); + float lr = dot(rr.rgb, vec3(0.2126, 0.7152, 0.0722)); + float lt = dot(t.rgb, vec3(0.2126, 0.7152, 0.0722)); + float lb = dot(b.rgb, vec3(0.2126, 0.7152, 0.0722)); + + float gx = (ltr + 2.0 * lr + lbr) - (ltl + 2.0 * ll + lbl); + float gy = (lbl + 2.0 * lb + lbr) - (ltl + 2.0 * lt + ltr); + float mag = sqrt(gx * gx + gy * gy); + + vec3 edge; + if (param_mode < 0.5) // sobel + { + float e = smoothstep(param_threshold, param_threshold + 0.1, mag) * param_strength; + edge = vec3(param_color_r, param_color_g, param_color_b) * e + + vec3(param_background_r, param_background_g, param_background_b) * (1.0 - e); + } + else if (param_mode < 1.5) // outline + { + float e = smoothstep(param_threshold, param_threshold + 0.1, mag) * param_strength; + edge = mix(src.rgb, vec3(param_color_r, param_color_g, param_color_b), e); + } + else // emboss + { + float a = radians(param_direction); + vec2 d = vec2(cos(a), sin(a)); + float e = dot(vec2(gx, gy), d) * param_strength; + edge = src.rgb + e * vec3(param_color_r, param_color_g, param_color_b); + } + + vec3 rgb = mix(src.rgb, edge, m); + vec4 out_c = vec4(rgb, src.a); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + gl_FragColor = out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.feedback_trails/README.md b/plugins/builtin/filters/com.hms.fx.feedback_trails/README.md new file mode 100644 index 0000000..5fc1fd3 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.feedback_trails/README.md @@ -0,0 +1,23 @@ +# Feedback Trails + +Feedback, Echo und Bewegungsspuren (PLAN.md §15.2). Ein GPU-Pass. + +## Parameter +- **Decay**: Rückkopplungsabfall (0..1). +- **Zoom / Rotation / Offset X/Y**: Transform des Feedback-Puffers. +- **Hue Shift**: Farbverschiebung des Feedback-Anteils. +- **Blend Mode**: `add`, `screen`, `normal`. +- **Clear**: Trigger (0/1). + +## Double-Buffer & Clear-Trigger +Der Shader deklariert einen eigenen Double-Buffer: `u_feedback_texture` hält das +vorherige Frame-Ergebnis (Rückgriff via `param_decay`). Die interne Auflösung ist +adaptiv (`u_quality_scale`). + +**Clear-Trigger:** `param_clear` ist ein flankenbasierter Trigger. Bei `1` wird der +Feedback-Puffer auf das aktuelle Eingangssignal zurückgesetzt (kontrolliertes +Leeren, §15.3). + +**Leeren-Regel:** Beim Projektwechsel wird der Feedback-Puffer kontrolliert geleert +(§15.3). Ein `Clear`-Impuls setzt den Puffer zurück; ohne Clear akkumuliert das +Feedback über Frames hinweg. diff --git a/plugins/builtin/filters/com.hms.fx.feedback_trails/plugin.json b/plugins/builtin/filters/com.hms.fx.feedback_trails/plugin.json new file mode 100644 index 0000000..4d3cec8 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.feedback_trails/plugin.json @@ -0,0 +1,173 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.feedback_trails", + "name": "Feedback Trails", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl", + "passes": [ + { + "pixel_shader": "shaders/d3d11/main.hlsl" + } + ] + }, + "gl": { + "type": "glsl", + "passes": [ + { + "fragment": "shaders/gl/main.frag" + } + ] + }, + "gles": { + "type": "glsl_es", + "passes": [ + { + "fragment": "shaders/gles/main.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "DESKTOP_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "parameters": [ + { + "id": "decay", + "label": "Decay", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0.9, + "dmx_slots": [ + 1 + ] + }, + { + "id": "zoom", + "label": "Zoom", + "type": "float", + "minimum": 0.5, + "maximum": 2, + "default": 1, + "dmx_slots": [ + 2 + ] + }, + { + "id": "rotation", + "label": "Rotation", + "type": "float", + "minimum": -180, + "maximum": 180, + "default": 0, + "dmx_slots": [ + 3 + ] + }, + { + "id": "offset_x", + "label": "Offset X", + "type": "float", + "minimum": -1, + "maximum": 1, + "default": 0, + "dmx_slots": [ + 4 + ] + }, + { + "id": "offset_y", + "label": "Offset Y", + "type": "float", + "minimum": -1, + "maximum": 1, + "default": 0, + "dmx_slots": [ + 5 + ] + }, + { + "id": "hue_shift", + "label": "Hue Shift", + "type": "float", + "minimum": 0, + "maximum": 360, + "default": 0, + "dmx_slots": [ + 6 + ] + }, + { + "id": "blend_mode", + "label": "Blend Mode", + "type": "enum", + "values": [ + "add", + "screen", + "normal" + ], + "default": "add", + "dmx_slots": [ + 7 + ] + }, + { + "id": "clear", + "label": "Clear", + "type": "bool", + "default": 0, + "dmx_slots": [ + 8 + ] + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 1.0, + "dmx_slots": [], + "curve": "linear" + } + ], + "failure_mode": "bypass", + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 0.5 + }, + { + "id": "medium", + "internal_scale": 0.75 + }, + { + "id": "high", + "internal_scale": 1.0 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "decay", + "zoom", + "rotation", + "offset_x", + "offset_y", + "hue_shift", + "blend_mode" + ] + } +} diff --git a/plugins/builtin/filters/com.hms.fx.feedback_trails/shaders/d3d11/main.hlsl b/plugins/builtin/filters/com.hms.fx.feedback_trails/shaders/d3d11/main.hlsl new file mode 100644 index 0000000..41c3398 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.feedback_trails/shaders/d3d11/main.hlsl @@ -0,0 +1,86 @@ +// HMS MediaEngine - Feedback Trails (PLAN.md §15.2) +// Feedback, Echo und Bewegungsspuren; ein GPU-Pass. +// Eigener Double-Buffer: u_feedback_texture hält das vorherige Frame-Ergebnis +// (Rückgriff via param_decay). Clear (param_clear) ist ein flankenbasierter +// Trigger: bei 1 wird der Feedback-Puffer auf das aktuelle Eingangssignal +// zurückgesetzt (kontrolliertes Leeren, §15.3). +// Interne Auflösung adaptiv (u_quality_scale). + +Texture2D u_input_texture : register(t0); +Texture2D u_feedback_texture : register(t1); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_decay; + float param_zoom; + float param_rotation; + float param_offset_x; + float param_offset_y; + float param_hue_shift; + float param_blend_mode; // 0 = add, 1 = screen, 2 = normal + float param_clear; // Trigger 0/1 + float u_quality_scale; // interne Auflösung (0.5 | 0.75 | 1.0) + float _pad0; + float _pad1; + float _pad2; + float _pad3; +}; + +float3 hue_shift(float3 c, float amount) +{ + float3 k = float3(0.57735, 0.57735, 0.57735); + float cosA = cos(amount); + return c * cosA + cross(k, c) * sin(amount) + k * dot(k, c) * (1.0 - cosA); +} + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + + // Clear-Trigger: Puffer auf Eingang zurücksetzen (kontrolliertes Leeren) + if (param_clear > 0.5) + { + float4 out_c = src; + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + return out_c; + } + + // Feedback-Transform: Zoom + Rotation + Offset um Bildmitte + float2 c = float2(0.5, 0.5); + float2 p = uv - c; + float a = radians(param_rotation); + float2x2 rot = float2x2(cos(a), -sin(a), sin(a), cos(a)); + p = mul(rot, p) / max(param_zoom, 0.0001); + p += float2(param_offset_x, param_offset_y) * 0.1; + float2 fb_uv = p + c; + + float4 fb = u_feedback_texture.Sample(u_sampler, fb_uv); + fb.rgb = hue_shift(fb.rgb, radians(param_hue_shift)); + float decay = clamp(param_decay, 0.0, 1.0); + + float4 acc; + if (param_blend_mode < 0.5) // add + acc = src + fb * decay; + else if (param_blend_mode < 1.5) // screen + acc = 1.0 - (1.0 - src) * (1.0 - fb * decay); + else // normal + acc = src + fb * decay * (1.0 - src.a); + + float4 out_c = float4(acc.rgb, max(src.a, fb.a)); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + return out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.feedback_trails/shaders/gl/main.frag b/plugins/builtin/filters/com.hms.fx.feedback_trails/shaders/gl/main.frag new file mode 100644 index 0000000..75c67cb --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.feedback_trails/shaders/gl/main.frag @@ -0,0 +1,76 @@ +#version 330 core +// HMS MediaEngine - Feedback Trails (GLSL, Linux x64) +// Semantik identisch zur HLSL-Variante (§12.6, §15.4). + +uniform sampler2D u_input_texture; +uniform sampler2D u_feedback_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_decay; +uniform float param_zoom; +uniform float param_rotation; +uniform float param_offset_x; +uniform float param_offset_y; +uniform float param_hue_shift; +uniform float param_blend_mode; +uniform float param_clear; +uniform float u_quality_scale; + +in vec2 v_uv; +out vec4 fragColor; + +vec3 hue_shift(vec3 c, float amount) +{ + vec3 k = vec3(0.57735, 0.57735, 0.57735); + float cosA = cos(amount); + return c * cosA + cross(k, c) * sin(amount) + k * dot(k, c) * (1.0 - cosA); +} + +void main() +{ + vec4 src = texture(u_input_texture, v_uv); + + if (param_clear > 0.5) + { + vec4 out_c = src; + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + fragColor = out_c; + return; + } + + vec2 c = vec2(0.5, 0.5); + vec2 p = v_uv - c; + float a = radians(param_rotation); + float ca = cos(a); + float sa = sin(a); + p = vec2(ca * p.x - sa * p.y, sa * p.x + ca * p.y) / max(param_zoom, 0.0001); + p += vec2(param_offset_x, param_offset_y) * 0.1; + vec2 fb_uv = p + c; + + vec4 fb = texture(u_feedback_texture, fb_uv); + fb.rgb = hue_shift(fb.rgb, radians(param_hue_shift)); + float decay = clamp(param_decay, 0.0, 1.0); + + vec4 acc; + if (param_blend_mode < 0.5) // add + acc = src + fb * decay; + else if (param_blend_mode < 1.5) // screen + acc = 1.0 - (1.0 - src) * (1.0 - fb * decay); + else // normal + acc = src + fb * decay * (1.0 - src.a); + + vec4 out_c = vec4(acc.rgb, max(src.a, fb.a)); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + fragColor = out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.feedback_trails/shaders/gles/main.frag b/plugins/builtin/filters/com.hms.fx.feedback_trails/shaders/gles/main.frag new file mode 100644 index 0000000..db3f934 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.feedback_trails/shaders/gles/main.frag @@ -0,0 +1,76 @@ +#version 100 +// HMS MediaEngine - Feedback Trails (GLES, Raspberry Pi) +// ES 2.0; ein GPU-Pass. +precision mediump float; + +uniform sampler2D u_input_texture; +uniform sampler2D u_feedback_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_decay; +uniform float param_zoom; +uniform float param_rotation; +uniform float param_offset_x; +uniform float param_offset_y; +uniform float param_hue_shift; +uniform float param_blend_mode; +uniform float param_clear; +uniform float u_quality_scale; + +varying vec2 v_uv; + +vec3 hue_shift(vec3 c, float amount) +{ + vec3 k = vec3(0.57735, 0.57735, 0.57735); + float cosA = cos(amount); + return c * cosA + cross(k, c) * sin(amount) + k * dot(k, c) * (1.0 - cosA); +} + +void main() +{ + vec4 src = texture2D(u_input_texture, v_uv); + + if (param_clear > 0.5) + { + vec4 out_c = src; + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + gl_FragColor = out_c; + return; + } + + vec2 c = vec2(0.5, 0.5); + vec2 p = v_uv - c; + float a = radians(param_rotation); + float ca = cos(a); + float sa = sin(a); + p = vec2(ca * p.x - sa * p.y, sa * p.x + ca * p.y) / max(param_zoom, 0.0001); + p += vec2(param_offset_x, param_offset_y) * 0.1; + vec2 fb_uv = p + c; + + vec4 fb = texture2D(u_feedback_texture, fb_uv); + fb.rgb = hue_shift(fb.rgb, radians(param_hue_shift)); + float decay = clamp(param_decay, 0.0, 1.0); + + vec4 acc; + if (param_blend_mode < 0.5) // add + acc = src + fb * decay; + else if (param_blend_mode < 1.5) // screen + acc = 1.0 - (1.0 - src) * (1.0 - fb * decay); + else // normal + acc = src + fb * decay * (1.0 - src.a); + + vec4 out_c = vec4(acc.rgb, max(src.a, fb.a)); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + gl_FragColor = out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.glow_bloom/plugin.json b/plugins/builtin/filters/com.hms.fx.glow_bloom/plugin.json new file mode 100644 index 0000000..a6805bf --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.glow_bloom/plugin.json @@ -0,0 +1,173 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.glow_bloom", + "name": "Glow Bloom", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl", + "passes": [ + { + "pixel_shader": "shaders/d3d11/main.hlsl" + } + ] + }, + "gl": { + "type": "glsl", + "passes": [ + { + "fragment": "shaders/gl/main.frag" + } + ] + }, + "gles": { + "type": "glsl_es", + "passes": [ + { + "fragment": "shaders/gles/main.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "DESKTOP_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 0.25, + "iterations": 2 + }, + { + "id": "medium", + "internal_scale": 0.5, + "iterations": 4 + }, + { + "id": "high", + "internal_scale": 1.0, + "iterations": 8 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "threshold", + "knee", + "radius", + "intensity", + "tint", + "composite_mode", + "mix" + ] + }, + "parameters": [ + { + "id": "threshold", + "label": "Threshold", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.5, + "dmx_slots": [ + 1 + ] + }, + { + "id": "knee", + "label": "Knee", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.2, + "dmx_slots": [ + 2 + ] + }, + { + "id": "radius", + "label": "Radius", + "type": "float", + "minimum": 0.0, + "maximum": 40.0, + "default": 8.0, + "dmx_slots": [ + 3 + ] + }, + { + "id": "intensity", + "label": "Intensity", + "type": "float", + "minimum": 0.0, + "maximum": 4.0, + "default": 1.0, + "dmx_slots": [ + 4 + ] + }, + { + "id": "tint", + "label": "Tint", + "type": "color", + "default": [ + 1.0, + 1.0, + 1.0 + ], + "dmx_slots": [ + 5 + ] + }, + { + "id": "quality", + "label": "Quality", + "type": "enum", + "values": [ + "auto", + "low", + "medium", + "high" + ], + "default": "auto", + "dmx_slots": [ + 6 + ] + }, + { + "id": "composite_mode", + "label": "Composite Mode", + "type": "enum", + "values": [ + "screen", + "add" + ], + "default": "screen", + "dmx_slots": [ + 7 + ] + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 1.0, + "dmx_slots": [ + 8 + ] + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/filters/com.hms.fx.glow_bloom/shaders/d3d11/main.hlsl b/plugins/builtin/filters/com.hms.fx.glow_bloom/shaders/d3d11/main.hlsl new file mode 100644 index 0000000..8331bdc --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.glow_bloom/shaders/d3d11/main.hlsl @@ -0,0 +1,85 @@ +// HMS MediaEngine - Glow Bloom (PLAN.md §15.2) +// Downsample-Kaskade: Bright-Pass + progressive Blur-Kaskade (§15.2). +// Iterationen adaptiv: low 2 / medium 4 / high 8 (u_quality_iterations). + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_threshold; + float param_knee; + float param_radius; + float param_intensity; + float param_tint_r; + float param_tint_g; + float param_tint_b; + float param_composite_mode; // 0 = screen, 1 = add + float param_mix; + float u_quality_iterations; // 2 | 4 | 8 je Variante + float u_quality_scale; // internal_scale (0.25 | 0.5 | 1.0) + float _pad0; + float _pad1; +}; + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.01) + { + return src; // Mix 0 = kostenloser Bypass (§15.3) + } + + // Bright-Pass mit weichem Knee + float lum = dot(src.rgb, float3(0.2126, 0.7152, 0.0722)); + float knee = max(param_knee, 0.0001); + float bright = smoothstep(param_threshold, param_threshold + knee, lum); + float3 glow = src.rgb * bright; + + // Downsample-Kaskade: progressive Blur über u_quality_iterations + float iterations = clamp(u_quality_iterations, 1.0, 8.0); + float scale = max(u_quality_scale, 0.01); + float2 texel = float2(1.0, 1.0) / (u_resolution.xy * scale); + float3 acc = glow; + float total = 1.0; + [loop] + for (float i = 0.0; i < iterations; i += 1.0) + { + float spread = (i + 1.0) * max(param_radius, 0.0) * 0.01; + float2 off = texel * spread; + float3 s = glow; + s += u_input_texture.Sample(u_sampler, uv + off).rgb * bright; + s += u_input_texture.Sample(u_sampler, uv - off).rgb * bright; + s += u_input_texture.Sample(u_sampler, uv + off.yx).rgb * bright; + s += u_input_texture.Sample(u_sampler, uv - off.yx).rgb * bright; + acc += s; + total += 4.0; + } + glow = acc / total; + + glow *= param_intensity * float3(param_tint_r, param_tint_g, param_tint_b); + + float3 comp; + if (param_composite_mode > 0.5) + comp = src.rgb + glow; // add + else + comp = 1.0 - (1.0 - src.rgb) * (1.0 - glow); // screen + + float3 rgb = lerp(src.rgb, comp, m); + float4 out_c = float4(rgb, src.a); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + return out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.glow_bloom/shaders/gl/main.frag b/plugins/builtin/filters/com.hms.fx.glow_bloom/shaders/gl/main.frag new file mode 100644 index 0000000..d043871 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.glow_bloom/shaders/gl/main.frag @@ -0,0 +1,79 @@ +#version 330 core +// HMS MediaEngine - Glow Bloom (GLSL, Linux x64) +// Semantik identisch zur HLSL-Variante (§12.6, §15.4). + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_threshold; +uniform float param_knee; +uniform float param_radius; +uniform float param_intensity; +uniform float param_tint_r; +uniform float param_tint_g; +uniform float param_tint_b; +uniform float param_composite_mode; +uniform float param_mix; +uniform float u_quality_iterations; +uniform float u_quality_scale; + +in vec2 v_uv; +out vec4 fragColor; + +void main() +{ + vec4 src = texture(u_input_texture, v_uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.01) + { + fragColor = src; + return; + } + + float lum = dot(src.rgb, vec3(0.2126, 0.7152, 0.0722)); + float knee = max(param_knee, 0.0001); + float bright = smoothstep(param_threshold, param_threshold + knee, lum); + vec3 glow = src.rgb * bright; + + float iterations = clamp(u_quality_iterations, 1.0, 8.0); + float scale = max(u_quality_scale, 0.01); + vec2 texel = vec2(1.0, 1.0) / (u_resolution * scale); + vec3 acc = glow; + float total = 1.0; + for (float i = 0.0; i < iterations; i += 1.0) + { + float spread = (i + 1.0) * max(param_radius, 0.0) * 0.01; + vec2 off = texel * spread; + vec3 s = glow; + s += texture(u_input_texture, v_uv + off).rgb * bright; + s += texture(u_input_texture, v_uv - off).rgb * bright; + s += texture(u_input_texture, v_uv + off.yx).rgb * bright; + s += texture(u_input_texture, v_uv - off.yx).rgb * bright; + acc += s; + total += 4.0; + } + glow = acc / total; + + glow *= param_intensity * vec3(param_tint_r, param_tint_g, param_tint_b); + + vec3 comp; + if (param_composite_mode > 0.5) + comp = src.rgb + glow; + else + comp = 1.0 - (1.0 - src.rgb) * (1.0 - glow); + + vec3 rgb = mix(src.rgb, comp, m); + vec4 out_c = vec4(rgb, src.a); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + fragColor = out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.glow_bloom/shaders/gles/main.frag b/plugins/builtin/filters/com.hms.fx.glow_bloom/shaders/gles/main.frag new file mode 100644 index 0000000..448ed30 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.glow_bloom/shaders/gles/main.frag @@ -0,0 +1,80 @@ +#version 100 +// HMS MediaEngine - Glow Bloom (GLES, Raspberry Pi) +// ES 2.0: Schleifen mit konstanter Höchstgrenze (8); Abbruch über Bedingung. +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_threshold; +uniform float param_knee; +uniform float param_radius; +uniform float param_intensity; +uniform float param_tint_r; +uniform float param_tint_g; +uniform float param_tint_b; +uniform float param_composite_mode; +uniform float param_mix; +uniform float u_quality_iterations; +uniform float u_quality_scale; + +varying vec2 v_uv; + +void main() +{ + vec4 src = texture2D(u_input_texture, v_uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.01) + { + gl_FragColor = src; + return; + } + + float lum = dot(src.rgb, vec3(0.2126, 0.7152, 0.0722)); + float knee = max(param_knee, 0.0001); + float bright = smoothstep(param_threshold, param_threshold + knee, lum); + vec3 glow = src.rgb * bright; + + float iterations = clamp(u_quality_iterations, 1.0, 8.0); + float scale = max(u_quality_scale, 0.01); + vec2 texel = vec2(1.0, 1.0) / (u_resolution * scale); + vec3 acc = glow; + float total = 1.0; + for (int i = 0; i < 8; i++) + { + if (float(i) >= iterations) { break; } + float spread = (float(i) + 1.0) * max(param_radius, 0.0) * 0.01; + vec2 off = texel * spread; + vec3 s = glow; + s += texture2D(u_input_texture, v_uv + off).rgb * bright; + s += texture2D(u_input_texture, v_uv - off).rgb * bright; + s += texture2D(u_input_texture, v_uv + off.yx).rgb * bright; + s += texture2D(u_input_texture, v_uv - off.yx).rgb * bright; + acc += s; + total += 4.0; + } + glow = acc / total; + + glow *= param_intensity * vec3(param_tint_r, param_tint_g, param_tint_b); + + vec3 comp; + if (param_composite_mode > 0.5) + comp = src.rgb + glow; + else + comp = 1.0 - (1.0 - src.rgb) * (1.0 - glow); + + vec3 rgb = mix(src.rgb, comp, m); + vec4 out_c = vec4(rgb, src.a); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + gl_FragColor = out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.gradient_map/plugin.json b/plugins/builtin/filters/com.hms.fx.gradient_map/plugin.json new file mode 100644 index 0000000..833993c --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.gradient_map/plugin.json @@ -0,0 +1,184 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.gradient_map", + "name": "Gradient Map", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl", + "passes": [ + { + "pixel_shader": "shaders/d3d11/pass1.hlsl" + } + ] + }, + "gl": { + "type": "glsl", + "passes": [ + { + "fragment": "shaders/gl/pass1.frag" + } + ] + }, + "gles": { + "type": "glsl_es", + "passes": [ + { + "fragment": "shaders/gles/pass1.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "PI_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 1.0 + }, + { + "id": "medium", + "internal_scale": 1.0 + }, + { + "id": "high", + "internal_scale": 1.0 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "color_a", + "color_b", + "low", + "high" + ] + }, + "parameters": [ + { + "id": "color_a", + "label": "Color A", + "type": "color", + "default": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "dmx_slots": [ + 1 + ] + }, + { + "id": "color_b", + "label": "Color B", + "type": "color", + "default": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "dmx_slots": [ + 2 + ] + }, + { + "id": "low", + "label": "Low", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 3 + ], + "curve": "linear" + }, + { + "id": "high", + "label": "High", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 1.0, + "dmx_slots": [ + 4 + ], + "curve": "linear" + }, + { + "id": "balance", + "label": "Balance", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 5 + ], + "curve": "linear" + }, + { + "id": "smoothness", + "label": "Smoothness", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.5, + "dmx_slots": [ + 6 + ], + "curve": "linear" + }, + { + "id": "mode", + "label": "Mode", + "type": "enum", + "values": [ + "luma", + "value" + ], + "default": "luma", + "dmx_slots": [ + 7 + ] + }, + { + "id": "invert", + "label": "Invert", + "type": "bool", + "default": false + }, + { + "id": "preserve_alpha", + "label": "Preserve Alpha", + "type": "bool", + "default": true + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 1.0, + "dmx_slots": [ + 8 + ], + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/filters/com.hms.fx.gradient_map/shaders/d3d11/pass1.hlsl b/plugins/builtin/filters/com.hms.fx.gradient_map/shaders/d3d11/pass1.hlsl new file mode 100644 index 0000000..c77364d --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.gradient_map/shaders/d3d11/pass1.hlsl @@ -0,0 +1,65 @@ +// HMS MediaEngine – Gradient Map +// PLAN.md §15.2 Pflichtfilter; semantisch identisch über Backends (§12.6) + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float4 param_color_a; + float4 param_color_b; + float param_low; + float param_high; + float param_balance; + float param_smoothness; + float param_mode; + float param_invert; + float param_preserve_alpha; + float param_mix; + float _pad0; + float _pad1; +}; + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.001) + { + return src * u_layer_opacity; + } + + float lum; + if (param_mode < 0.5) + { + lum = dot(src.rgb, float3(0.299, 0.587, 0.114)); + } + else + { + lum = max(src.r, max(src.g, src.b)); + } + if (param_invert > 0.5) + { + lum = 1.0 - lum; + } + float t = (lum - param_low) / max(param_high - param_low, 0.0001); + t = clamp(t, 0.0, 1.0); + t = pow(t, exp(param_balance)); + t = smoothstep(0.5 - param_smoothness * 0.5, 0.5 + param_smoothness * 0.5, t); + float3 mapped = lerp(param_color_a.rgb, param_color_b.rgb, t); + float a = (param_preserve_alpha > 0.5) ? src.a : 1.0; + float4 result = float4(mapped.r, mapped.g, mapped.b, a); + result = lerp(src, result, m); + return result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.gradient_map/shaders/gl/pass1.frag b/plugins/builtin/filters/com.hms.fx.gradient_map/shaders/gl/pass1.frag new file mode 100644 index 0000000..7fbf3eb --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.gradient_map/shaders/gl/pass1.frag @@ -0,0 +1,64 @@ +#version 330 core +// HMS MediaEngine – Gradient Map +// PLAN.md §15.2 Pflichtfilter; GLSL (Linux x64), semantisch identisch zu HLSL (§12.6) + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform vec4 param_color_a; +uniform vec4 param_color_b; +uniform float param_low; +uniform float param_high; +uniform float param_balance; +uniform float param_smoothness; +uniform float param_mode; +uniform float param_invert; +uniform float param_preserve_alpha; +uniform float param_mix; + +in vec2 v_uv; +out vec4 fragColor; + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + + float lum; + if (param_mode < 0.5) + { + lum = dot(src.rgb, vec3(0.299, 0.587, 0.114)); + } + else + { + lum = max(src.r, max(src.g, src.b)); + } + if (param_invert > 0.5) + { + lum = 1.0 - lum; + } + float t = (lum - param_low) / max(param_high - param_low, 0.0001); + t = clamp(t, 0.0, 1.0); + t = pow(t, exp(param_balance)); + t = smoothstep(0.5 - param_smoothness * 0.5, 0.5 + param_smoothness * 0.5, t); + vec3 mapped = mix(param_color_a.rgb, param_color_b.rgb, t); + float a = (param_preserve_alpha > 0.5) ? src.a : 1.0; + vec4 result = vec4(mapped.r, mapped.g, mapped.b, a); + result = mix(src, result, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.gradient_map/shaders/gles/pass1.frag b/plugins/builtin/filters/com.hms.fx.gradient_map/shaders/gles/pass1.frag new file mode 100644 index 0000000..fa8d241 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.gradient_map/shaders/gles/pass1.frag @@ -0,0 +1,64 @@ +#version 100 +// HMS MediaEngine – Gradient Map +// PLAN.md §15.2 Pflichtfilter; GLES (Raspberry Pi), ES 2.0 +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform vec4 param_color_a; +uniform vec4 param_color_b; +uniform float param_low; +uniform float param_high; +uniform float param_balance; +uniform float param_smoothness; +uniform float param_mode; +uniform float param_invert; +uniform float param_preserve_alpha; +uniform float param_mix; + +varying vec2 v_uv; + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture2D(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + + float lum; + if (param_mode < 0.5) + { + lum = dot(src.rgb, vec3(0.299, 0.587, 0.114)); + } + else + { + lum = max(src.r, max(src.g, src.b)); + } + if (param_invert > 0.5) + { + lum = 1.0 - lum; + } + float t = (lum - param_low) / max(param_high - param_low, 0.0001); + t = clamp(t, 0.0, 1.0); + t = pow(t, exp(param_balance)); + t = smoothstep(0.5 - param_smoothness * 0.5, 0.5 + param_smoothness * 0.5, t); + vec3 mapped = mix(param_color_a.rgb, param_color_b.rgb, t); + float a = (param_preserve_alpha > 0.5) ? src.a : 1.0; + vec4 result = vec4(mapped.r, mapped.g, mapped.b, a); + result = mix(src, result, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.kaleidoscope/plugin.json b/plugins/builtin/filters/com.hms.fx.kaleidoscope/plugin.json new file mode 100644 index 0000000..f8633ca --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.kaleidoscope/plugin.json @@ -0,0 +1,167 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.kaleidoscope", + "name": "Kaleidoscope", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl", + "passes": [ + { + "pixel_shader": "shaders/d3d11/pass1.hlsl" + } + ] + }, + "gl": { + "type": "glsl", + "passes": [ + { + "fragment": "shaders/gl/pass1.frag" + } + ] + }, + "gles": { + "type": "glsl_es", + "passes": [ + { + "fragment": "shaders/gles/pass1.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "PI_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 1.0 + }, + { + "id": "medium", + "internal_scale": 1.0 + }, + { + "id": "high", + "internal_scale": 1.0 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "segments", + "rotation", + "zoom" + ] + }, + "parameters": [ + { + "id": "segments", + "label": "Segments", + "type": "int", + "minimum": 2, + "maximum": 32, + "default": 6, + "dmx_slots": [ + 1 + ], + "curve": "linear" + }, + { + "id": "rotation", + "label": "Rotation", + "type": "float", + "minimum": -180.0, + "maximum": 180.0, + "default": 0.0, + "dmx_slots": [ + 2 + ], + "curve": "linear" + }, + { + "id": "center_x", + "label": "Center X", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.5, + "dmx_slots": [ + 3 + ], + "curve": "linear" + }, + { + "id": "center_y", + "label": "Center Y", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.5, + "dmx_slots": [ + 4 + ], + "curve": "linear" + }, + { + "id": "zoom", + "label": "Zoom", + "type": "float", + "minimum": 0.1, + "maximum": 4.0, + "default": 1.0, + "dmx_slots": [ + 5 + ], + "curve": "linear" + }, + { + "id": "mirror_mode", + "label": "Mirror Mode", + "type": "enum", + "values": [ + "mirror", + "none" + ], + "default": "mirror", + "dmx_slots": [ + 6 + ] + }, + { + "id": "phase", + "label": "Phase", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 7 + ], + "curve": "linear" + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 1.0, + "dmx_slots": [ + 8 + ], + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/filters/com.hms.fx.kaleidoscope/shaders/d3d11/pass1.hlsl b/plugins/builtin/filters/com.hms.fx.kaleidoscope/shaders/d3d11/pass1.hlsl new file mode 100644 index 0000000..614cb8f --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.kaleidoscope/shaders/d3d11/pass1.hlsl @@ -0,0 +1,60 @@ +// HMS MediaEngine – Kaleidoscope +// PLAN.md §15.2 Pflichtfilter; semantisch identisch über Backends (§12.6) + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_segments; + float param_rotation; + float param_center_x; + float param_center_y; + float param_zoom; + float param_mirror_mode; + float param_phase; + float param_mix; + float _pad0; + float _pad1; +}; + + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.001) + { + return src * u_layer_opacity; + } + + float2 center = float2(param_center_x, param_center_y); + float2 p = uv - center; + p = p / max(param_zoom, 0.0001); + float r = length(p); + float theta = atan2(p.y, p.x); + theta = theta + radians(param_rotation) + param_phase * 6.28318; + float seg = max(float(param_segments), 2.0); + float seg_angle = 6.28318 / seg; + theta = fmod(theta, seg_angle); + if (param_mirror_mode > 0.5) + { + theta = seg_angle * 0.5 - abs(theta - seg_angle * 0.5); + } + float2 new_uv = center + float2(cos(theta) * r, sin(theta) * r); + new_uv = clamp(new_uv, 0.0, 1.0); + float4 transformed = u_input_texture.Sample(u_sampler, new_uv); + float4 result = lerp(src, transformed, m); + return result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.kaleidoscope/shaders/gl/pass1.frag b/plugins/builtin/filters/com.hms.fx.kaleidoscope/shaders/gl/pass1.frag new file mode 100644 index 0000000..f76edcb --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.kaleidoscope/shaders/gl/pass1.frag @@ -0,0 +1,59 @@ +#version 330 core +// HMS MediaEngine – Kaleidoscope +// PLAN.md §15.2 Pflichtfilter; GLSL (Linux x64), semantisch identisch zu HLSL (§12.6) + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_segments; +uniform float param_rotation; +uniform float param_center_x; +uniform float param_center_y; +uniform float param_zoom; +uniform float param_mirror_mode; +uniform float param_phase; +uniform float param_mix; + +in vec2 v_uv; +out vec4 fragColor; + + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + + vec2 center = vec2(param_center_x, param_center_y); + vec2 p = uv - center; + p = p / max(param_zoom, 0.0001); + float r = length(p); + float theta = atan(p.y, p.x); + theta = theta + radians(param_rotation) + param_phase * 6.28318; + float seg = max(float(param_segments), 2.0); + float seg_angle = 6.28318 / seg; + theta = mod(theta, seg_angle); + if (param_mirror_mode > 0.5) + { + theta = seg_angle * 0.5 - abs(theta - seg_angle * 0.5); + } + vec2 new_uv = center + vec2(cos(theta) * r, sin(theta) * r); + new_uv = clamp(new_uv, 0.0, 1.0); + vec4 transformed = texture(u_input_texture, new_uv); + vec4 result = mix(src, transformed, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.kaleidoscope/shaders/gles/pass1.frag b/plugins/builtin/filters/com.hms.fx.kaleidoscope/shaders/gles/pass1.frag new file mode 100644 index 0000000..f67edc6 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.kaleidoscope/shaders/gles/pass1.frag @@ -0,0 +1,70 @@ +#version 100 +// HMS MediaEngine – Kaleidoscope +// PLAN.md §15.2 Pflichtfilter; GLES (Raspberry Pi), ES 2.0 +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_segments; +uniform float param_rotation; +uniform float param_center_x; +uniform float param_center_y; +uniform float param_zoom; +uniform float param_mirror_mode; +uniform float param_phase; +uniform float param_mix; + +varying vec2 v_uv; + + +float hms_atan2(float y, float x) +{ + float a = atan(y / max(abs(x), 0.0001)); + if (x < 0.0) + { + if (y >= 0.0) { a += 3.14159265; } + else { a -= 3.14159265; } + } + return a; +} + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture2D(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + + vec2 center = vec2(param_center_x, param_center_y); + vec2 p = uv - center; + p = p / max(param_zoom, 0.0001); + float r = length(p); + float theta = hms_atan2(p.y, p.x); + theta = theta + radians(param_rotation) + param_phase * 6.28318; + float seg = max(float(param_segments), 2.0); + float seg_angle = 6.28318 / seg; + theta = mod(theta, seg_angle); + if (param_mirror_mode > 0.5) + { + theta = seg_angle * 0.5 - abs(theta - seg_angle * 0.5); + } + vec2 new_uv = center + vec2(cos(theta) * r, sin(theta) * r); + new_uv = clamp(new_uv, 0.0, 1.0); + vec4 transformed = texture2D(u_input_texture, new_uv); + vec4 result = mix(src, transformed, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.mirror_tile/plugin.json b/plugins/builtin/filters/com.hms.fx.mirror_tile/plugin.json new file mode 100644 index 0000000..1c01e01 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.mirror_tile/plugin.json @@ -0,0 +1,178 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.mirror_tile", + "name": "Mirror / Tile", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl", + "passes": [ + { + "pixel_shader": "shaders/d3d11/pass1.hlsl" + } + ] + }, + "gl": { + "type": "glsl", + "passes": [ + { + "fragment": "shaders/gl/pass1.frag" + } + ] + }, + "gles": { + "type": "glsl_es", + "passes": [ + { + "fragment": "shaders/gles/pass1.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "PI_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 1.0 + }, + { + "id": "medium", + "internal_scale": 1.0 + }, + { + "id": "high", + "internal_scale": 1.0 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "tiles_x", + "tiles_y", + "offset_x", + "offset_y" + ] + }, + "parameters": [ + { + "id": "mode", + "label": "Mode", + "type": "enum", + "values": [ + "mirror_h", + "mirror_v", + "tile", + "quad" + ], + "default": "mirror_h", + "dmx_slots": [ + 1 + ] + }, + { + "id": "tiles_x", + "label": "Tiles X", + "type": "float", + "minimum": 1.0, + "maximum": 16.0, + "default": 1.0, + "dmx_slots": [ + 2 + ], + "curve": "linear" + }, + { + "id": "tiles_y", + "label": "Tiles Y", + "type": "float", + "minimum": 1.0, + "maximum": 16.0, + "default": 1.0, + "dmx_slots": [ + 3 + ], + "curve": "linear" + }, + { + "id": "offset_x", + "label": "Offset X", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 4 + ], + "curve": "linear" + }, + { + "id": "offset_y", + "label": "Offset Y", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 5 + ], + "curve": "linear" + }, + { + "id": "center_x", + "label": "Center X", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.5, + "dmx_slots": [ + 6 + ], + "curve": "linear" + }, + { + "id": "center_y", + "label": "Center Y", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.5, + "dmx_slots": [ + 7 + ], + "curve": "linear" + }, + { + "id": "rotation", + "label": "Rotation", + "type": "float", + "minimum": -180.0, + "maximum": 180.0, + "default": 0.0 + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 1.0, + "dmx_slots": [ + 8 + ], + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/filters/com.hms.fx.mirror_tile/shaders/d3d11/pass1.hlsl b/plugins/builtin/filters/com.hms.fx.mirror_tile/shaders/d3d11/pass1.hlsl new file mode 100644 index 0000000..910c8eb --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.mirror_tile/shaders/d3d11/pass1.hlsl @@ -0,0 +1,71 @@ +// HMS MediaEngine – Mirror / Tile +// PLAN.md §15.2 Pflichtfilter; semantisch identisch über Backends (§12.6) + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_mode; + float param_tiles_x; + float param_tiles_y; + float param_offset_x; + float param_offset_y; + float param_center_x; + float param_center_y; + float param_rotation; + float param_mix; + float _pad0; +}; + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.001) + { + return src * u_layer_opacity; + } + + float2 center = float2(param_center_x, param_center_y); + float2 p = uv - center; + float ang = radians(param_rotation); + float c = cos(ang); + float s = sin(ang); + p = float2(p.x * c - p.y * s, p.x * s + p.y * c); + float2 tiles = float2(max(param_tiles_x, 1.0), max(param_tiles_y, 1.0)); + float2 off = float2(param_offset_x, param_offset_y); + float2 new_uv; + if (param_mode < 0.5) + { + new_uv = float2(abs(frac(p.x * tiles.x) * 2.0 - 1.0), p.y); + } + else if (param_mode < 1.5) + { + new_uv = float2(p.x, abs(frac(p.y * tiles.y) * 2.0 - 1.0)); + } + else if (param_mode < 2.5) + { + new_uv = float2(frac(p.x * tiles.x), frac(p.y * tiles.y)); + } + else + { + new_uv = float2(abs(frac(p.x * tiles.x) * 2.0 - 1.0), abs(frac(p.y * tiles.y) * 2.0 - 1.0)); + } + new_uv = new_uv + off + center; + new_uv = clamp(new_uv, 0.0, 1.0); + float4 transformed = u_input_texture.Sample(u_sampler, new_uv); + float4 result = lerp(src, transformed, m); + return result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.mirror_tile/shaders/gl/pass1.frag b/plugins/builtin/filters/com.hms.fx.mirror_tile/shaders/gl/pass1.frag new file mode 100644 index 0000000..6582a56 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.mirror_tile/shaders/gl/pass1.frag @@ -0,0 +1,71 @@ +#version 330 core +// HMS MediaEngine – Mirror / Tile +// PLAN.md §15.2 Pflichtfilter; GLSL (Linux x64), semantisch identisch zu HLSL (§12.6) + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_mode; +uniform float param_tiles_x; +uniform float param_tiles_y; +uniform float param_offset_x; +uniform float param_offset_y; +uniform float param_center_x; +uniform float param_center_y; +uniform float param_rotation; +uniform float param_mix; + +in vec2 v_uv; +out vec4 fragColor; + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + + vec2 center = vec2(param_center_x, param_center_y); + vec2 p = uv - center; + float ang = radians(param_rotation); + float c = cos(ang); + float s = sin(ang); + p = vec2(p.x * c - p.y * s, p.x * s + p.y * c); + vec2 tiles = vec2(max(param_tiles_x, 1.0), max(param_tiles_y, 1.0)); + vec2 off = vec2(param_offset_x, param_offset_y); + vec2 new_uv; + if (param_mode < 0.5) + { + new_uv = vec2(abs(fract(p.x * tiles.x) * 2.0 - 1.0), p.y); + } + else if (param_mode < 1.5) + { + new_uv = vec2(p.x, abs(fract(p.y * tiles.y) * 2.0 - 1.0)); + } + else if (param_mode < 2.5) + { + new_uv = vec2(fract(p.x * tiles.x), fract(p.y * tiles.y)); + } + else + { + new_uv = vec2(abs(fract(p.x * tiles.x) * 2.0 - 1.0), abs(fract(p.y * tiles.y) * 2.0 - 1.0)); + } + new_uv = new_uv + off + center; + new_uv = clamp(new_uv, 0.0, 1.0); + vec4 transformed = texture(u_input_texture, new_uv); + vec4 result = mix(src, transformed, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.mirror_tile/shaders/gles/pass1.frag b/plugins/builtin/filters/com.hms.fx.mirror_tile/shaders/gles/pass1.frag new file mode 100644 index 0000000..b8a3f5f --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.mirror_tile/shaders/gles/pass1.frag @@ -0,0 +1,71 @@ +#version 100 +// HMS MediaEngine – Mirror / Tile +// PLAN.md §15.2 Pflichtfilter; GLES (Raspberry Pi), ES 2.0 +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_mode; +uniform float param_tiles_x; +uniform float param_tiles_y; +uniform float param_offset_x; +uniform float param_offset_y; +uniform float param_center_x; +uniform float param_center_y; +uniform float param_rotation; +uniform float param_mix; + +varying vec2 v_uv; + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture2D(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + + vec2 center = vec2(param_center_x, param_center_y); + vec2 p = uv - center; + float ang = radians(param_rotation); + float c = cos(ang); + float s = sin(ang); + p = vec2(p.x * c - p.y * s, p.x * s + p.y * c); + vec2 tiles = vec2(max(param_tiles_x, 1.0), max(param_tiles_y, 1.0)); + vec2 off = vec2(param_offset_x, param_offset_y); + vec2 new_uv; + if (param_mode < 0.5) + { + new_uv = vec2(abs(fract(p.x * tiles.x) * 2.0 - 1.0), p.y); + } + else if (param_mode < 1.5) + { + new_uv = vec2(p.x, abs(fract(p.y * tiles.y) * 2.0 - 1.0)); + } + else if (param_mode < 2.5) + { + new_uv = vec2(fract(p.x * tiles.x), fract(p.y * tiles.y)); + } + else + { + new_uv = vec2(abs(fract(p.x * tiles.x) * 2.0 - 1.0), abs(fract(p.y * tiles.y) * 2.0 - 1.0)); + } + new_uv = new_uv + off + center; + new_uv = clamp(new_uv, 0.0, 1.0); + vec4 transformed = texture2D(u_input_texture, new_uv); + vec4 result = mix(src, transformed, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.pixelate_quantize/plugin.json b/plugins/builtin/filters/com.hms.fx.pixelate_quantize/plugin.json new file mode 100644 index 0000000..505880b --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.pixelate_quantize/plugin.json @@ -0,0 +1,173 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.pixelate_quantize", + "name": "Pixelate / Quantize", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl", + "passes": [ + { + "pixel_shader": "shaders/d3d11/pass1.hlsl" + } + ] + }, + "gl": { + "type": "glsl", + "passes": [ + { + "fragment": "shaders/gl/pass1.frag" + } + ] + }, + "gles": { + "type": "glsl_es", + "passes": [ + { + "fragment": "shaders/gles/pass1.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "PI_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 1.0 + }, + { + "id": "medium", + "internal_scale": 1.0 + }, + { + "id": "high", + "internal_scale": 1.0 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "pixel_x", + "pixel_y", + "color_levels" + ] + }, + "parameters": [ + { + "id": "pixel_x", + "label": "Pixel X", + "type": "float", + "minimum": 1.0, + "maximum": 512.0, + "default": 32.0, + "dmx_slots": [ + 1 + ], + "curve": "linear" + }, + { + "id": "pixel_y", + "label": "Pixel Y", + "type": "float", + "minimum": 1.0, + "maximum": 512.0, + "default": 32.0, + "dmx_slots": [ + 2 + ], + "curve": "linear" + }, + { + "id": "color_levels", + "label": "Color Levels", + "type": "int", + "minimum": 2, + "maximum": 256, + "default": 16, + "dmx_slots": [ + 3 + ], + "curve": "linear" + }, + { + "id": "dither", + "label": "Dither", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 4 + ], + "curve": "linear" + }, + { + "id": "grid_offset_x", + "label": "Grid Offset X", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 5 + ], + "curve": "linear" + }, + { + "id": "grid_offset_y", + "label": "Grid Offset Y", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 6 + ], + "curve": "linear" + }, + { + "id": "aspect_lock", + "label": "Aspect Lock", + "type": "bool", + "default": true + }, + { + "id": "mode", + "label": "Mode", + "type": "enum", + "values": [ + "pixelate", + "posterize" + ], + "default": "pixelate", + "dmx_slots": [ + 7 + ] + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 1.0, + "dmx_slots": [ + 8 + ], + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/filters/com.hms.fx.pixelate_quantize/shaders/d3d11/pass1.hlsl b/plugins/builtin/filters/com.hms.fx.pixelate_quantize/shaders/d3d11/pass1.hlsl new file mode 100644 index 0000000..e42d2d9 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.pixelate_quantize/shaders/d3d11/pass1.hlsl @@ -0,0 +1,65 @@ +// HMS MediaEngine – Pixelate / Quantize +// PLAN.md §15.2 Pflichtfilter; semantisch identisch über Backends (§12.6) + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_pixel_x; + float param_pixel_y; + float param_color_levels; + float param_dither; + float param_grid_offset_x; + float param_grid_offset_y; + float param_aspect_lock; + float param_mode; + float param_mix; + float _pad0; +}; + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.001) + { + return src * u_layer_opacity; + } + + float2 grid = float2(max(param_pixel_x, 1.0), max(param_pixel_y, 1.0)); + if (param_aspect_lock > 0.5) + { + grid.y = grid.x * (u_resolution.x / u_resolution.y); + } + float2 cell = float2(1.0, 1.0) / grid; + float2 off = float2(param_grid_offset_x, param_grid_offset_y); + float2 cell_uv = floor(uv / cell + off) * cell; + float4 pixelated = u_input_texture.Sample(u_sampler, cell_uv); + + float4 result = pixelated; + if (param_mode > 0.5) + { + float levels = max(float(param_color_levels), 2.0); + float3 q = floor(src.rgb * levels + 0.5) / levels; + if (param_dither > 0.001) + { + float d = (frac(sin(dot(uv, float2(12.9898, 78.233)) * 43758.5453)) - 0.5) * param_dither; + q = floor(src.rgb * levels + 0.5 + d) / levels; + } + result = float4(q.r, q.g, q.b, src.a); + } + result = lerp(src, result, m); + return result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.pixelate_quantize/shaders/gl/pass1.frag b/plugins/builtin/filters/com.hms.fx.pixelate_quantize/shaders/gl/pass1.frag new file mode 100644 index 0000000..d281b83 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.pixelate_quantize/shaders/gl/pass1.frag @@ -0,0 +1,65 @@ +#version 330 core +// HMS MediaEngine – Pixelate / Quantize +// PLAN.md §15.2 Pflichtfilter; GLSL (Linux x64), semantisch identisch zu HLSL (§12.6) + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_pixel_x; +uniform float param_pixel_y; +uniform float param_color_levels; +uniform float param_dither; +uniform float param_grid_offset_x; +uniform float param_grid_offset_y; +uniform float param_aspect_lock; +uniform float param_mode; +uniform float param_mix; + +in vec2 v_uv; +out vec4 fragColor; + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + + vec2 grid = vec2(max(param_pixel_x, 1.0), max(param_pixel_y, 1.0)); + if (param_aspect_lock > 0.5) + { + grid.y = grid.x * (u_resolution.x / u_resolution.y); + } + vec2 cell = vec2(1.0, 1.0) / grid; + vec2 off = vec2(param_grid_offset_x, param_grid_offset_y); + vec2 cell_uv = floor(uv / cell + off) * cell; + vec4 pixelated = texture(u_input_texture, cell_uv); + + vec4 result = pixelated; + if (param_mode > 0.5) + { + float levels = max(float(param_color_levels), 2.0); + vec3 q = floor(src.rgb * levels + 0.5) / levels; + if (param_dither > 0.001) + { + float d = (fract(sin(dot(uv, vec2(12.9898, 78.233)) * 43758.5453)) - 0.5) * param_dither; + q = floor(src.rgb * levels + 0.5 + d) / levels; + } + result = vec4(q.r, q.g, q.b, src.a); + } + result = mix(src, result, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.pixelate_quantize/shaders/gles/pass1.frag b/plugins/builtin/filters/com.hms.fx.pixelate_quantize/shaders/gles/pass1.frag new file mode 100644 index 0000000..34c6e38 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.pixelate_quantize/shaders/gles/pass1.frag @@ -0,0 +1,65 @@ +#version 100 +// HMS MediaEngine – Pixelate / Quantize +// PLAN.md §15.2 Pflichtfilter; GLES (Raspberry Pi), ES 2.0 +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_pixel_x; +uniform float param_pixel_y; +uniform float param_color_levels; +uniform float param_dither; +uniform float param_grid_offset_x; +uniform float param_grid_offset_y; +uniform float param_aspect_lock; +uniform float param_mode; +uniform float param_mix; + +varying vec2 v_uv; + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture2D(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + + vec2 grid = vec2(max(param_pixel_x, 1.0), max(param_pixel_y, 1.0)); + if (param_aspect_lock > 0.5) + { + grid.y = grid.x * (u_resolution.x / u_resolution.y); + } + vec2 cell = vec2(1.0, 1.0) / grid; + vec2 off = vec2(param_grid_offset_x, param_grid_offset_y); + vec2 cell_uv = floor(uv / cell + off) * cell; + vec4 pixelated = texture2D(u_input_texture, cell_uv); + + vec4 result = pixelated; + if (param_mode > 0.5) + { + float levels = max(float(param_color_levels), 2.0); + vec3 q = floor(src.rgb * levels + 0.5) / levels; + if (param_dither > 0.001) + { + float d = (fract(sin(dot(uv, vec2(12.9898, 78.233)) * 43758.5453)) - 0.5) * param_dither; + q = floor(src.rgb * levels + 0.5 + d) / levels; + } + result = vec4(q.r, q.g, q.b, src.a); + } + result = mix(src, result, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.rgb_split/plugin.json b/plugins/builtin/filters/com.hms.fx.rgb_split/plugin.json new file mode 100644 index 0000000..1c84070 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.rgb_split/plugin.json @@ -0,0 +1,168 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.rgb_split", + "name": "RGB Split", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl", + "passes": [ + { + "pixel_shader": "shaders/d3d11/main.hlsl" + } + ] + }, + "gl": { + "type": "glsl", + "passes": [ + { + "fragment": "shaders/gl/main.frag" + } + ] + }, + "gles": { + "type": "glsl_es", + "passes": [ + { + "fragment": "shaders/gles/main.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "PI_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "parameters": [ + { + "id": "amount", + "label": "Amount", + "type": "float", + "minimum": 0, + "maximum": 100, + "default": 8, + "dmx_slots": [ + 1 + ] + }, + { + "id": "angle", + "label": "Angle", + "type": "float", + "minimum": 0, + "maximum": 360, + "default": 0, + "dmx_slots": [ + 2 + ] + }, + { + "id": "radial", + "label": "Radial", + "type": "bool", + "default": 0, + "dmx_slots": [ + 3 + ] + }, + { + "id": "center_x", + "label": "Center X", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0.5, + "dmx_slots": [ + 4 + ] + }, + { + "id": "center_y", + "label": "Center Y", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0.5, + "dmx_slots": [ + 5 + ] + }, + { + "id": "red_balance", + "label": "Red Balance", + "type": "float", + "minimum": 0, + "maximum": 2, + "default": 1, + "dmx_slots": [ + 6 + ] + }, + { + "id": "blue_balance", + "label": "Blue Balance", + "type": "float", + "minimum": 0, + "maximum": 2, + "default": 1, + "dmx_slots": [ + 7 + ] + }, + { + "id": "edge_mode", + "label": "Edge Mode", + "type": "enum", + "values": [ + "clamp", + "repeat" + ], + "default": "clamp", + "dmx_slots": [ + 8 + ] + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 1, + "dmx_slots": [] + } + ], + "failure_mode": "bypass", + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 1.0, + "samples": 1 + }, + { + "id": "medium", + "internal_scale": 1.0, + "samples": 1 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 1 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "mix" + ] + } +} diff --git a/plugins/builtin/filters/com.hms.fx.rgb_split/shaders/d3d11/main.hlsl b/plugins/builtin/filters/com.hms.fx.rgb_split/shaders/d3d11/main.hlsl new file mode 100644 index 0000000..3fbd96a --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.rgb_split/shaders/d3d11/main.hlsl @@ -0,0 +1,75 @@ +// HMS MediaEngine - RGB Split (PLAN.md §15.2) +// RGB-Versatz / chromatische Aberration; ein GPU-Pass. + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_amount; + float param_angle; + float param_radial; // 0 = linear, 1 = radial + float param_center_x; + float param_center_y; + float param_red_balance; + float param_blue_balance; + float param_edge_mode; // 0 = clamp, 1 = repeat + float param_mix; + float _pad0; + float _pad1; + float _pad2; + float _pad3; +}; + +float2 wrap_uv(float2 uv) +{ + if (param_edge_mode > 0.5) + return frac(uv); + return clamp(uv, 0.0, 1.0); +} + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.01) + { + return src; // Mix 0 = kostenloser Bypass (§15.3) + } + + float amount = param_amount * 0.01; + float2 dir; + if (param_radial > 0.5) + { + dir = normalize(uv - float2(param_center_x, param_center_y) + 1e-5); + } + else + { + float a = radians(param_angle); + dir = float2(cos(a), sin(a)); + } + + float2 r_uv = wrap_uv(uv + dir * amount * param_red_balance); + float2 b_uv = wrap_uv(uv - dir * amount * param_blue_balance); + + float4 r = u_input_texture.Sample(u_sampler, r_uv); + float4 g = src; + float4 b = u_input_texture.Sample(u_sampler, b_uv); + + float4 out_c = float4(r.r, g.g, b.b, src.a); + out_c = lerp(src, out_c, m); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + return out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.rgb_split/shaders/gl/main.frag b/plugins/builtin/filters/com.hms.fx.rgb_split/shaders/gl/main.frag new file mode 100644 index 0000000..3ce19f7 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.rgb_split/shaders/gl/main.frag @@ -0,0 +1,71 @@ +#version 330 core +// HMS MediaEngine - RGB Split (GLSL, Linux x64) +// Semantik identisch zur HLSL-Variante (§12.6, §15.4). + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_amount; +uniform float param_angle; +uniform float param_radial; +uniform float param_center_x; +uniform float param_center_y; +uniform float param_red_balance; +uniform float param_blue_balance; +uniform float param_edge_mode; +uniform float param_mix; + +in vec2 v_uv; +out vec4 fragColor; + +vec2 wrap_uv(vec2 uv) +{ + if (param_edge_mode > 0.5) + return fract(uv); + return clamp(uv, 0.0, 1.0); +} + +void main() +{ + vec4 src = texture(u_input_texture, v_uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.01) + { + fragColor = src; + return; + } + + float amount = param_amount * 0.01; + vec2 dir; + if (param_radial > 0.5) + { + dir = normalize(v_uv - vec2(param_center_x, param_center_y) + 1e-5); + } + else + { + float a = radians(param_angle); + dir = vec2(cos(a), sin(a)); + } + + vec2 r_uv = wrap_uv(v_uv + dir * amount * param_red_balance); + vec2 b_uv = wrap_uv(v_uv - dir * amount * param_blue_balance); + + vec4 r = texture(u_input_texture, r_uv); + vec4 g = src; + vec4 b = texture(u_input_texture, b_uv); + + vec4 out_c = vec4(r.r, g.g, b.b, src.a); + out_c = mix(src, out_c, m); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + fragColor = out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.rgb_split/shaders/gles/main.frag b/plugins/builtin/filters/com.hms.fx.rgb_split/shaders/gles/main.frag new file mode 100644 index 0000000..a18f359 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.rgb_split/shaders/gles/main.frag @@ -0,0 +1,71 @@ +#version 100 +// HMS MediaEngine - RGB Split (GLES, Raspberry Pi) +// ES 2.0; ein GPU-Pass. +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_amount; +uniform float param_angle; +uniform float param_radial; +uniform float param_center_x; +uniform float param_center_y; +uniform float param_red_balance; +uniform float param_blue_balance; +uniform float param_edge_mode; +uniform float param_mix; + +varying vec2 v_uv; + +vec2 wrap_uv(vec2 uv) +{ + if (param_edge_mode > 0.5) + return fract(uv); + return clamp(uv, 0.0, 1.0); +} + +void main() +{ + vec4 src = texture2D(u_input_texture, v_uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.01) + { + gl_FragColor = src; + return; + } + + float amount = param_amount * 0.01; + vec2 dir; + if (param_radial > 0.5) + { + dir = normalize(v_uv - vec2(param_center_x, param_center_y) + 1e-5); + } + else + { + float a = radians(param_angle); + dir = vec2(cos(a), sin(a)); + } + + vec2 r_uv = wrap_uv(v_uv + dir * amount * param_red_balance); + vec2 b_uv = wrap_uv(v_uv - dir * amount * param_blue_balance); + + vec4 r = texture2D(u_input_texture, r_uv); + vec4 g = src; + vec4 b = texture2D(u_input_texture, b_uv); + + vec4 out_c = vec4(r.r, g.g, b.b, src.a); + out_c = mix(src, out_c, m); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + gl_FragColor = out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.strobe_pulse/README.md b/plugins/builtin/filters/com.hms.fx.strobe_pulse/README.md new file mode 100644 index 0000000..fdcfed6 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.strobe_pulse/README.md @@ -0,0 +1,18 @@ +# Strobe Pulse + +Zeitgesteuertes Blinken/Pulsieren (PLAN.md §15.2). Ein GPU-Pass. + +## Parameter +- **Rate (Hz)**: Blinkfrequenz. **Sichere Voreinstellung: max. 2.0 Hz.** +- **Duty**: Einschaltdauer (0..1). +- **Attack / Release**: Ein-/Ausblendzeit. +- **Phase**: Phasenversatz (0..1). +- **Sync Mode**: `free` (Zeitbasis), `tap` (Audio-Peak), `beat` (Audio-Beat). +- **Minimum Level**: Untergrenze der Helligkeit. +- **Mix**: 0 = kostenloser Bypass (§15.3). + +## Sicherheitshinweis (Photosensitivität) +**Oberhalb der sicheren Voreinstellung (Rate > 2.0 Hz) nur nach expliziter Freigabe, +nie durch DMX allein freischaltbar** (§15.2). Der Shader begrenzt `param_rate` +hart auf 2.0 Hz; höhere Raten erfordern eine explizite, projektweite Freigabe +außerhalb des DMX-Pfads. diff --git a/plugins/builtin/filters/com.hms.fx.strobe_pulse/plugin.json b/plugins/builtin/filters/com.hms.fx.strobe_pulse/plugin.json new file mode 100644 index 0000000..45244d6 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.strobe_pulse/plugin.json @@ -0,0 +1,164 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.strobe_pulse", + "name": "Strobe Pulse", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl", + "passes": [ + { + "pixel_shader": "shaders/d3d11/main.hlsl" + } + ] + }, + "gl": { + "type": "glsl", + "passes": [ + { + "fragment": "shaders/gl/main.frag" + } + ] + }, + "gles": { + "type": "glsl_es", + "passes": [ + { + "fragment": "shaders/gles/main.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "PI_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "parameters": [ + { + "id": "rate", + "label": "Rate (Hz)", + "type": "float", + "minimum": 0, + "maximum": 2.0, + "default": 1.0, + "dmx_slots": [ + 1 + ] + }, + { + "id": "duty", + "label": "Duty", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0.5, + "dmx_slots": [ + 2 + ] + }, + { + "id": "attack", + "label": "Attack", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0.1, + "dmx_slots": [ + 3 + ] + }, + { + "id": "release", + "label": "Release", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0.2, + "dmx_slots": [ + 4 + ] + }, + { + "id": "phase", + "label": "Phase", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0, + "dmx_slots": [ + 5 + ] + }, + { + "id": "sync_mode", + "label": "Sync Mode", + "type": "enum", + "values": [ + "free", + "tap", + "beat" + ], + "default": "free", + "dmx_slots": [ + 6 + ] + }, + { + "id": "minimum_level", + "label": "Minimum Level", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0, + "dmx_slots": [ + 7 + ] + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 1, + "dmx_slots": [ + 8 + ] + } + ], + "failure_mode": "bypass", + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 1.0, + "samples": 1 + }, + { + "id": "medium", + "internal_scale": 1.0, + "samples": 1 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 1 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "rate", + "duty", + "mix" + ] + } +} diff --git a/plugins/builtin/filters/com.hms.fx.strobe_pulse/shaders/d3d11/main.hlsl b/plugins/builtin/filters/com.hms.fx.strobe_pulse/shaders/d3d11/main.hlsl new file mode 100644 index 0000000..12d9b50 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.strobe_pulse/shaders/d3d11/main.hlsl @@ -0,0 +1,76 @@ +// HMS MediaEngine - Strobe Pulse (PLAN.md §15.2) +// Zeitgesteuertes Blinken/Pulsieren; ein GPU-Pass. +// Sicherheit: Rate ist auf max. 2.0 Hz begrenzt (sichere Voreinstellung). +// Oberhalb der sicheren Voreinstellung nur nach expliziter Freigabe, +// nie durch DMX allein freischaltbar (§15.2). + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_rate; // Hz, max. sicher 2.0 + float param_duty; + float param_attack; + float param_release; + float param_phase; // 0..1 + float param_sync_mode; // 0 = free, 1 = tap, 2 = beat + float param_minimum_level; + float param_mix; + float _pad0; + float _pad1; + float _pad2; + float _pad3; + float _pad4; +}; + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.01) + { + return src; // Mix 0 = kostenloser Bypass (§15.3) + } + + float rate = clamp(param_rate, 0.0, 2.0); // sichere Obergrenze + float t; + if (param_sync_mode > 1.5) // beat + t = u_audio_beat; + else if (param_sync_mode > 0.5) // tap + t = u_audio_peak; + else // free + t = u_time_seconds; + + float period = max(rate, 0.0001); + float phase = frac(t * period + param_phase); + float duty = clamp(param_duty, 0.0, 1.0); + float attack = max(param_attack, 0.0001); + float release = max(param_release, 0.0001); + + float level; + if (phase < duty) + level = smoothstep(0.0, attack, phase); + else + level = 1.0 - smoothstep(duty, min(duty + release, 1.0), phase); + + level = lerp(param_minimum_level, 1.0, level); + level = saturate(level); + + float3 rgb = src.rgb * lerp(1.0, level, m); + float4 out_c = float4(rgb, src.a); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + return out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.strobe_pulse/shaders/gl/main.frag b/plugins/builtin/filters/com.hms.fx.strobe_pulse/shaders/gl/main.frag new file mode 100644 index 0000000..1329d62 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.strobe_pulse/shaders/gl/main.frag @@ -0,0 +1,68 @@ +#version 330 core +// HMS MediaEngine - Strobe Pulse (GLSL, Linux x64) +// Semantik identisch zur HLSL-Variante (§12.6, §15.4). + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_rate; +uniform float param_duty; +uniform float param_attack; +uniform float param_release; +uniform float param_phase; +uniform float param_sync_mode; +uniform float param_minimum_level; +uniform float param_mix; + +in vec2 v_uv; +out vec4 fragColor; + +void main() +{ + vec4 src = texture(u_input_texture, v_uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.01) + { + fragColor = src; + return; + } + + float rate = clamp(param_rate, 0.0, 2.0); // sichere Obergrenze + float t; + if (param_sync_mode > 1.5) // beat + t = u_audio_beat; + else if (param_sync_mode > 0.5) // tap + t = u_audio_peak; + else // free + t = u_time_seconds; + + float period = max(rate, 0.0001); + float phase = fract(t * period + param_phase); + float duty = clamp(param_duty, 0.0, 1.0); + float attack = max(param_attack, 0.0001); + float release = max(param_release, 0.0001); + + float level; + if (phase < duty) + level = smoothstep(0.0, attack, phase); + else + level = 1.0 - smoothstep(duty, min(duty + release, 1.0), phase); + + level = mix(param_minimum_level, 1.0, level); + level = clamp(level, 0.0, 1.0); + + vec3 rgb = src.rgb * mix(1.0, level, m); + vec4 out_c = vec4(rgb, src.a); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + fragColor = out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.strobe_pulse/shaders/gles/main.frag b/plugins/builtin/filters/com.hms.fx.strobe_pulse/shaders/gles/main.frag new file mode 100644 index 0000000..c1fbdb0 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.strobe_pulse/shaders/gles/main.frag @@ -0,0 +1,68 @@ +#version 100 +// HMS MediaEngine - Strobe Pulse (GLES, Raspberry Pi) +// ES 2.0; ein GPU-Pass. +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_rate; +uniform float param_duty; +uniform float param_attack; +uniform float param_release; +uniform float param_phase; +uniform float param_sync_mode; +uniform float param_minimum_level; +uniform float param_mix; + +varying vec2 v_uv; + +void main() +{ + vec4 src = texture2D(u_input_texture, v_uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.01) + { + gl_FragColor = src; + return; + } + + float rate = clamp(param_rate, 0.0, 2.0); // sichere Obergrenze + float t; + if (param_sync_mode > 1.5) // beat + t = u_audio_beat; + else if (param_sync_mode > 0.5) // tap + t = u_audio_peak; + else // free + t = u_time_seconds; + + float period = max(rate, 0.0001); + float phase = fract(t * period + param_phase); + float duty = clamp(param_duty, 0.0, 1.0); + float attack = max(param_attack, 0.0001); + float release = max(param_release, 0.0001); + + float level; + if (phase < duty) + level = smoothstep(0.0, attack, phase); + else + level = 1.0 - smoothstep(duty, min(duty + release, 1.0), phase); + + level = mix(param_minimum_level, 1.0, level); + level = clamp(level, 0.0, 1.0); + + vec3 rgb = src.rgb * mix(1.0, level, m); + vec4 out_c = vec4(rgb, src.a); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + gl_FragColor = out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.transform2d/plugin.json b/plugins/builtin/filters/com.hms.fx.transform2d/plugin.json new file mode 100644 index 0000000..88fa342 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.transform2d/plugin.json @@ -0,0 +1,178 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.transform2d", + "name": "Transform 2D", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl", + "passes": [ + { + "pixel_shader": "shaders/d3d11/pass1.hlsl" + } + ] + }, + "gl": { + "type": "glsl", + "passes": [ + { + "fragment": "shaders/gl/pass1.frag" + } + ] + }, + "gles": { + "type": "glsl_es", + "passes": [ + { + "fragment": "shaders/gles/pass1.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "PI_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 1.0 + }, + { + "id": "medium", + "internal_scale": 1.0 + }, + { + "id": "high", + "internal_scale": 1.0 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "position_x", + "position_y", + "scale_x", + "scale_y", + "rotation" + ] + }, + "parameters": [ + { + "id": "position_x", + "label": "Position X", + "type": "float", + "minimum": -2.0, + "maximum": 2.0, + "default": 0.0, + "dmx_slots": [ + 1 + ], + "curve": "linear" + }, + { + "id": "position_y", + "label": "Position Y", + "type": "float", + "minimum": -2.0, + "maximum": 2.0, + "default": 0.0, + "dmx_slots": [ + 2 + ], + "curve": "linear" + }, + { + "id": "scale_x", + "label": "Scale X", + "type": "float", + "minimum": 0.1, + "maximum": 4.0, + "default": 1.0, + "dmx_slots": [ + 3 + ], + "curve": "linear" + }, + { + "id": "scale_y", + "label": "Scale Y", + "type": "float", + "minimum": 0.1, + "maximum": 4.0, + "default": 1.0, + "dmx_slots": [ + 4 + ], + "curve": "linear" + }, + { + "id": "rotation", + "label": "Rotation", + "type": "float", + "minimum": -180.0, + "maximum": 180.0, + "default": 0.0, + "dmx_slots": [ + 5 + ], + "curve": "linear" + }, + { + "id": "anchor_x", + "label": "Anchor X", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.5, + "dmx_slots": [ + 6 + ], + "curve": "linear" + }, + { + "id": "anchor_y", + "label": "Anchor Y", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.5, + "dmx_slots": [ + 7 + ], + "curve": "linear" + }, + { + "id": "wrap", + "label": "Wrap / Tile", + "type": "enum", + "values": [ + "clamp", + "repeat" + ], + "default": "clamp" + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 1.0, + "dmx_slots": [ + 8 + ], + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/filters/com.hms.fx.transform2d/shaders/d3d11/pass1.hlsl b/plugins/builtin/filters/com.hms.fx.transform2d/shaders/d3d11/pass1.hlsl new file mode 100644 index 0000000..9ea25b8 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.transform2d/shaders/d3d11/pass1.hlsl @@ -0,0 +1,63 @@ +// HMS MediaEngine – Transform 2D +// PLAN.md §15.2 Pflichtfilter; semantisch identisch über Backends (§12.6) + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_position_x; + float param_position_y; + float param_scale_x; + float param_scale_y; + float param_rotation; + float param_anchor_x; + float param_anchor_y; + float param_wrap; + float param_mix; + float _pad0; +}; + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.001) + { + return src * u_layer_opacity; + } + + float2 anchor = float2(param_anchor_x, param_anchor_y); + float2 p = uv - anchor; + float sx = max(param_scale_x, 0.0001); + float sy = max(param_scale_y, 0.0001); + p = p / float2(sx, sy); + float ang = radians(param_rotation); + float c = cos(ang); + float s = sin(ang); + p = float2(p.x * c - p.y * s, p.x * s + p.y * c); + p = p + float2(param_position_x, param_position_y); + float2 new_uv = p + anchor; + if (param_wrap > 0.5) + { + new_uv = frac(new_uv); + } + else + { + new_uv = clamp(new_uv, 0.0, 1.0); + } + float4 transformed = u_input_texture.Sample(u_sampler, new_uv); + float4 result = lerp(src, transformed, m); + return result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.transform2d/shaders/gl/pass1.frag b/plugins/builtin/filters/com.hms.fx.transform2d/shaders/gl/pass1.frag new file mode 100644 index 0000000..5756899 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.transform2d/shaders/gl/pass1.frag @@ -0,0 +1,63 @@ +#version 330 core +// HMS MediaEngine – Transform 2D +// PLAN.md §15.2 Pflichtfilter; GLSL (Linux x64), semantisch identisch zu HLSL (§12.6) + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_position_x; +uniform float param_position_y; +uniform float param_scale_x; +uniform float param_scale_y; +uniform float param_rotation; +uniform float param_anchor_x; +uniform float param_anchor_y; +uniform float param_wrap; +uniform float param_mix; + +in vec2 v_uv; +out vec4 fragColor; + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + + vec2 anchor = vec2(param_anchor_x, param_anchor_y); + vec2 p = uv - anchor; + float sx = max(param_scale_x, 0.0001); + float sy = max(param_scale_y, 0.0001); + p = p / vec2(sx, sy); + float ang = radians(param_rotation); + float c = cos(ang); + float s = sin(ang); + p = vec2(p.x * c - p.y * s, p.x * s + p.y * c); + p = p + vec2(param_position_x, param_position_y); + vec2 new_uv = p + anchor; + if (param_wrap > 0.5) + { + new_uv = fract(new_uv); + } + else + { + new_uv = clamp(new_uv, 0.0, 1.0); + } + vec4 transformed = texture(u_input_texture, new_uv); + vec4 result = mix(src, transformed, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.transform2d/shaders/gles/pass1.frag b/plugins/builtin/filters/com.hms.fx.transform2d/shaders/gles/pass1.frag new file mode 100644 index 0000000..f8ea114 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.transform2d/shaders/gles/pass1.frag @@ -0,0 +1,63 @@ +#version 100 +// HMS MediaEngine – Transform 2D +// PLAN.md §15.2 Pflichtfilter; GLES (Raspberry Pi), ES 2.0 +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_position_x; +uniform float param_position_y; +uniform float param_scale_x; +uniform float param_scale_y; +uniform float param_rotation; +uniform float param_anchor_x; +uniform float param_anchor_y; +uniform float param_wrap; +uniform float param_mix; + +varying vec2 v_uv; + +void main() +{ + vec2 uv = v_uv; + vec4 src = texture2D(u_input_texture, uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.001) + { + gl_FragColor = src * u_layer_opacity; + return; + } + + vec2 anchor = vec2(param_anchor_x, param_anchor_y); + vec2 p = uv - anchor; + float sx = max(param_scale_x, 0.0001); + float sy = max(param_scale_y, 0.0001); + p = p / vec2(sx, sy); + float ang = radians(param_rotation); + float c = cos(ang); + float s = sin(ang); + p = vec2(p.x * c - p.y * s, p.x * s + p.y * c); + p = p + vec2(param_position_x, param_position_y); + vec2 new_uv = p + anchor; + if (param_wrap > 0.5) + { + new_uv = fract(new_uv); + } + else + { + new_uv = clamp(new_uv, 0.0, 1.0); + } + vec4 transformed = texture2D(u_input_texture, new_uv); + vec4 result = mix(src, transformed, m); + gl_FragColor = result * u_layer_opacity; +} diff --git a/plugins/builtin/filters/com.hms.fx.vignette/plugin.json b/plugins/builtin/filters/com.hms.fx.vignette/plugin.json new file mode 100644 index 0000000..197dcca --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.vignette/plugin.json @@ -0,0 +1,168 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.vignette", + "name": "Vignette", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl", + "passes": [ + { + "pixel_shader": "shaders/d3d11/main.hlsl" + } + ] + }, + "gl": { + "type": "glsl", + "passes": [ + { + "fragment": "shaders/gl/main.frag" + } + ] + }, + "gles": { + "type": "glsl_es", + "passes": [ + { + "fragment": "shaders/gles/main.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "PI_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "parameters": [ + { + "id": "amount", + "label": "Amount", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0.5, + "dmx_slots": [ + 1 + ] + }, + { + "id": "radius", + "label": "Radius", + "type": "float", + "minimum": 0, + "maximum": 2, + "default": 0.5, + "dmx_slots": [ + 2 + ] + }, + { + "id": "softness", + "label": "Softness", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0.4, + "dmx_slots": [ + 3 + ] + }, + { + "id": "roundness", + "label": "Roundness", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0.5, + "dmx_slots": [ + 4 + ] + }, + { + "id": "center_x", + "label": "Center X", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0.5, + "dmx_slots": [ + 5 + ] + }, + { + "id": "center_y", + "label": "Center Y", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 0.5, + "dmx_slots": [ + 6 + ] + }, + { + "id": "color", + "label": "Color", + "type": "color", + "default": [ + 0, + 0, + 0 + ], + "dmx_slots": [ + 7 + ] + }, + { + "id": "invert", + "label": "Invert", + "type": "bool", + "default": 0, + "dmx_slots": [ + 8 + ] + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 1, + "dmx_slots": [] + } + ], + "failure_mode": "bypass", + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 1.0, + "samples": 1 + }, + { + "id": "medium", + "internal_scale": 1.0, + "samples": 1 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 1 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "mix" + ] + } +} diff --git a/plugins/builtin/filters/com.hms.fx.vignette/shaders/d3d11/main.hlsl b/plugins/builtin/filters/com.hms.fx.vignette/shaders/d3d11/main.hlsl new file mode 100644 index 0000000..78c58bc --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.vignette/shaders/d3d11/main.hlsl @@ -0,0 +1,64 @@ +// HMS MediaEngine - Vignette (PLAN.md §15.2) +// Randabdunklung/-färbung; ein GPU-Pass. + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_amount; + float param_radius; + float param_softness; + float param_roundness; + float param_center_x; + float param_center_y; + float param_color_r; + float param_color_g; + float param_color_b; + float param_invert; // 0 = abdunkeln, 1 = aufhellen + float param_mix; + float _pad0; + float _pad1; + float _pad2; + float _pad3; +}; + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.01) + { + return src; // Mix 0 = kostenloser Bypass (§15.3) + } + + float2 c = float2(param_center_x, param_center_y); + float2 d = uv - c; + d.x *= lerp(1.0, u_resolution.x / max(u_resolution.y, 1.0), param_roundness); + float dist = length(d); + + float radius = max(param_radius, 0.0001); + float soft = max(param_softness, 0.0001); + float v = smoothstep(radius, radius + soft, dist); + v = saturate(v * param_amount); + if (param_invert > 0.5) + v = 1.0 - v; + + float3 vignette = lerp(src.rgb, float3(param_color_r, param_color_g, param_color_b), v); + float3 rgb = lerp(src.rgb, vignette, m); + float4 out_c = float4(rgb, src.a); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + return out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.vignette/shaders/gl/main.frag b/plugins/builtin/filters/com.hms.fx.vignette/shaders/gl/main.frag new file mode 100644 index 0000000..b59f0eb --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.vignette/shaders/gl/main.frag @@ -0,0 +1,60 @@ +#version 330 core +// HMS MediaEngine - Vignette (GLSL, Linux x64) +// Semantik identisch zur HLSL-Variante (§12.6, §15.4). + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_amount; +uniform float param_radius; +uniform float param_softness; +uniform float param_roundness; +uniform float param_center_x; +uniform float param_center_y; +uniform float param_color_r; +uniform float param_color_g; +uniform float param_color_b; +uniform float param_invert; +uniform float param_mix; + +in vec2 v_uv; +out vec4 fragColor; + +void main() +{ + vec4 src = texture(u_input_texture, v_uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.01) + { + fragColor = src; + return; + } + + vec2 c = vec2(param_center_x, param_center_y); + vec2 d = v_uv - c; + d.x *= mix(1.0, u_resolution.x / max(u_resolution.y, 1.0), param_roundness); + float dist = length(d); + + float radius = max(param_radius, 0.0001); + float soft = max(param_softness, 0.0001); + float v = smoothstep(radius, radius + soft, dist); + v = clamp(v * param_amount, 0.0, 1.0); + if (param_invert > 0.5) + v = 1.0 - v; + + vec3 vignette = mix(src.rgb, vec3(param_color_r, param_color_g, param_color_b), v); + vec3 rgb = mix(src.rgb, vignette, m); + vec4 out_c = vec4(rgb, src.a); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + fragColor = out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.vignette/shaders/gles/main.frag b/plugins/builtin/filters/com.hms.fx.vignette/shaders/gles/main.frag new file mode 100644 index 0000000..e30e425 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.vignette/shaders/gles/main.frag @@ -0,0 +1,60 @@ +#version 100 +// HMS MediaEngine - Vignette (GLES, Raspberry Pi) +// ES 2.0; ein GPU-Pass. +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_amount; +uniform float param_radius; +uniform float param_softness; +uniform float param_roundness; +uniform float param_center_x; +uniform float param_center_y; +uniform float param_color_r; +uniform float param_color_g; +uniform float param_color_b; +uniform float param_invert; +uniform float param_mix; + +varying vec2 v_uv; + +void main() +{ + vec4 src = texture2D(u_input_texture, v_uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.01) + { + gl_FragColor = src; + return; + } + + vec2 c = vec2(param_center_x, param_center_y); + vec2 d = v_uv - c; + d.x *= mix(1.0, u_resolution.x / max(u_resolution.y, 1.0), param_roundness); + float dist = length(d); + + float radius = max(param_radius, 0.0001); + float soft = max(param_softness, 0.0001); + float v = smoothstep(radius, radius + soft, dist); + v = clamp(v * param_amount, 0.0, 1.0); + if (param_invert > 0.5) + v = 1.0 - v; + + vec3 vignette = mix(src.rgb, vec3(param_color_r, param_color_g, param_color_b), v); + vec3 rgb = mix(src.rgb, vignette, m); + vec4 out_c = vec4(rgb, src.a); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + gl_FragColor = out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.wave_displace/plugin.json b/plugins/builtin/filters/com.hms.fx.wave_displace/plugin.json new file mode 100644 index 0000000..0606e31 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.wave_displace/plugin.json @@ -0,0 +1,168 @@ +{ + "schema_version": 1, + "id": "com.hms.fx.wave_displace", + "name": "Wave Displace", + "version": "1.0.0", + "api_version": 1, + "kind": "filter", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl", + "passes": [ + { + "pixel_shader": "shaders/d3d11/main.hlsl" + } + ] + }, + "gl": { + "type": "glsl", + "passes": [ + { + "fragment": "shaders/gl/main.frag" + } + ] + }, + "gles": { + "type": "glsl_es", + "passes": [ + { + "fragment": "shaders/gles/main.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "PI_LITE", + "requires_input_texture": true, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "parameters": [ + { + "id": "amount_x", + "label": "Amount X", + "type": "float", + "minimum": 0, + "maximum": 100, + "default": 10, + "dmx_slots": [ + 1 + ] + }, + { + "id": "amount_y", + "label": "Amount Y", + "type": "float", + "minimum": 0, + "maximum": 100, + "default": 10, + "dmx_slots": [ + 2 + ] + }, + { + "id": "frequency", + "label": "Frequency", + "type": "float", + "minimum": 0, + "maximum": 20, + "default": 2, + "dmx_slots": [ + 3 + ] + }, + { + "id": "angle", + "label": "Angle", + "type": "float", + "minimum": 0, + "maximum": 360, + "default": 0, + "dmx_slots": [ + 4 + ] + }, + { + "id": "phase", + "label": "Phase", + "type": "float", + "minimum": 0, + "maximum": 6.283, + "default": 0, + "dmx_slots": [ + 5 + ] + }, + { + "id": "speed", + "label": "Speed", + "type": "float", + "minimum": 0, + "maximum": 10, + "default": 1, + "dmx_slots": [ + 6 + ] + }, + { + "id": "noise_scale", + "label": "Noise Scale", + "type": "float", + "minimum": 0.1, + "maximum": 20, + "default": 4, + "dmx_slots": [ + 7 + ] + }, + { + "id": "edge_mode", + "label": "Edge Mode", + "type": "enum", + "values": [ + "clamp", + "repeat" + ], + "default": "clamp", + "dmx_slots": [ + 8 + ] + }, + { + "id": "mix", + "label": "Mix", + "type": "float", + "minimum": 0, + "maximum": 1, + "default": 1, + "dmx_slots": [] + } + ], + "failure_mode": "bypass", + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "noise_octaves": 4 + }, + { + "id": "medium", + "noise_octaves": 8 + }, + { + "id": "high", + "noise_octaves": 16 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "amount_x", + "amount_y" + ] + } +} diff --git a/plugins/builtin/filters/com.hms.fx.wave_displace/shaders/d3d11/main.hlsl b/plugins/builtin/filters/com.hms.fx.wave_displace/shaders/d3d11/main.hlsl new file mode 100644 index 0000000..6448707 --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.wave_displace/shaders/d3d11/main.hlsl @@ -0,0 +1,99 @@ +// HMS MediaEngine - Wave Displace (PLAN.md §15.2) +// Wave-, Turbulence- und Noise-Displacement; Noise-Detail adaptiv (§15.2). +// Amount X/Y bleiben bei Qualitätswechsel semantisch unverändert. + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float param_amount_x; + float param_amount_y; + float param_frequency; + float param_angle; + float param_phase; + float param_speed; + float param_noise_scale; + float param_edge_mode; // 0 = clamp, 1 = repeat + float param_mix; + float u_quality_octaves; // 4 | 8 | 16 je Variante + float _pad0; + float _pad1; + float _pad2; +}; + +float hash21(float2 p) +{ + p = frac(p * float2(123.34, 456.21)); + p += dot(p, p + 45.32); + return frac(p.x * p.y); +} + +float value_noise(float2 p) +{ + float2 i = floor(p); + float2 f = frac(p); + f = f * f * (3.0 - 2.0 * f); + float a = hash21(i); + float b = hash21(i + float2(1.0, 0.0)); + float c = hash21(i + float2(0.0, 1.0)); + float d = hash21(i + float2(1.0, 1.0)); + return lerp(lerp(a, b, f.x), lerp(c, d, f.x), f.y); +} + +float fbm(float2 p, float octaves) +{ + float v = 0.0; + float amp = 0.5; + float freq = 1.0; + [loop] + for (float i = 0.0; i < octaves; i += 1.0) + { + v += amp * value_noise(p * freq); + amp *= 0.5; + freq *= 2.0; + } + return v; +} + +float2 wrap_uv(float2 uv) +{ + if (param_edge_mode > 0.5) + return frac(uv); + return clamp(uv, 0.0, 1.0); +} + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 src = u_input_texture.Sample(u_sampler, uv); + float m = saturate(param_mix); + if (m < 0.01) + { + return src; // Mix 0 = kostenloser Bypass (§15.3) + } + + float angle = radians(param_angle); + float2 dir = float2(cos(angle), sin(angle)); + float t = u_time_seconds * param_speed; + float wave = sin(dot(uv, dir) * param_frequency * 6.28318 + t + param_phase); + float noise = fbm(uv * param_noise_scale + t * 0.1, u_quality_octaves); + float field = wave * 0.5 + noise; + float2 offset = float2(field * param_amount_x, field * param_amount_y) * 0.01; + + float4 displaced = u_input_texture.Sample(u_sampler, wrap_uv(uv + offset)); + float4 out_c = lerp(src, displaced, m); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + return out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.wave_displace/shaders/gl/main.frag b/plugins/builtin/filters/com.hms.fx.wave_displace/shaders/gl/main.frag new file mode 100644 index 0000000..2c5e2ae --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.wave_displace/shaders/gl/main.frag @@ -0,0 +1,94 @@ +#version 330 core +// HMS MediaEngine - Wave Displace (GLSL, Linux x64) +// Semantik identisch zur HLSL-Variante (§12.6, §15.4). + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_amount_x; +uniform float param_amount_y; +uniform float param_frequency; +uniform float param_angle; +uniform float param_phase; +uniform float param_speed; +uniform float param_noise_scale; +uniform float param_edge_mode; +uniform float param_mix; +uniform float u_quality_octaves; + +in vec2 v_uv; +out vec4 fragColor; + +float hash21(vec2 p) +{ + p = fract(p * vec2(123.34, 456.21)); + p += dot(p, p + 45.32); + return fract(p.x * p.y); +} + +float value_noise(vec2 p) +{ + vec2 i = floor(p); + vec2 f = fract(p); + f = f * f * (3.0 - 2.0 * f); + float a = hash21(i); + float b = hash21(i + vec2(1.0, 0.0)); + float c = hash21(i + vec2(0.0, 1.0)); + float d = hash21(i + vec2(1.0, 1.0)); + return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); +} + +float fbm(vec2 p, float octaves) +{ + float v = 0.0; + float amp = 0.5; + float freq = 1.0; + for (float i = 0.0; i < octaves; i += 1.0) + { + v += amp * value_noise(p * freq); + amp *= 0.5; + freq *= 2.0; + } + return v; +} + +vec2 wrap_uv(vec2 uv) +{ + if (param_edge_mode > 0.5) + return fract(uv); + return clamp(uv, 0.0, 1.0); +} + +void main() +{ + vec4 src = texture(u_input_texture, v_uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.01) + { + fragColor = src; + return; + } + + float angle = radians(param_angle); + vec2 dir = vec2(cos(angle), sin(angle)); + float t = u_time_seconds * param_speed; + float wave = sin(dot(v_uv, dir) * param_frequency * 6.28318 + t + param_phase); + float noise = fbm(v_uv * param_noise_scale + t * 0.1, u_quality_octaves); + float field = wave * 0.5 + noise; + vec2 offset = vec2(field * param_amount_x, field * param_amount_y) * 0.01; + + vec4 displaced = texture(u_input_texture, wrap_uv(v_uv + offset)); + vec4 out_c = mix(src, displaced, m); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + fragColor = out_c; +} diff --git a/plugins/builtin/filters/com.hms.fx.wave_displace/shaders/gles/main.frag b/plugins/builtin/filters/com.hms.fx.wave_displace/shaders/gles/main.frag new file mode 100644 index 0000000..14ed3bd --- /dev/null +++ b/plugins/builtin/filters/com.hms.fx.wave_displace/shaders/gles/main.frag @@ -0,0 +1,95 @@ +#version 100 +// HMS MediaEngine - Wave Displace (GLES, Raspberry Pi) +// ES 2.0: Schleifen mit konstanter Höchstgrenze; Abbruch über Bedingung. +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float param_amount_x; +uniform float param_amount_y; +uniform float param_frequency; +uniform float param_angle; +uniform float param_phase; +uniform float param_speed; +uniform float param_noise_scale; +uniform float param_edge_mode; +uniform float param_mix; +uniform float u_quality_octaves; + +varying vec2 v_uv; + +float hash21(vec2 p) +{ + p = fract(p * vec2(123.34, 456.21)); + p += dot(p, p + 45.32); + return fract(p.x * p.y); +} + +float value_noise(vec2 p) +{ + vec2 i = floor(p); + vec2 f = fract(p); + f = f * f * (3.0 - 2.0 * f); + float a = hash21(i); + float b = hash21(i + vec2(1.0, 0.0)); + float c = hash21(i + vec2(0.0, 1.0)); + float d = hash21(i + vec2(1.0, 1.0)); + return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); +} + +float fbm(vec2 p, float octaves) +{ + float v = 0.0; + float amp = 0.5; + float freq = 1.0; + for (int i = 0; i < 16; i++) + { + if (float(i) >= octaves) { break; } + v += amp * value_noise(p * freq); + amp *= 0.5; + freq *= 2.0; + } + return v; +} + +vec2 wrap_uv(vec2 uv) +{ + if (param_edge_mode > 0.5) + return fract(uv); + return clamp(uv, 0.0, 1.0); +} + +void main() +{ + vec4 src = texture2D(u_input_texture, v_uv); + float m = clamp(param_mix, 0.0, 1.0); + if (m < 0.01) + { + gl_FragColor = src; + return; + } + + float angle = radians(param_angle); + vec2 dir = vec2(cos(angle), sin(angle)); + float t = u_time_seconds * param_speed; + float wave = sin(dot(v_uv, dir) * param_frequency * 6.28318 + t + param_phase); + float noise = fbm(v_uv * param_noise_scale + t * 0.1, u_quality_octaves); + float field = wave * 0.5 + noise; + vec2 offset = vec2(field * param_amount_x, field * param_amount_y) * 0.01; + + vec4 displaced = texture2D(u_input_texture, wrap_uv(v_uv + offset)); + vec4 out_c = mix(src, displaced, m); + out_c.rgb *= u_layer_opacity; + out_c.a *= u_layer_opacity; + gl_FragColor = out_c; +} diff --git a/plugins/builtin/generators/com.hms.generator.checker_grid/plugin.json b/plugins/builtin/generators/com.hms.generator.checker_grid/plugin.json new file mode 100644 index 0000000..5634acf --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.checker_grid/plugin.json @@ -0,0 +1,155 @@ +{ + "schema_version": 1, + "id": "com.hms.generator.checker_grid", + "name": "Checker Grid", + "version": "1.0.0", + "api_version": 1, + "kind": "generator", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl_singlepass", + "passes": [ + { + "pixel_shader": "shaders/d3d11/checker_grid.hlsl" + } + ] + }, + "gl": { + "type": "glsl_singlepass", + "passes": [ + { + "fragment": "shaders/gl/checker_grid.frag" + } + ] + }, + "gles": { + "type": "glsl_es_singlepass", + "passes": [ + { + "fragment": "shaders/gles/checker_grid.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "DESKTOP_LITE", + "requires_input_texture": false, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 0.5, + "samples": 1 + }, + { + "id": "medium", + "internal_scale": 1.0, + "samples": 1 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 1 + } + ] + }, + "parameters": [ + { + "id": "color_a", + "label": "Color A", + "type": "color", + "default": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "dmx_slots": [ + 1, + 2, + 3, + 4 + ] + }, + { + "id": "color_b", + "label": "Color B", + "type": "color", + "default": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "dmx_slots": [ + 5, + 6, + 7, + 8 + ] + }, + { + "id": "cell_size", + "label": "Cell Size", + "type": "float", + "minimum": 0.01, + "maximum": 1.0, + "default": 0.1, + "curve": "quadratic" + }, + { + "id": "line_width", + "label": "Line Width", + "type": "float", + "minimum": 0.0, + "maximum": 0.5, + "default": 0.02, + "curve": "linear" + }, + { + "id": "offset_x", + "label": "Offset X", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 0.0, + "curve": "linear" + }, + { + "id": "offset_y", + "label": "Offset Y", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 0.0, + "curve": "linear" + }, + { + "id": "angle", + "label": "Angle", + "type": "float", + "minimum": 0.0, + "maximum": 360.0, + "default": 0.0, + "curve": "linear" + }, + { + "id": "speed", + "label": "Speed", + "type": "float", + "minimum": -2.0, + "maximum": 2.0, + "default": 0.5, + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/generators/com.hms.generator.checker_grid/shaders/d3d11/checker_grid.hlsl b/plugins/builtin/generators/com.hms.generator.checker_grid/shaders/d3d11/checker_grid.hlsl new file mode 100644 index 0000000..8f41b18 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.checker_grid/shaders/d3d11/checker_grid.hlsl @@ -0,0 +1,51 @@ +// HMS MediaEngine - Generator (D3D11) +// Erzeugt ohne Eingangstextur; cbuffer-Satz identisch zu Filtern (§12.6). + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float4 param_color_a; + float4 param_color_b; + float param_cell_size; + float param_line_width; + float param_offset_x; + float param_offset_y; + float param_angle; + float param_speed; + float _pad0; + float _pad1; + float _pad2; +}; + + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float ang = radians(param_angle); + float2 dir = float2(cos(ang), sin(ang)); + float2 perp = float2(-dir.y, dir.x); + float2 p = uv + float2(param_offset_x, param_offset_y); + p += dir * (u_time_seconds * param_speed); + float2 coord = float2(dot(p, dir), dot(p, perp)) / max(param_cell_size, 0.0001); + float2 cell = floor(coord); + float2 f = frac(coord); + float checker = step(0.5, frac(cell.x * 0.5 + cell.y * 0.5)); + float lw = max(param_line_width, 0.0); + float line = step(1.0 - lw, min(f.x, f.y)); + float4 col = lerp(param_color_a, param_color_b, checker); + col = lerp(col, param_color_b, line); + col.a *= u_layer_opacity; + return col; +} diff --git a/plugins/builtin/generators/com.hms.generator.checker_grid/shaders/gl/checker_grid.frag b/plugins/builtin/generators/com.hms.generator.checker_grid/shaders/gl/checker_grid.frag new file mode 100644 index 0000000..e077bcb --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.checker_grid/shaders/gl/checker_grid.frag @@ -0,0 +1,47 @@ +#version 330 core +// HMS MediaEngine - Generator (GLSL, Linux x64) +// Semantik identisch zur HLSL-Variante (§12.6, §15.4). + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform vec4 param_color_a; +uniform vec4 param_color_b; +uniform float param_cell_size; +uniform float param_line_width; +uniform float param_offset_x; +uniform float param_offset_y; +uniform float param_angle; +uniform float param_speed; + + +in vec2 v_uv; +out vec4 fragColor; + +void main() +{ + float ang = radians(param_angle); + vec2 dir = vec2(cos(ang), sin(ang)); + vec2 perp = vec2(-dir.y, dir.x); + vec2 p = v_uv + vec2(param_offset_x, param_offset_y); + p += dir * (u_time_seconds * param_speed); + vec2 coord = vec2(dot(p, dir), dot(p, perp)) / max(param_cell_size, 0.0001); + vec2 cell = floor(coord); + vec2 f = fract(coord); + float checker = step(0.5, fract(cell.x * 0.5 + cell.y * 0.5)); + float lw = max(param_line_width, 0.0); + float line = step(1.0 - lw, min(f.x, f.y)); + vec4 col = mix(param_color_a, param_color_b, checker); + col = mix(col, param_color_b, line); + col.a *= u_layer_opacity; + fragColor = col; +} diff --git a/plugins/builtin/generators/com.hms.generator.checker_grid/shaders/gles/checker_grid.frag b/plugins/builtin/generators/com.hms.generator.checker_grid/shaders/gles/checker_grid.frag new file mode 100644 index 0000000..e97ea84 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.checker_grid/shaders/gles/checker_grid.frag @@ -0,0 +1,47 @@ +#version 100 +// HMS MediaEngine - Generator (GLES, Raspberry Pi) +// ES 2.0: texture2D, varying, gl_FragColor, Schleifen mit konstanter Höchstgrenze. +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform vec4 param_color_a; +uniform vec4 param_color_b; +uniform float param_cell_size; +uniform float param_line_width; +uniform float param_offset_x; +uniform float param_offset_y; +uniform float param_angle; +uniform float param_speed; + + +varying vec2 v_uv; + +void main() +{ + float ang = radians(param_angle); + vec2 dir = vec2(cos(ang), sin(ang)); + vec2 perp = vec2(-dir.y, dir.x); + vec2 p = v_uv + vec2(param_offset_x, param_offset_y); + p += dir * (u_time_seconds * param_speed); + vec2 coord = vec2(dot(p, dir), dot(p, perp)) / max(param_cell_size, 0.0001); + vec2 cell = floor(coord); + vec2 f = fract(coord); + float checker = step(0.5, fract(cell.x * 0.5 + cell.y * 0.5)); + float lw = max(param_line_width, 0.0); + float line = step(1.0 - lw, min(f.x, f.y)); + vec4 col = mix(param_color_a, param_color_b, checker); + col = mix(col, param_color_b, line); + col.a *= u_layer_opacity; + gl_FragColor = col; +} diff --git a/plugins/builtin/generators/com.hms.generator.drops_ripples/plugin.json b/plugins/builtin/generators/com.hms.generator.drops_ripples/plugin.json new file mode 100644 index 0000000..ee02345 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.drops_ripples/plugin.json @@ -0,0 +1,181 @@ +{ + "schema_version": 1, + "id": "com.hms.generator.drops_ripples", + "name": "Drops & Ripples", + "version": "1.0.0", + "api_version": 1, + "kind": "generator", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl_singlepass", + "passes": [ + { + "pixel_shader": "shaders/d3d11/drops_ripples.hlsl" + } + ] + }, + "gl": { + "type": "glsl_singlepass", + "passes": [ + { + "fragment": "shaders/gl/drops_ripples.frag" + } + ] + }, + "gles": { + "type": "glsl_es_singlepass", + "passes": [ + { + "fragment": "shaders/gles/drops_ripples.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "DESKTOP_LITE", + "requires_input_texture": false, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 0.5, + "samples": 8 + }, + { + "id": "medium", + "internal_scale": 0.75, + "samples": 24 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 64 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "density", + "seed" + ] + }, + "parameters": [ + { + "id": "color_a", + "label": "Color A", + "type": "color", + "default": [ + 0.2, + 0.8, + 1.0, + 1.0 + ], + "dmx_slots": [ + 1 + ] + }, + { + "id": "color_b", + "label": "Color B", + "type": "color", + "default": [ + 1.0, + 0.4, + 0.2, + 1.0 + ], + "dmx_slots": [ + 2 + ] + }, + { + "id": "density", + "label": "Density", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.5, + "dmx_slots": [ + 3 + ], + "curve": "linear" + }, + { + "id": "size", + "label": "Size", + "type": "float", + "minimum": 0.01, + "maximum": 0.3, + "default": 0.05, + "dmx_slots": [ + 4 + ], + "curve": "quadratic" + }, + { + "id": "lifetime", + "label": "Lifetime", + "type": "float", + "minimum": 0.1, + "maximum": 4.0, + "default": 1.5, + "dmx_slots": [ + 5 + ], + "curve": "linear" + }, + { + "id": "speed", + "label": "Speed", + "type": "float", + "minimum": 0.0, + "maximum": 4.0, + "default": 1.0, + "dmx_slots": [ + 6 + ], + "curve": "linear" + }, + { + "id": "ripple_width", + "label": "Ripple Width", + "type": "float", + "minimum": 0.5, + "maximum": 20.0, + "default": 6.0, + "dmx_slots": [ + 7 + ], + "curve": "linear" + }, + { + "id": "direction", + "label": "Direction", + "type": "float", + "minimum": 0.0, + "maximum": 6.2831853, + "default": 0.0, + "dmx_slots": [ + 8 + ], + "curve": "linear" + }, + { + "id": "seed", + "label": "Seed", + "type": "float", + "minimum": 0.0, + "maximum": 100.0, + "default": 1.0, + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/generators/com.hms.generator.drops_ripples/shaders/d3d11/drops_ripples.hlsl b/plugins/builtin/generators/com.hms.generator.drops_ripples/shaders/d3d11/drops_ripples.hlsl new file mode 100644 index 0000000..cfd91a6 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.drops_ripples/shaders/d3d11/drops_ripples.hlsl @@ -0,0 +1,74 @@ +// Drops & Ripples Generator +// HMS MediaEngine – Generator (kein Eingangstextur-Bezug, cbuffer identisch §14.4) +// D3D11-Pixelshader (PS_5_0). Semantik identisch zu GL/GLES (§12.6, §15.1). + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float4 param_color_a; + float4 param_color_b; + float param_density; + float param_size; + float param_lifetime; + float param_speed; + float param_ripple_width; + float param_direction; + float param_seed; + float u_quality_samples; // AQ-Variante (Backend-Bindung) + float _pad0; // 16-Byte-Alignment + float _pad1; // 16-Byte-Alignment +}; + +float hash21(float2 p) +{ + p = frac(p * float2(123.34, 456.21)); + p += dot(p, p + 45.32); + return frac(p.x * p.y); +} + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float2 p = uv - 0.5; + float t = u_time_seconds * param_speed; + float maxN = clamp(u_quality_samples, 1.0, 64.0); + float dirx = cos(param_direction); + float diry = sin(param_direction); + float acc = 0.0; + float colAcc = 0.0; + [loop] + for (int i = 0; i < 64; i++) + { + if (float(i) >= maxN) { break; } + float2 base = float2(hash21(float2(float(i) + param_seed, 1.0)), hash21(float2(float(i) + param_seed, 2.0))); + float life = frac(t * 0.5 + float(i) * 0.37 + param_seed); + float age = life * param_lifetime; + float2 pos = base - 0.5 + float2(dirx, diry) * (age * 0.15); + pos = frac(pos + 0.5) - 0.5; + float2 dp = p - pos; + float dist = length(dp); + float drop = exp(-dist * dist / max(param_size * param_size, 1e-5)); + float ring = sin(dist * param_ripple_width - age * 8.0); + float ringMask = exp(-abs(dist - age * 0.3) * param_ripple_width * 0.5); + float v = drop + ring * ringMask * 0.5; + v *= (1.0 - life); + acc += v; + colAcc += v * (float(i) / max(maxN - 1.0, 1.0)); + } + acc = saturate(acc); + float mixT = maxN > 1.0 ? colAcc / max(acc, 1e-5) : 0.0; + float3 col = lerp(param_color_a.rgb, param_color_b.rgb, mixT); + return float4(col * acc * u_layer_opacity, acc * u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.drops_ripples/shaders/gl/drops_ripples.frag b/plugins/builtin/generators/com.hms.generator.drops_ripples/shaders/gl/drops_ripples.frag new file mode 100644 index 0000000..6a6c7e4 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.drops_ripples/shaders/gl/drops_ripples.frag @@ -0,0 +1,68 @@ +// Drops & Ripples Generator +// HMS MediaEngine – Generator (GLSL, Linux x64). Semantik identisch zu HLSL/GLES (§12.6, §15.1). +#version 330 core + +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float4 param_color_a; +uniform float4 param_color_b; +uniform float param_density; +uniform float param_size; +uniform float param_lifetime; +uniform float param_speed; +uniform float param_ripple_width; +uniform float param_direction; +uniform float param_seed; +uniform float u_quality_samples; + +in vec2 v_uv; +out vec4 fragColor; + +float hash21(vec2 p) +{ + p = fract(p * vec2(123.34, 456.21)); + p += dot(p, p + 45.32); + return fract(p.x * p.y); +} + +void main() +{ + vec2 p = v_uv - 0.5; + float t = u_time_seconds * param_speed; + float maxN = clamp(u_quality_samples, 1.0, 64.0); + float dirx = cos(param_direction); + float diry = sin(param_direction); + float acc = 0.0; + float colAcc = 0.0; + for (int i = 0; i < 64; i++) + { + if (float(i) >= maxN) { break; } + vec2 base = vec2(hash21(vec2(float(i) + param_seed, 1.0)), hash21(vec2(float(i) + param_seed, 2.0))); + float life = fract(t * 0.5 + float(i) * 0.37 + param_seed); + float age = life * param_lifetime; + vec2 pos = base - 0.5 + vec2(dirx, diry) * (age * 0.15); + pos = fract(pos + 0.5) - 0.5; + vec2 dp = p - pos; + float dist = length(dp); + float drop = exp(-dist * dist / max(param_size * param_size, 1e-5)); + float ring = sin(dist * param_ripple_width - age * 8.0); + float ringMask = exp(-abs(dist - age * 0.3) * param_ripple_width * 0.5); + float v = drop + ring * ringMask * 0.5; + v *= (1.0 - life); + acc += v; + colAcc += v * (float(i) / max(maxN - 1.0, 1.0)); + } + acc = clamp(acc, 0.0, 1.0); + float mixT = maxN > 1.0 ? colAcc / max(acc, 1e-5) : 0.0; + vec3 col = mix(param_color_a.rgb, param_color_b.rgb, mixT); + fragColor = vec4(col * acc * u_layer_opacity, acc * u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.drops_ripples/shaders/gles/drops_ripples.frag b/plugins/builtin/generators/com.hms.generator.drops_ripples/shaders/gles/drops_ripples.frag new file mode 100644 index 0000000..3a44384 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.drops_ripples/shaders/gles/drops_ripples.frag @@ -0,0 +1,68 @@ +// Drops & Ripples Generator +// HMS MediaEngine – Generator (GLES, Raspberry Pi). ES 2.0: texture2D, gl_FragColor, Schleifen mit konstanter Höchstgrenze. +#version 100 +precision mediump float; + +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float4 param_color_a; +uniform float4 param_color_b; +uniform float param_density; +uniform float param_size; +uniform float param_lifetime; +uniform float param_speed; +uniform float param_ripple_width; +uniform float param_direction; +uniform float param_seed; +uniform float u_quality_samples; + +varying vec2 v_uv; + +float hash21(vec2 p) +{ + p = fract(p * vec2(123.34, 456.21)); + p += dot(p, p + 45.32); + return fract(p.x * p.y); +} + +void main() +{ + vec2 p = v_uv - 0.5; + float t = u_time_seconds * param_speed; + float maxN = clamp(u_quality_samples, 1.0, 64.0); + float dirx = cos(param_direction); + float diry = sin(param_direction); + float acc = 0.0; + float colAcc = 0.0; + for (int i = 0; i < 64; i++) + { + if (float(i) >= maxN) { break; } + vec2 base = vec2(hash21(vec2(float(i) + param_seed, 1.0)), hash21(vec2(float(i) + param_seed, 2.0))); + float life = fract(t * 0.5 + float(i) * 0.37 + param_seed); + float age = life * param_lifetime; + vec2 pos = base - 0.5 + vec2(dirx, diry) * (age * 0.15); + pos = fract(pos + 0.5) - 0.5; + vec2 dp = p - pos; + float dist = length(dp); + float drop = exp(-dist * dist / max(param_size * param_size, 1e-5)); + float ring = sin(dist * param_ripple_width - age * 8.0); + float ringMask = exp(-abs(dist - age * 0.3) * param_ripple_width * 0.5); + float v = drop + ring * ringMask * 0.5; + v *= (1.0 - life); + acc += v; + colAcc += v * (float(i) / max(maxN - 1.0, 1.0)); + } + acc = clamp(acc, 0.0, 1.0); + float mixT = maxN > 1.0 ? colAcc / max(acc, 1e-5) : 0.0; + vec3 col = mix(param_color_a.rgb, param_color_b.rgb, mixT); + gl_FragColor = vec4(col * acc * u_layer_opacity, acc * u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.gradient/plugin.json b/plugins/builtin/generators/com.hms.generator.gradient/plugin.json new file mode 100644 index 0000000..7a46b8c --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.gradient/plugin.json @@ -0,0 +1,175 @@ +{ + "schema_version": 1, + "id": "com.hms.generator.gradient", + "name": "Gradient", + "version": "1.0.0", + "api_version": 1, + "kind": "generator", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl_singlepass", + "passes": [ + { + "pixel_shader": "shaders/d3d11/gradient.hlsl" + } + ] + }, + "gl": { + "type": "glsl_singlepass", + "passes": [ + { + "fragment": "shaders/gl/gradient.frag" + } + ] + }, + "gles": { + "type": "glsl_es_singlepass", + "passes": [ + { + "fragment": "shaders/gles/gradient.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "DESKTOP_LITE", + "requires_input_texture": false, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 0.5, + "samples": 4 + }, + { + "id": "medium", + "internal_scale": 0.75, + "samples": 8 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 16 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "type", + "angle", + "center_x", + "center_y", + "width", + "phase", + "speed" + ] + }, + "parameters": [ + { + "id": "color_a", + "label": "Color A", + "type": "color", + "default": [ + 1.0, + 0.0, + 0.0, + 1.0 + ], + "dmx_slots": [ + 1, + 2, + 3, + 4 + ] + }, + { + "id": "color_b", + "label": "Color B", + "type": "color", + "default": [ + 0.0, + 0.0, + 1.0, + 1.0 + ], + "dmx_slots": [ + 5, + 6, + 7, + 8 + ] + }, + { + "id": "type", + "label": "Type", + "type": "enum", + "values": [ + "linear", + "radial" + ], + "default": "linear" + }, + { + "id": "angle", + "label": "Angle", + "type": "float", + "minimum": 0.0, + "maximum": 360.0, + "default": 0.0, + "curve": "linear" + }, + { + "id": "center_x", + "label": "Center X", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 0.0, + "curve": "linear" + }, + { + "id": "center_y", + "label": "Center Y", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 0.0, + "curve": "linear" + }, + { + "id": "width", + "label": "Width", + "type": "float", + "minimum": 0.1, + "maximum": 2.0, + "default": 0.5, + "curve": "linear" + }, + { + "id": "phase", + "label": "Phase", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.0, + "curve": "linear" + }, + { + "id": "speed", + "label": "Speed", + "type": "float", + "minimum": -2.0, + "maximum": 2.0, + "default": 0.5, + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/generators/com.hms.generator.gradient/shaders/d3d11/gradient.hlsl b/plugins/builtin/generators/com.hms.generator.gradient/shaders/d3d11/gradient.hlsl new file mode 100644 index 0000000..1767a9b --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.gradient/shaders/d3d11/gradient.hlsl @@ -0,0 +1,54 @@ +// HMS MediaEngine - Generator (D3D11) +// Erzeugt ohne Eingangstextur; cbuffer-Satz identisch zu Filtern (§12.6). + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float4 param_color_a; + float4 param_color_b; + float param_type; + float param_angle; + float param_center_x; + float param_center_y; + float param_width; + float param_phase; + float param_speed; + float _pad0; + float _pad1; + float _pad2; +}; + + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float2 p = uv - float2(param_center_x, param_center_y); + float ang = radians(param_angle); + float2 dir = float2(cos(ang), sin(ang)); + float t; + if (param_type < 0.5) + { + t = dot(p, dir) / max(param_width, 0.0001) + 0.5; + } + else + { + t = length(p) / max(param_width, 0.0001); + } + t += param_phase + u_time_seconds * param_speed; + t = frac(t); + float4 col = lerp(param_color_a, param_color_b, saturate(t)); + col.a *= u_layer_opacity; + return col; +} diff --git a/plugins/builtin/generators/com.hms.generator.gradient/shaders/gl/gradient.frag b/plugins/builtin/generators/com.hms.generator.gradient/shaders/gl/gradient.frag new file mode 100644 index 0000000..6b35a38 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.gradient/shaders/gl/gradient.frag @@ -0,0 +1,50 @@ +#version 330 core +// HMS MediaEngine - Generator (GLSL, Linux x64) +// Semantik identisch zur HLSL-Variante (§12.6, §15.4). + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform vec4 param_color_a; +uniform vec4 param_color_b; +uniform float param_type; +uniform float param_angle; +uniform float param_center_x; +uniform float param_center_y; +uniform float param_width; +uniform float param_phase; +uniform float param_speed; + + +in vec2 v_uv; +out vec4 fragColor; + +void main() +{ + vec2 p = v_uv - vec2(param_center_x, param_center_y); + float ang = radians(param_angle); + vec2 dir = vec2(cos(ang), sin(ang)); + float t; + if (param_type < 0.5) + { + t = dot(p, dir) / max(param_width, 0.0001) + 0.5; + } + else + { + t = length(p) / max(param_width, 0.0001); + } + t += param_phase + u_time_seconds * param_speed; + t = fract(t); + vec4 col = mix(param_color_a, param_color_b, clamp(t, 0.0, 1.0)); + col.a *= u_layer_opacity; + fragColor = col; +} diff --git a/plugins/builtin/generators/com.hms.generator.gradient/shaders/gles/gradient.frag b/plugins/builtin/generators/com.hms.generator.gradient/shaders/gles/gradient.frag new file mode 100644 index 0000000..8039725 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.gradient/shaders/gles/gradient.frag @@ -0,0 +1,50 @@ +#version 100 +// HMS MediaEngine - Generator (GLES, Raspberry Pi) +// ES 2.0: texture2D, varying, gl_FragColor, Schleifen mit konstanter Höchstgrenze. +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform vec4 param_color_a; +uniform vec4 param_color_b; +uniform float param_type; +uniform float param_angle; +uniform float param_center_x; +uniform float param_center_y; +uniform float param_width; +uniform float param_phase; +uniform float param_speed; + + +varying vec2 v_uv; + +void main() +{ + vec2 p = v_uv - vec2(param_center_x, param_center_y); + float ang = radians(param_angle); + vec2 dir = vec2(cos(ang), sin(ang)); + float t; + if (param_type < 0.5) + { + t = dot(p, dir) / max(param_width, 0.0001) + 0.5; + } + else + { + t = length(p) / max(param_width, 0.0001); + } + t += param_phase + u_time_seconds * param_speed; + t = fract(t); + vec4 col = mix(param_color_a, param_color_b, clamp(t, 0.0, 1.0)); + col.a *= u_layer_opacity; + gl_FragColor = col; +} diff --git a/plugins/builtin/generators/com.hms.generator.noise_clouds/plugin.json b/plugins/builtin/generators/com.hms.generator.noise_clouds/plugin.json new file mode 100644 index 0000000..19948e7 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.noise_clouds/plugin.json @@ -0,0 +1,179 @@ +{ + "schema_version": 1, + "id": "com.hms.generator.noise_clouds", + "name": "Noise Clouds", + "version": "1.0.0", + "api_version": 1, + "kind": "generator", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl_singlepass", + "passes": [ + { + "pixel_shader": "shaders/d3d11/noise_clouds.hlsl" + } + ] + }, + "gl": { + "type": "glsl_singlepass", + "passes": [ + { + "fragment": "shaders/gl/noise_clouds.frag" + } + ] + }, + "gles": { + "type": "glsl_es_singlepass", + "passes": [ + { + "fragment": "shaders/gles/noise_clouds.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "DESKTOP_LITE", + "requires_input_texture": false, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 0.5, + "samples": 2 + }, + { + "id": "medium", + "internal_scale": 0.75, + "samples": 3 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 5 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "seed", + "scale", + "contrast" + ] + }, + "parameters": [ + { + "id": "color_a", + "label": "Color A", + "type": "color", + "default": [ + 0.8, + 0.9, + 1.0, + 1.0 + ], + "dmx_slots": [ + 1, + 2, + 3, + 4 + ] + }, + { + "id": "color_b", + "label": "Color B", + "type": "color", + "default": [ + 0.1, + 0.1, + 0.2, + 1.0 + ], + "dmx_slots": [ + 5, + 6, + 7, + 8 + ] + }, + { + "id": "seed", + "label": "Seed", + "type": "float", + "minimum": 0.0, + "maximum": 100.0, + "default": 1.0, + "curve": "linear" + }, + { + "id": "scale", + "label": "Scale", + "type": "float", + "minimum": 0.1, + "maximum": 10.0, + "default": 2.0, + "curve": "quadratic" + }, + { + "id": "detail", + "label": "Detail", + "type": "int", + "minimum": 1, + "maximum": 5, + "default": 3, + "curve": "linear" + }, + { + "id": "contrast", + "label": "Contrast", + "type": "float", + "minimum": 0.1, + "maximum": 3.0, + "default": 1.0, + "curve": "linear" + }, + { + "id": "speed", + "label": "Speed", + "type": "float", + "minimum": -2.0, + "maximum": 2.0, + "default": 0.5, + "curve": "linear" + }, + { + "id": "direction_x", + "label": "Direction X", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 1.0, + "curve": "linear" + }, + { + "id": "direction_y", + "label": "Direction Y", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 0.0, + "curve": "linear" + }, + { + "id": "morph", + "label": "Morph", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.2, + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/generators/com.hms.generator.noise_clouds/shaders/d3d11/noise_clouds.hlsl b/plugins/builtin/generators/com.hms.generator.noise_clouds/shaders/d3d11/noise_clouds.hlsl new file mode 100644 index 0000000..d012991 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.noise_clouds/shaders/d3d11/noise_clouds.hlsl @@ -0,0 +1,79 @@ +// HMS MediaEngine - Generator (D3D11) +// Erzeugt ohne Eingangstextur; cbuffer-Satz identisch zu Filtern (§12.6). + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float4 param_color_a; + float4 param_color_b; + float param_seed; + float param_scale; + float param_detail; + float param_contrast; + float param_speed; + float param_direction_x; + float param_direction_y; + float param_morph; + float u_quality_octaves; + float _pad0; + float _pad1; + float _pad2; +}; + + +float hash21(float2 p) +{ + return frac(sin(dot(p, float2(127.1, 311.7))) * 43758.5453); +} + +float vnoise(float2 p) +{ + float2 i = floor(p); + float2 f = frac(p); + f = f * f * (3.0 - 2.0 * f); + float a = hash21(i); + float b = hash21(i + float2(1.0, 0.0)); + float c = hash21(i + float2(0.0, 1.0)); + float d = hash21(i + float2(1.0, 1.0)); + return lerp(lerp(a, b, f.x), lerp(c, d, f.x), f.y); +} + +float fbm(float2 p, float octaves) +{ + float v = 0.0; + float amp = 0.5; + float freq = 1.0; + for (float i = 0.0; i < octaves; i += 1.0) + { + v += amp * vnoise(p * freq); + freq *= 2.0; + amp *= 0.5; + } + return v; +} + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float2 p = uv * max(param_scale, 0.0001); + p += float2(param_direction_x, param_direction_y) * u_time_seconds * param_speed; + p += param_morph * sin(u_time_seconds * 0.5 + p * 2.0); + float n = fbm(p + param_seed * 100.0, clamp(u_quality_octaves, 1.0, 5.0)); + n = (n - 0.5) * param_contrast + 0.5; + n = saturate(n); + float4 col = lerp(param_color_a, param_color_b, n); + col.a *= u_layer_opacity; + return col; +} diff --git a/plugins/builtin/generators/com.hms.generator.noise_clouds/shaders/gl/noise_clouds.frag b/plugins/builtin/generators/com.hms.generator.noise_clouds/shaders/gl/noise_clouds.frag new file mode 100644 index 0000000..ae4804c --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.noise_clouds/shaders/gl/noise_clouds.frag @@ -0,0 +1,75 @@ +#version 330 core +// HMS MediaEngine - Generator (GLSL, Linux x64) +// Semantik identisch zur HLSL-Variante (§12.6, §15.4). + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform vec4 param_color_a; +uniform vec4 param_color_b; +uniform float param_seed; +uniform float param_scale; +uniform float param_detail; +uniform float param_contrast; +uniform float param_speed; +uniform float param_direction_x; +uniform float param_direction_y; +uniform float param_morph; +uniform float u_quality_octaves; + + +in vec2 v_uv; +out vec4 fragColor; + +float hash21(vec2 p) +{ + return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); +} + +float vnoise(vec2 p) +{ + vec2 i = floor(p); + vec2 f = fract(p); + f = f * f * (3.0 - 2.0 * f); + float a = hash21(i); + float b = hash21(i + vec2(1.0, 0.0)); + float c = hash21(i + vec2(0.0, 1.0)); + float d = hash21(i + vec2(1.0, 1.0)); + return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); +} + +float fbm(vec2 p, float octaves) +{ + float v = 0.0; + float amp = 0.5; + float freq = 1.0; + for (float i = 0.0; i < octaves; i += 1.0) + { + v += amp * vnoise(p * freq); + freq *= 2.0; + amp *= 0.5; + } + return v; +} + +void main() +{ + vec2 p = v_uv * max(param_scale, 0.0001); + p += vec2(param_direction_x, param_direction_y) * u_time_seconds * param_speed; + p += param_morph * sin(u_time_seconds * 0.5 + p * 2.0); + float n = fbm(p + param_seed * 100.0, clamp(u_quality_octaves, 1.0, 5.0)); + n = (n - 0.5) * param_contrast + 0.5; + n = clamp(n, 0.0, 1.0); + vec4 col = mix(param_color_a, param_color_b, n); + col.a *= u_layer_opacity; + fragColor = col; +} diff --git a/plugins/builtin/generators/com.hms.generator.noise_clouds/shaders/gles/noise_clouds.frag b/plugins/builtin/generators/com.hms.generator.noise_clouds/shaders/gles/noise_clouds.frag new file mode 100644 index 0000000..b849dfa --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.noise_clouds/shaders/gles/noise_clouds.frag @@ -0,0 +1,76 @@ +#version 100 +// HMS MediaEngine - Generator (GLES, Raspberry Pi) +// ES 2.0: texture2D, varying, gl_FragColor, Schleifen mit konstanter Höchstgrenze. +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform vec4 param_color_a; +uniform vec4 param_color_b; +uniform float param_seed; +uniform float param_scale; +uniform float param_detail; +uniform float param_contrast; +uniform float param_speed; +uniform float param_direction_x; +uniform float param_direction_y; +uniform float param_morph; +uniform float u_quality_octaves; + + +varying vec2 v_uv; + +float hash21(vec2 p) +{ + return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); +} + +float vnoise(vec2 p) +{ + vec2 i = floor(p); + vec2 f = fract(p); + f = f * f * (3.0 - 2.0 * f); + float a = hash21(i); + float b = hash21(i + vec2(1.0, 0.0)); + float c = hash21(i + vec2(0.0, 1.0)); + float d = hash21(i + vec2(1.0, 1.0)); + return mix(mix(a, b, f.x), mix(c, d, f.x), f.y); +} + +float fbm(vec2 p, float octaves) +{ + float v = 0.0; + float amp = 0.5; + float freq = 1.0; + for (int i = 0; i < 5; i++) + { + if (float(i) >= octaves) { break; } + v += amp * vnoise(p * freq); + freq *= 2.0; + amp *= 0.5; + } + return v; +} + +void main() +{ + vec2 p = v_uv * max(param_scale, 0.0001); + p += vec2(param_direction_x, param_direction_y) * u_time_seconds * param_speed; + p += param_morph * sin(u_time_seconds * 0.5 + p * 2.0); + float n = fbm(p + param_seed * 100.0, clamp(u_quality_octaves, 1.0, 5.0)); + n = (n - 0.5) * param_contrast + 0.5; + n = clamp(n, 0.0, 1.0); + vec4 col = mix(param_color_a, param_color_b, n); + col.a *= u_layer_opacity; + gl_FragColor = col; +} diff --git a/plugins/builtin/generators/com.hms.generator.plasma/plugin.json b/plugins/builtin/generators/com.hms.generator.plasma/plugin.json new file mode 100644 index 0000000..9d9510d --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.plasma/plugin.json @@ -0,0 +1,182 @@ +{ + "schema_version": 1, + "id": "com.hms.generator.plasma", + "name": "Plasma", + "version": "1.0.0", + "api_version": 1, + "kind": "generator", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl_singlepass", + "passes": [ + { + "pixel_shader": "shaders/d3d11/plasma.hlsl" + } + ] + }, + "gl": { + "type": "glsl_singlepass", + "passes": [ + { + "fragment": "shaders/gl/plasma.frag" + } + ] + }, + "gles": { + "type": "glsl_es_singlepass", + "passes": [ + { + "fragment": "shaders/gles/plasma.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "DESKTOP_LITE", + "requires_input_texture": false, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 0.5, + "samples": 3 + }, + { + "id": "medium", + "internal_scale": 0.75, + "samples": 5 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 8 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "scale", + "phase", + "seed" + ] + }, + "parameters": [ + { + "id": "color_a", + "label": "Color A", + "type": "color", + "default": [ + 1.0, + 0.1, + 0.2, + 1.0 + ], + "dmx_slots": [ + 1 + ] + }, + { + "id": "color_b", + "label": "Color B", + "type": "color", + "default": [ + 0.1, + 0.3, + 1.0, + 1.0 + ], + "dmx_slots": [ + 2 + ] + }, + { + "id": "scale", + "label": "Scale", + "type": "float", + "minimum": 0.5, + "maximum": 8.0, + "default": 2.0, + "dmx_slots": [ + 3 + ], + "curve": "quadratic" + }, + { + "id": "stretch_x", + "label": "Stretch X", + "type": "float", + "minimum": 0.1, + "maximum": 4.0, + "default": 1.0, + "dmx_slots": [ + 4 + ], + "curve": "linear" + }, + { + "id": "stretch_y", + "label": "Stretch Y", + "type": "float", + "minimum": 0.1, + "maximum": 4.0, + "default": 1.0, + "dmx_slots": [ + 5 + ], + "curve": "linear" + }, + { + "id": "distance", + "label": "Distance", + "type": "float", + "minimum": 0.0, + "maximum": 4.0, + "default": 1.0, + "dmx_slots": [ + 6 + ], + "curve": "linear" + }, + { + "id": "phase", + "label": "Phase", + "type": "float", + "minimum": 0.0, + "maximum": 6.2831853, + "default": 0.0, + "dmx_slots": [ + 7 + ], + "curve": "linear" + }, + { + "id": "speed", + "label": "Speed", + "type": "float", + "minimum": 0.0, + "maximum": 4.0, + "default": 1.0, + "dmx_slots": [ + 8 + ], + "curve": "linear" + }, + { + "id": "seed", + "label": "Seed", + "type": "float", + "minimum": 0.0, + "maximum": 100.0, + "default": 1.0, + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/generators/com.hms.generator.plasma/shaders/d3d11/plasma.hlsl b/plugins/builtin/generators/com.hms.generator.plasma/shaders/d3d11/plasma.hlsl new file mode 100644 index 0000000..e32c14e --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.plasma/shaders/d3d11/plasma.hlsl @@ -0,0 +1,66 @@ +// Plasma Generator +// HMS MediaEngine – Generator (kein Eingangstextur-Bezug, cbuffer identisch §14.4) +// D3D11-Pixelshader (PS_5_0). Semantik identisch zu GL/GLES (§12.6, §15.1). + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float4 param_color_a; + float4 param_color_b; + float param_scale; + float param_stretch_x; + float param_stretch_y; + float param_distance; + float param_phase; + float param_speed; + float param_seed; + float u_quality_samples; // AQ-Variante (Backend-Bindung) + float _pad0; // 16-Byte-Alignment + float _pad1; // 16-Byte-Alignment +}; + +float hash21(float2 p) +{ + p = frac(p * float2(123.34, 456.21)); + p += dot(p, p + 45.32); + return frac(p.x * p.y); +} + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float2 p = (uv - 0.5) * param_scale; + p.x *= param_stretch_x; + p.y *= param_stretch_y; + float t = u_time_seconds * param_speed + param_phase; + float terms = clamp(u_quality_samples, 1.0, 8.0); + float v = 0.0; + [loop] + for (int i = 0; i < 8; i++) + { + if (float(i) >= terms) { break; } + float fi = float(i + 1); + v += sin(p.x * fi + t * fi * 0.5 + param_seed) + * sin(p.y * fi * 1.3 - t * fi * 0.3 + param_seed * 2.0); + v += sin(length(p) * fi * 0.7 + t * 0.8 + param_seed); + } + v /= terms; + v = v * 0.5 + 0.5; + float d = length(p) * param_distance; + v += 0.5 + 0.5 * sin(d * 3.0 - t * 1.5 + param_seed); + v = saturate(v); + float3 col = lerp(param_color_a.rgb, param_color_b.rgb, v); + return float4(col * u_layer_opacity, u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.plasma/shaders/gl/plasma.frag b/plugins/builtin/generators/com.hms.generator.plasma/shaders/gl/plasma.frag new file mode 100644 index 0000000..8f061ba --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.plasma/shaders/gl/plasma.frag @@ -0,0 +1,60 @@ +// Plasma Generator +// HMS MediaEngine – Generator (GLSL, Linux x64). Semantik identisch zu HLSL/GLES (§12.6, §15.1). +#version 330 core + +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float4 param_color_a; +uniform float4 param_color_b; +uniform float param_scale; +uniform float param_stretch_x; +uniform float param_stretch_y; +uniform float param_distance; +uniform float param_phase; +uniform float param_speed; +uniform float param_seed; +uniform float u_quality_samples; + +in vec2 v_uv; +out vec4 fragColor; + +float hash21(vec2 p) +{ + p = fract(p * vec2(123.34, 456.21)); + p += dot(p, p + 45.32); + return fract(p.x * p.y); +} + +void main() +{ + vec2 p = (v_uv - 0.5) * param_scale; + p.x *= param_stretch_x; + p.y *= param_stretch_y; + float t = u_time_seconds * param_speed + param_phase; + float terms = clamp(u_quality_samples, 1.0, 8.0); + float v = 0.0; + for (int i = 0; i < 8; i++) + { + if (float(i) >= terms) { break; } + float fi = float(i + 1); + v += sin(p.x * fi + t * fi * 0.5 + param_seed) + * sin(p.y * fi * 1.3 - t * fi * 0.3 + param_seed * 2.0); + v += sin(length(p) * fi * 0.7 + t * 0.8 + param_seed); + } + v /= terms; + v = v * 0.5 + 0.5; + float d = length(p) * param_distance; + v += 0.5 + 0.5 * sin(d * 3.0 - t * 1.5 + param_seed); + v = clamp(v, 0.0, 1.0); + vec3 col = mix(param_color_a.rgb, param_color_b.rgb, v); + fragColor = vec4(col * u_layer_opacity, u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.plasma/shaders/gles/plasma.frag b/plugins/builtin/generators/com.hms.generator.plasma/shaders/gles/plasma.frag new file mode 100644 index 0000000..c9f6f19 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.plasma/shaders/gles/plasma.frag @@ -0,0 +1,60 @@ +// Plasma Generator +// HMS MediaEngine – Generator (GLES, Raspberry Pi). ES 2.0: texture2D, gl_FragColor, Schleifen mit konstanter Höchstgrenze. +#version 100 +precision mediump float; + +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float4 param_color_a; +uniform float4 param_color_b; +uniform float param_scale; +uniform float param_stretch_x; +uniform float param_stretch_y; +uniform float param_distance; +uniform float param_phase; +uniform float param_speed; +uniform float param_seed; +uniform float u_quality_samples; + +varying vec2 v_uv; + +float hash21(vec2 p) +{ + p = fract(p * vec2(123.34, 456.21)); + p += dot(p, p + 45.32); + return fract(p.x * p.y); +} + +void main() +{ + vec2 p = (v_uv - 0.5) * param_scale; + p.x *= param_stretch_x; + p.y *= param_stretch_y; + float t = u_time_seconds * param_speed + param_phase; + float terms = clamp(u_quality_samples, 1.0, 8.0); + float v = 0.0; + for (int i = 0; i < 8; i++) + { + if (float(i) >= terms) { break; } + float fi = float(i + 1); + v += sin(p.x * fi + t * fi * 0.5 + param_seed) + * sin(p.y * fi * 1.3 - t * fi * 0.3 + param_seed * 2.0); + v += sin(length(p) * fi * 0.7 + t * 0.8 + param_seed); + } + v /= terms; + v = v * 0.5 + 0.5; + float d = length(p) * param_distance; + v += 0.5 + 0.5 * sin(d * 3.0 - t * 1.5 + param_seed); + v = clamp(v, 0.0, 1.0); + vec3 col = mix(param_color_a.rgb, param_color_b.rgb, v); + gl_FragColor = vec4(col * u_layer_opacity, u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.shapes/plugin.json b/plugins/builtin/generators/com.hms.generator.shapes/plugin.json new file mode 100644 index 0000000..25aa1ed --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.shapes/plugin.json @@ -0,0 +1,188 @@ +{ + "schema_version": 1, + "id": "com.hms.generator.shapes", + "name": "Shapes", + "version": "1.0.0", + "api_version": 1, + "kind": "generator", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl_singlepass", + "passes": [ + { + "pixel_shader": "shaders/d3d11/shapes.hlsl" + } + ] + }, + "gl": { + "type": "glsl_singlepass", + "passes": [ + { + "fragment": "shaders/gl/shapes.frag" + } + ] + }, + "gles": { + "type": "glsl_es_singlepass", + "passes": [ + { + "fragment": "shaders/gles/shapes.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "DESKTOP_LITE", + "requires_input_texture": false, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 0.5, + "samples": 8 + }, + { + "id": "medium", + "internal_scale": 0.75, + "samples": 24 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 64 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "count", + "seed" + ] + }, + "parameters": [ + { + "id": "form", + "label": "Form", + "type": "enum", + "values": [ + "circle", + "rect", + "triangle", + "line", + "polygon" + ], + "default": "circle", + "dmx_slots": [ + 1 + ] + }, + { + "id": "count", + "label": "Count", + "type": "int", + "minimum": 1, + "maximum": 64, + "default": 24, + "dmx_slots": [ + 2 + ], + "curve": "linear" + }, + { + "id": "size", + "label": "Size", + "type": "float", + "minimum": 0.01, + "maximum": 0.5, + "default": 0.1, + "dmx_slots": [ + 3 + ], + "curve": "quadratic" + }, + { + "id": "rotation", + "label": "Rotation", + "type": "float", + "minimum": 0.0, + "maximum": 6.2831853, + "default": 0.0, + "dmx_slots": [ + 4 + ], + "curve": "linear" + }, + { + "id": "distribution", + "label": "Distribution", + "type": "enum", + "values": [ + "grid", + "ring", + "random" + ], + "default": "grid", + "dmx_slots": [ + 5 + ] + }, + { + "id": "fill_stroke", + "label": "Fill/Stroke", + "type": "enum", + "values": [ + "fill", + "stroke" + ], + "default": "fill", + "dmx_slots": [ + 6 + ] + }, + { + "id": "color_a", + "label": "Color A", + "type": "color", + "default": [ + 1.0, + 0.2, + 0.2, + 1.0 + ], + "dmx_slots": [ + 7 + ] + }, + { + "id": "color_b", + "label": "Color B", + "type": "color", + "default": [ + 0.2, + 0.2, + 1.0, + 1.0 + ], + "dmx_slots": [ + 8 + ] + }, + { + "id": "seed", + "label": "Seed", + "type": "float", + "minimum": 0.0, + "maximum": 100.0, + "default": 1.0, + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/generators/com.hms.generator.shapes/shaders/d3d11/shapes.hlsl b/plugins/builtin/generators/com.hms.generator.shapes/shaders/d3d11/shapes.hlsl new file mode 100644 index 0000000..d578d64 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.shapes/shaders/d3d11/shapes.hlsl @@ -0,0 +1,99 @@ +// Shapes Generator +// HMS MediaEngine – Generator (kein Eingangstextur-Bezug, cbuffer identisch §14.4) +// D3D11-Pixelshader (PS_5_0). Semantik identisch zu GL/GLES (§12.6, §15.1). + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float4 param_color_a; + float4 param_color_b; + float param_form; + float param_count; + float param_size; + float param_rotation; + float param_distribution; + float param_fill_stroke; + float param_seed; + float u_quality_samples; // AQ-Variante (Backend-Bindung) + float _pad0; // 16-Byte-Alignment + float _pad1; // 16-Byte-Alignment +}; + +float hash21(float2 p) +{ + p = frac(p * float2(123.34, 456.21)); + p += dot(p, p + 45.32); + return frac(p.x * p.y); +} + +float sdCircle(float2 p, float r) { return length(p) - r; } +float sdBox(float2 p, float2 b) { float2 d = abs(p) - b; return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0); } +float sdTri(float2 p, float s) { p = abs(p); return max(p.x * 0.8660254 + p.y * 0.5, -p.y) - s * 0.5; } +float sdLine(float2 p, float2 a, float2 b) { float2 pa = p - a; float2 ba = b - a; float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); return length(pa - ba * h); } +float sdPoly(float2 p, float r, int n) { float an = 6.2831853 / float(n); float a = atan2(p.x, p.y) + 0.5; float b = an * floor(a / an); float2 q = float2(cos(b), sin(b)) * dot(float2(cos(a), sin(a)), float2(cos(b), sin(b))); return length(p - q * r); } + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float2 p = uv - 0.5; + float t = u_time_seconds; + float maxN = clamp(u_quality_samples, 1.0, 64.0); + float form = floor(param_form + 0.5); + float distr = floor(param_distribution + 0.5); + float fill = floor(param_fill_stroke + 0.5); + float acc = 0.0; + float colAcc = 0.0; + [loop] + for (int i = 0; i < 64; i++) + { + if (float(i) >= maxN) { break; } + float2 pos; + if (distr < 0.5) + { + float g = ceil(sqrt(maxN)); + float gx = float(i % int(g)); + float gy = floor(float(i) / g); + pos = (float2(gx, gy) + 0.5) / g - 0.5; + } + else if (distr < 1.5) + { + float ang = 6.2831853 * float(i) / maxN; + pos = 0.35 * float2(cos(ang), sin(ang)); + } + else + { + pos = float2(hash21(float2(float(i) + param_seed, 1.0)), hash21(float2(float(i) + param_seed, 2.0))) - 0.5; + } + float2 lp = p - pos; + float cr = cos(param_rotation); + float sr = sin(param_rotation); + lp = float2(cr * lp.x - sr * lp.y, sr * lp.x + cr * lp.y); + float d; + if (form < 0.5) { d = sdCircle(lp, param_size); } + else if (form < 1.5) { d = sdBox(lp, float2(param_size, param_size)); } + else if (form < 2.5) { d = sdTri(lp, param_size); } + else if (form < 3.5) { d = sdLine(lp, float2(-param_size, 0.0), float2(param_size, 0.0)); } + else { d = sdPoly(lp, param_size, 6); } + float v; + if (fill < 0.5) { v = 1.0 - smoothstep(0.0, 0.01, d); } + else { v = 1.0 - smoothstep(0.0, 0.01, abs(d) - 0.01); } + acc += v; + colAcc += v * (float(i) / max(maxN - 1.0, 1.0)); + } + acc = saturate(acc); + float mixT = maxN > 1.0 ? colAcc / max(acc, 1e-5) : 0.0; + float3 col = lerp(param_color_a.rgb, param_color_b.rgb, mixT); + return float4(col * acc * u_layer_opacity, acc * u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.shapes/shaders/gl/shapes.frag b/plugins/builtin/generators/com.hms.generator.shapes/shaders/gl/shapes.frag new file mode 100644 index 0000000..eb6795d --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.shapes/shaders/gl/shapes.frag @@ -0,0 +1,93 @@ +// Shapes Generator +// HMS MediaEngine – Generator (GLSL, Linux x64). Semantik identisch zu HLSL/GLES (§12.6, §15.1). +#version 330 core + +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float4 param_color_a; +uniform float4 param_color_b; +uniform float param_form; +uniform float param_count; +uniform float param_size; +uniform float param_rotation; +uniform float param_distribution; +uniform float param_fill_stroke; +uniform float param_seed; +uniform float u_quality_samples; + +in vec2 v_uv; +out vec4 fragColor; + +float hash21(vec2 p) +{ + p = fract(p * vec2(123.34, 456.21)); + p += dot(p, p + 45.32); + return fract(p.x * p.y); +} + +float sdCircle(vec2 p, float r) { return length(p) - r; } +float sdBox(vec2 p, vec2 b) { vec2 d = abs(p) - b; return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0); } +float sdTri(vec2 p, float s) { p = abs(p); return max(p.x * 0.8660254 + p.y * 0.5, -p.y) - s * 0.5; } +float sdLine(vec2 p, vec2 a, vec2 b) { vec2 pa = p - a; vec2 ba = b - a; float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); return length(pa - ba * h); } +float sdPoly(vec2 p, float r, int n) { float an = 6.2831853 / float(n); float a = atan(p.x, p.y) + 0.5; float b = an * floor(a / an); vec2 q = vec2(cos(b), sin(b)) * dot(vec2(cos(a), sin(a)), vec2(cos(b), sin(b))); return length(p - q * r); } + +void main() +{ + vec2 p = v_uv - 0.5; + float t = u_time_seconds; + float maxN = clamp(u_quality_samples, 1.0, 64.0); + float form = floor(param_form + 0.5); + float distr = floor(param_distribution + 0.5); + float fill = floor(param_fill_stroke + 0.5); + float acc = 0.0; + float colAcc = 0.0; + for (int i = 0; i < 64; i++) + { + if (float(i) >= maxN) { break; } + vec2 pos; + if (distr < 0.5) + { + float g = ceil(sqrt(maxN)); + float gx = float(i % int(g)); + float gy = floor(float(i) / g); + pos = (vec2(gx, gy) + 0.5) / g - 0.5; + } + else if (distr < 1.5) + { + float ang = 6.2831853 * float(i) / maxN; + pos = 0.35 * vec2(cos(ang), sin(ang)); + } + else + { + pos = vec2(hash21(vec2(float(i) + param_seed, 1.0)), hash21(vec2(float(i) + param_seed, 2.0))) - 0.5; + } + vec2 lp = p - pos; + float cr = cos(param_rotation); + float sr = sin(param_rotation); + lp = vec2(cr * lp.x - sr * lp.y, sr * lp.x + cr * lp.y); + float d; + if (form < 0.5) { d = sdCircle(lp, param_size); } + else if (form < 1.5) { d = sdBox(lp, vec2(param_size, param_size)); } + else if (form < 2.5) { d = sdTri(lp, param_size); } + else if (form < 3.5) { d = sdLine(lp, vec2(-param_size, 0.0), vec2(param_size, 0.0)); } + else { d = sdPoly(lp, param_size, 6); } + float v; + if (fill < 0.5) { v = 1.0 - smoothstep(0.0, 0.01, d); } + else { v = 1.0 - smoothstep(0.0, 0.01, abs(d) - 0.01); } + acc += v; + colAcc += v * (float(i) / max(maxN - 1.0, 1.0)); + } + acc = clamp(acc, 0.0, 1.0); + float mixT = maxN > 1.0 ? colAcc / max(acc, 1e-5) : 0.0; + vec3 col = mix(param_color_a.rgb, param_color_b.rgb, mixT); + fragColor = vec4(col * acc * u_layer_opacity, acc * u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.shapes/shaders/gles/shapes.frag b/plugins/builtin/generators/com.hms.generator.shapes/shaders/gles/shapes.frag new file mode 100644 index 0000000..1f34f2b --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.shapes/shaders/gles/shapes.frag @@ -0,0 +1,93 @@ +// Shapes Generator +// HMS MediaEngine – Generator (GLES, Raspberry Pi). ES 2.0: texture2D, gl_FragColor, Schleifen mit konstanter Höchstgrenze. +#version 100 +precision mediump float; + +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float4 param_color_a; +uniform float4 param_color_b; +uniform float param_form; +uniform float param_count; +uniform float param_size; +uniform float param_rotation; +uniform float param_distribution; +uniform float param_fill_stroke; +uniform float param_seed; +uniform float u_quality_samples; + +varying vec2 v_uv; + +float hash21(vec2 p) +{ + p = fract(p * vec2(123.34, 456.21)); + p += dot(p, p + 45.32); + return fract(p.x * p.y); +} + +float sdCircle(vec2 p, float r) { return length(p) - r; } +float sdBox(vec2 p, vec2 b) { vec2 d = abs(p) - b; return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0); } +float sdTri(vec2 p, float s) { p = abs(p); return max(p.x * 0.8660254 + p.y * 0.5, -p.y) - s * 0.5; } +float sdLine(vec2 p, vec2 a, vec2 b) { vec2 pa = p - a; vec2 ba = b - a; float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); return length(pa - ba * h); } +float sdPoly(vec2 p, float r, int n) { float an = 6.2831853 / float(n); float a = atan(p.x, p.y) + 0.5; float b = an * floor(a / an); vec2 q = vec2(cos(b), sin(b)) * dot(vec2(cos(a), sin(a)), vec2(cos(b), sin(b))); return length(p - q * r); } + +void main() +{ + vec2 p = v_uv - 0.5; + float t = u_time_seconds; + float maxN = clamp(u_quality_samples, 1.0, 64.0); + float form = floor(param_form + 0.5); + float distr = floor(param_distribution + 0.5); + float fill = floor(param_fill_stroke + 0.5); + float acc = 0.0; + float colAcc = 0.0; + for (int i = 0; i < 64; i++) + { + if (float(i) >= maxN) { break; } + vec2 pos; + if (distr < 0.5) + { + float g = ceil(sqrt(maxN)); + float gx = float(i % int(g)); + float gy = floor(float(i) / g); + pos = (vec2(gx, gy) + 0.5) / g - 0.5; + } + else if (distr < 1.5) + { + float ang = 6.2831853 * float(i) / maxN; + pos = 0.35 * vec2(cos(ang), sin(ang)); + } + else + { + pos = vec2(hash21(vec2(float(i) + param_seed, 1.0)), hash21(vec2(float(i) + param_seed, 2.0))) - 0.5; + } + vec2 lp = p - pos; + float cr = cos(param_rotation); + float sr = sin(param_rotation); + lp = vec2(cr * lp.x - sr * lp.y, sr * lp.x + cr * lp.y); + float d; + if (form < 0.5) { d = sdCircle(lp, param_size); } + else if (form < 1.5) { d = sdBox(lp, vec2(param_size, param_size)); } + else if (form < 2.5) { d = sdTri(lp, param_size); } + else if (form < 3.5) { d = sdLine(lp, vec2(-param_size, 0.0), vec2(param_size, 0.0)); } + else { d = sdPoly(lp, param_size, 6); } + float v; + if (fill < 0.5) { v = 1.0 - smoothstep(0.0, 0.01, d); } + else { v = 1.0 - smoothstep(0.0, 0.01, abs(d) - 0.01); } + acc += v; + colAcc += v * (float(i) / max(maxN - 1.0, 1.0)); + } + acc = clamp(acc, 0.0, 1.0); + float mixT = maxN > 1.0 ? colAcc / max(acc, 1e-5) : 0.0; + vec3 col = mix(param_color_a.rgb, param_color_b.rgb, mixT); + gl_FragColor = vec4(col * acc * u_layer_opacity, acc * u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.solid/plugin.json b/plugins/builtin/generators/com.hms.generator.solid/plugin.json new file mode 100644 index 0000000..3aeab46 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.solid/plugin.json @@ -0,0 +1,96 @@ +{ + "schema_version": 1, + "id": "com.hms.generator.solid", + "name": "Solid", + "version": "1.0.0", + "api_version": 1, + "kind": "generator", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl_singlepass", + "passes": [ + { + "pixel_shader": "shaders/d3d11/solid.hlsl" + } + ] + }, + "gl": { + "type": "glsl_singlepass", + "passes": [ + { + "fragment": "shaders/gl/solid.frag" + } + ] + }, + "gles": { + "type": "glsl_es_singlepass", + "passes": [ + { + "fragment": "shaders/gles/solid.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "DESKTOP_LITE", + "requires_input_texture": false, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 0.5, + "samples": 1 + }, + { + "id": "medium", + "internal_scale": 1.0, + "samples": 1 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 1 + } + ] + }, + "parameters": [ + { + "id": "color_a", + "label": "Color A", + "type": "color", + "default": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "dmx_slots": [ + 1, + 2, + 3, + 4 + ] + }, + { + "id": "alpha", + "label": "Alpha", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 1.0, + "dmx_slots": [ + 5 + ], + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/generators/com.hms.generator.solid/shaders/d3d11/solid.hlsl b/plugins/builtin/generators/com.hms.generator.solid/shaders/d3d11/solid.hlsl new file mode 100644 index 0000000..34bb331 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.solid/shaders/d3d11/solid.hlsl @@ -0,0 +1,33 @@ +// HMS MediaEngine - Generator (D3D11) +// Erzeugt ohne Eingangstextur; cbuffer-Satz identisch zu Filtern (§12.6). + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float4 param_color_a; + float param_alpha; + float _pad0; + float _pad1; + float _pad2; +}; + + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float4 col = param_color_a; + col.a *= saturate(param_alpha) * u_layer_opacity; + return col; +} diff --git a/plugins/builtin/generators/com.hms.generator.solid/shaders/gl/solid.frag b/plugins/builtin/generators/com.hms.generator.solid/shaders/gl/solid.frag new file mode 100644 index 0000000..ac02658 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.solid/shaders/gl/solid.frag @@ -0,0 +1,29 @@ +#version 330 core +// HMS MediaEngine - Generator (GLSL, Linux x64) +// Semantik identisch zur HLSL-Variante (§12.6, §15.4). + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform vec4 param_color_a; +uniform float param_alpha; + + +in vec2 v_uv; +out vec4 fragColor; + +void main() +{ + vec4 col = param_color_a; + col.a *= clamp(param_alpha, 0.0, 1.0) * u_layer_opacity; + fragColor = col; +} diff --git a/plugins/builtin/generators/com.hms.generator.solid/shaders/gles/solid.frag b/plugins/builtin/generators/com.hms.generator.solid/shaders/gles/solid.frag new file mode 100644 index 0000000..48ec7ae --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.solid/shaders/gles/solid.frag @@ -0,0 +1,29 @@ +#version 100 +// HMS MediaEngine - Generator (GLES, Raspberry Pi) +// ES 2.0: texture2D, varying, gl_FragColor, Schleifen mit konstanter Höchstgrenze. +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform vec4 param_color_a; +uniform float param_alpha; + + +varying vec2 v_uv; + +void main() +{ + vec4 col = param_color_a; + col.a *= clamp(param_alpha, 0.0, 1.0) * u_layer_opacity; + gl_FragColor = col; +} diff --git a/plugins/builtin/generators/com.hms.generator.starfield/plugin.json b/plugins/builtin/generators/com.hms.generator.starfield/plugin.json new file mode 100644 index 0000000..f0164ea --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.starfield/plugin.json @@ -0,0 +1,190 @@ +{ + "schema_version": 1, + "id": "com.hms.generator.starfield", + "name": "Starfield", + "version": "1.0.0", + "api_version": 1, + "kind": "generator", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl_singlepass", + "passes": [ + { + "pixel_shader": "shaders/d3d11/starfield.hlsl" + } + ] + }, + "gl": { + "type": "glsl_singlepass", + "passes": [ + { + "fragment": "shaders/gl/starfield.frag" + } + ] + }, + "gles": { + "type": "glsl_es_singlepass", + "passes": [ + { + "fragment": "shaders/gles/starfield.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "DESKTOP_LITE", + "requires_input_texture": false, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 0.5, + "samples": 32 + }, + { + "id": "medium", + "internal_scale": 0.75, + "samples": 128 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 512 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "count", + "seed" + ] + }, + "parameters": [ + { + "id": "color_a", + "label": "Color A", + "type": "color", + "default": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "dmx_slots": [ + 1 + ] + }, + { + "id": "color_b", + "label": "Color B", + "type": "color", + "default": [ + 0.4, + 0.6, + 1.0, + 1.0 + ], + "dmx_slots": [ + 2 + ] + }, + { + "id": "count", + "label": "Count", + "type": "int", + "minimum": 1, + "maximum": 512, + "default": 128, + "dmx_slots": [ + 3 + ], + "curve": "linear" + }, + { + "id": "size", + "label": "Size", + "type": "float", + "minimum": 0.001, + "maximum": 0.05, + "default": 0.01, + "dmx_slots": [ + 4 + ], + "curve": "quadratic" + }, + { + "id": "speed", + "label": "Speed", + "type": "float", + "minimum": 0.0, + "maximum": 4.0, + "default": 1.0, + "dmx_slots": [ + 5 + ], + "curve": "linear" + }, + { + "id": "direction_x", + "label": "Direction X", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 0.0, + "dmx_slots": [ + 6 + ], + "curve": "linear" + }, + { + "id": "direction_y", + "label": "Direction Y", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": -1.0, + "dmx_slots": [ + 7 + ], + "curve": "linear" + }, + { + "id": "depth", + "label": "Depth", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.5, + "dmx_slots": [ + 8 + ], + "curve": "linear" + }, + { + "id": "twinkle", + "label": "Twinkle", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.5, + "curve": "linear" + }, + { + "id": "seed", + "label": "Seed", + "type": "float", + "minimum": 0.0, + "maximum": 100.0, + "default": 1.0, + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/generators/com.hms.generator.starfield/shaders/d3d11/starfield.hlsl b/plugins/builtin/generators/com.hms.generator.starfield/shaders/d3d11/starfield.hlsl new file mode 100644 index 0000000..ff3e3b6 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.starfield/shaders/d3d11/starfield.hlsl @@ -0,0 +1,74 @@ +// Starfield Generator +// HMS MediaEngine – Generator (kein Eingangstextur-Bezug, cbuffer identisch §14.4) +// D3D11-Pixelshader (PS_5_0). Semantik identisch zu GL/GLES (§12.6, §15.1). + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float4 param_color_a; + float4 param_color_b; + float param_count; + float param_size; + float param_speed; + float param_direction_x; + float param_direction_y; + float param_depth; + float param_twinkle; + float param_seed; + float u_quality_samples; // AQ-Variante (Backend-Bindung) + float _pad0; // 16-Byte-Alignment +}; + +float hash21(float2 p) +{ + p = frac(p * float2(123.34, 456.21)); + p += dot(p, p + 45.32); + return frac(p.x * p.y); +} + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float2 p = uv; + float t = u_time_seconds * param_speed; + float maxN = clamp(u_quality_samples, 1.0, 512.0); + float dl = length(float2(param_direction_x, param_direction_y)); + float2 dir = dl > 1e-4 ? float2(param_direction_x, param_direction_y) / dl : float2(0.0, -1.0); + float acc = 0.0; + float colAcc = 0.0; + [loop] + for (int i = 0; i < 512; i++) + { + if (float(i) >= maxN) { break; } + float2 base = float2(hash21(float2(float(i) + param_seed, 1.0)), hash21(float2(float(i) + param_seed, 2.0))); + float depth = hash21(float2(float(i) + param_seed, 3.0)) * param_depth; + float2 pos = base + dir * t * (0.1 + depth * 0.9); + pos = frac(pos); + float2 d = pos - p; + d = abs(d); + d = min(d, 1.0 - d); + float dist = length(d); + float s = param_size * (0.3 + depth); + float star = exp(-dist * dist / max(s * s, 1e-5)); + float tw = 0.5 + 0.5 * sin(t * 3.0 + param_seed * 10.0 + float(i) * 1.7); + star *= lerp(1.0, tw, param_twinkle); + acc += star; + colAcc += star * depth; + } + acc = saturate(acc); + float mixT = maxN > 1.0 ? colAcc / max(acc, 1e-5) : 0.0; + float3 col = lerp(param_color_a.rgb, param_color_b.rgb, mixT); + return float4(col * acc * u_layer_opacity, acc * u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.starfield/shaders/gl/starfield.frag b/plugins/builtin/generators/com.hms.generator.starfield/shaders/gl/starfield.frag new file mode 100644 index 0000000..6835427 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.starfield/shaders/gl/starfield.frag @@ -0,0 +1,69 @@ +// Starfield Generator +// HMS MediaEngine – Generator (GLSL, Linux x64). Semantik identisch zu HLSL/GLES (§12.6, §15.1). +#version 330 core + +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float4 param_color_a; +uniform float4 param_color_b; +uniform float param_count; +uniform float param_size; +uniform float param_speed; +uniform float param_direction_x; +uniform float param_direction_y; +uniform float param_depth; +uniform float param_twinkle; +uniform float param_seed; +uniform float u_quality_samples; + +in vec2 v_uv; +out vec4 fragColor; + +float hash21(vec2 p) +{ + p = fract(p * vec2(123.34, 456.21)); + p += dot(p, p + 45.32); + return fract(p.x * p.y); +} + +void main() +{ + vec2 p = v_uv; + float t = u_time_seconds * param_speed; + float maxN = clamp(u_quality_samples, 1.0, 512.0); + float dl = length(vec2(param_direction_x, param_direction_y)); + vec2 dir = dl > 1e-4 ? vec2(param_direction_x, param_direction_y) / dl : vec2(0.0, -1.0); + float acc = 0.0; + float colAcc = 0.0; + for (int i = 0; i < 512; i++) + { + if (float(i) >= maxN) { break; } + vec2 base = vec2(hash21(vec2(float(i) + param_seed, 1.0)), hash21(vec2(float(i) + param_seed, 2.0))); + float depth = hash21(vec2(float(i) + param_seed, 3.0)) * param_depth; + vec2 pos = base + dir * t * (0.1 + depth * 0.9); + pos = fract(pos); + vec2 d = pos - p; + d = abs(d); + d = min(d, 1.0 - d); + float dist = length(d); + float s = param_size * (0.3 + depth); + float star = exp(-dist * dist / max(s * s, 1e-5)); + float tw = 0.5 + 0.5 * sin(t * 3.0 + param_seed * 10.0 + float(i) * 1.7); + star *= mix(1.0, tw, param_twinkle); + acc += star; + colAcc += star * depth; + } + acc = clamp(acc, 0.0, 1.0); + float mixT = maxN > 1.0 ? colAcc / max(acc, 1e-5) : 0.0; + vec3 col = mix(param_color_a.rgb, param_color_b.rgb, mixT); + fragColor = vec4(col * acc * u_layer_opacity, acc * u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.starfield/shaders/gles/starfield.frag b/plugins/builtin/generators/com.hms.generator.starfield/shaders/gles/starfield.frag new file mode 100644 index 0000000..2c08a6c --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.starfield/shaders/gles/starfield.frag @@ -0,0 +1,69 @@ +// Starfield Generator +// HMS MediaEngine – Generator (GLES, Raspberry Pi). ES 2.0: texture2D, gl_FragColor, Schleifen mit konstanter Höchstgrenze. +#version 100 +precision mediump float; + +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float4 param_color_a; +uniform float4 param_color_b; +uniform float param_count; +uniform float param_size; +uniform float param_speed; +uniform float param_direction_x; +uniform float param_direction_y; +uniform float param_depth; +uniform float param_twinkle; +uniform float param_seed; +uniform float u_quality_samples; + +varying vec2 v_uv; + +float hash21(vec2 p) +{ + p = fract(p * vec2(123.34, 456.21)); + p += dot(p, p + 45.32); + return fract(p.x * p.y); +} + +void main() +{ + vec2 p = v_uv; + float t = u_time_seconds * param_speed; + float maxN = clamp(u_quality_samples, 1.0, 512.0); + float dl = length(vec2(param_direction_x, param_direction_y)); + vec2 dir = dl > 1e-4 ? vec2(param_direction_x, param_direction_y) / dl : vec2(0.0, -1.0); + float acc = 0.0; + float colAcc = 0.0; + for (int i = 0; i < 512; i++) + { + if (float(i) >= maxN) { break; } + vec2 base = vec2(hash21(vec2(float(i) + param_seed, 1.0)), hash21(vec2(float(i) + param_seed, 2.0))); + float depth = hash21(vec2(float(i) + param_seed, 3.0)) * param_depth; + vec2 pos = base + dir * t * (0.1 + depth * 0.9); + pos = fract(pos); + vec2 d = pos - p; + d = abs(d); + d = min(d, 1.0 - d); + float dist = length(d); + float s = param_size * (0.3 + depth); + float star = exp(-dist * dist / max(s * s, 1e-5)); + float tw = 0.5 + 0.5 * sin(t * 3.0 + param_seed * 10.0 + float(i) * 1.7); + star *= mix(1.0, tw, param_twinkle); + acc += star; + colAcc += star * depth; + } + acc = clamp(acc, 0.0, 1.0); + float mixT = maxN > 1.0 ? colAcc / max(acc, 1e-5) : 0.0; + vec3 col = mix(param_color_a.rgb, param_color_b.rgb, mixT); + gl_FragColor = vec4(col * acc * u_layer_opacity, acc * u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.stripes_chaser/plugin.json b/plugins/builtin/generators/com.hms.generator.stripes_chaser/plugin.json new file mode 100644 index 0000000..441dcdc --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.stripes_chaser/plugin.json @@ -0,0 +1,164 @@ +{ + "schema_version": 1, + "id": "com.hms.generator.stripes_chaser", + "name": "Stripes Chaser", + "version": "1.0.0", + "api_version": 1, + "kind": "generator", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl_singlepass", + "passes": [ + { + "pixel_shader": "shaders/d3d11/stripes_chaser.hlsl" + } + ] + }, + "gl": { + "type": "glsl_singlepass", + "passes": [ + { + "fragment": "shaders/gl/stripes_chaser.frag" + } + ] + }, + "gles": { + "type": "glsl_es_singlepass", + "passes": [ + { + "fragment": "shaders/gles/stripes_chaser.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "DESKTOP_LITE", + "requires_input_texture": false, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 0.5, + "samples": 1 + }, + { + "id": "medium", + "internal_scale": 1.0, + "samples": 1 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 1 + } + ] + }, + "parameters": [ + { + "id": "color_a", + "label": "Color A", + "type": "color", + "default": [ + 1.0, + 1.0, + 0.0, + 1.0 + ], + "dmx_slots": [ + 1, + 2, + 3, + 4 + ] + }, + { + "id": "color_b", + "label": "Color B", + "type": "color", + "default": [ + 0.0, + 0.0, + 1.0, + 1.0 + ], + "dmx_slots": [ + 5, + 6, + 7, + 8 + ] + }, + { + "id": "width", + "label": "Width", + "type": "float", + "minimum": 0.01, + "maximum": 1.0, + "default": 0.2, + "curve": "linear" + }, + { + "id": "spacing", + "label": "Spacing", + "type": "float", + "minimum": 0.05, + "maximum": 2.0, + "default": 0.5, + "curve": "linear" + }, + { + "id": "angle", + "label": "Angle", + "type": "float", + "minimum": 0.0, + "maximum": 360.0, + "default": 0.0, + "curve": "linear" + }, + { + "id": "direction", + "label": "Direction", + "type": "float", + "minimum": -1.0, + "maximum": 1.0, + "default": 1.0, + "curve": "linear" + }, + { + "id": "phase", + "label": "Phase", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.0, + "curve": "linear" + }, + { + "id": "speed", + "label": "Speed", + "type": "float", + "minimum": -2.0, + "maximum": 2.0, + "default": 0.5, + "curve": "linear" + }, + { + "id": "softness", + "label": "Softness", + "type": "float", + "minimum": 0.0, + "maximum": 0.5, + "default": 0.05, + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/generators/com.hms.generator.stripes_chaser/shaders/d3d11/stripes_chaser.hlsl b/plugins/builtin/generators/com.hms.generator.stripes_chaser/shaders/d3d11/stripes_chaser.hlsl new file mode 100644 index 0000000..297134f --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.stripes_chaser/shaders/d3d11/stripes_chaser.hlsl @@ -0,0 +1,48 @@ +// HMS MediaEngine - Generator (D3D11) +// Erzeugt ohne Eingangstextur; cbuffer-Satz identisch zu Filtern (§12.6). + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float4 param_color_a; + float4 param_color_b; + float param_width; + float param_spacing; + float param_angle; + float param_direction; + float param_phase; + float param_speed; + float param_softness; + float _pad0; + float _pad1; + float _pad2; +}; + + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float ang = radians(param_angle); + float2 dir = float2(cos(ang), sin(ang)); + float2 p = uv + dir * (u_time_seconds * param_speed * param_direction); + float t = dot(p, dir) / max(param_spacing, 0.0001) + param_phase; + float stripe = frac(t); + float w = max(param_width, 0.0001) / max(param_spacing, 0.0001); + float soft = max(param_softness, 0.0001); + float m = smoothstep(w, w + soft, stripe); + float4 col = lerp(param_color_a, param_color_b, m); + col.a *= u_layer_opacity; + return col; +} diff --git a/plugins/builtin/generators/com.hms.generator.stripes_chaser/shaders/gl/stripes_chaser.frag b/plugins/builtin/generators/com.hms.generator.stripes_chaser/shaders/gl/stripes_chaser.frag new file mode 100644 index 0000000..9168108 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.stripes_chaser/shaders/gl/stripes_chaser.frag @@ -0,0 +1,44 @@ +#version 330 core +// HMS MediaEngine - Generator (GLSL, Linux x64) +// Semantik identisch zur HLSL-Variante (§12.6, §15.4). + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform vec4 param_color_a; +uniform vec4 param_color_b; +uniform float param_width; +uniform float param_spacing; +uniform float param_angle; +uniform float param_direction; +uniform float param_phase; +uniform float param_speed; +uniform float param_softness; + + +in vec2 v_uv; +out vec4 fragColor; + +void main() +{ + float ang = radians(param_angle); + vec2 dir = vec2(cos(ang), sin(ang)); + vec2 p = v_uv + dir * (u_time_seconds * param_speed * param_direction); + float t = dot(p, dir) / max(param_spacing, 0.0001) + param_phase; + float stripe = fract(t); + float w = max(param_width, 0.0001) / max(param_spacing, 0.0001); + float soft = max(param_softness, 0.0001); + float m = smoothstep(w, w + soft, stripe); + vec4 col = mix(param_color_a, param_color_b, m); + col.a *= u_layer_opacity; + fragColor = col; +} diff --git a/plugins/builtin/generators/com.hms.generator.stripes_chaser/shaders/gles/stripes_chaser.frag b/plugins/builtin/generators/com.hms.generator.stripes_chaser/shaders/gles/stripes_chaser.frag new file mode 100644 index 0000000..be10857 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.stripes_chaser/shaders/gles/stripes_chaser.frag @@ -0,0 +1,44 @@ +#version 100 +// HMS MediaEngine - Generator (GLES, Raspberry Pi) +// ES 2.0: texture2D, varying, gl_FragColor, Schleifen mit konstanter Höchstgrenze. +precision mediump float; + +uniform sampler2D u_input_texture; +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform vec4 param_color_a; +uniform vec4 param_color_b; +uniform float param_width; +uniform float param_spacing; +uniform float param_angle; +uniform float param_direction; +uniform float param_phase; +uniform float param_speed; +uniform float param_softness; + + +varying vec2 v_uv; + +void main() +{ + float ang = radians(param_angle); + vec2 dir = vec2(cos(ang), sin(ang)); + vec2 p = v_uv + dir * (u_time_seconds * param_speed * param_direction); + float t = dot(p, dir) / max(param_spacing, 0.0001) + param_phase; + float stripe = fract(t); + float w = max(param_width, 0.0001) / max(param_spacing, 0.0001); + float soft = max(param_softness, 0.0001); + float m = smoothstep(w, w + soft, stripe); + vec4 col = mix(param_color_a, param_color_b, m); + col.a *= u_layer_opacity; + gl_FragColor = col; +} diff --git a/plugins/builtin/generators/com.hms.generator.wave_bars/plugin.json b/plugins/builtin/generators/com.hms.generator.wave_bars/plugin.json new file mode 100644 index 0000000..fb6ed32 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.wave_bars/plugin.json @@ -0,0 +1,184 @@ +{ + "schema_version": 1, + "id": "com.hms.generator.wave_bars", + "name": "Wave Bars", + "version": "1.0.0", + "api_version": 1, + "kind": "generator", + "vendor": "HMS", + "entrypoints": { + "d3d11": { + "type": "hlsl_singlepass", + "passes": [ + { + "pixel_shader": "shaders/d3d11/wave_bars.hlsl" + } + ] + }, + "gl": { + "type": "glsl_singlepass", + "passes": [ + { + "fragment": "shaders/gl/wave_bars.frag" + } + ] + }, + "gles": { + "type": "glsl_es_singlepass", + "passes": [ + { + "fragment": "shaders/gles/wave_bars.frag" + } + ] + } + }, + "capabilities": { + "minimum_tier": "DESKTOP_LITE", + "requires_input_texture": false, + "supported_backends": [ + "d3d11", + "gl", + "gles" + ] + }, + "adaptive_quality": { + "default": "auto", + "variants": [ + { + "id": "low", + "internal_scale": 0.5, + "samples": 8 + }, + { + "id": "medium", + "internal_scale": 0.75, + "samples": 16 + }, + { + "id": "high", + "internal_scale": 1.0, + "samples": 32 + } + ], + "transition_ms": 180, + "semantic_parameters_unchanged": [ + "count", + "phase" + ] + }, + "parameters": [ + { + "id": "color_a", + "label": "Color A", + "type": "color", + "default": [ + 1.0, + 0.6, + 0.1, + 1.0 + ], + "dmx_slots": [ + 1 + ] + }, + { + "id": "color_b", + "label": "Color B", + "type": "color", + "default": [ + 0.1, + 0.2, + 1.0, + 1.0 + ], + "dmx_slots": [ + 2 + ] + }, + { + "id": "waveform", + "label": "Waveform", + "type": "enum", + "values": [ + "sine", + "triangle", + "saw", + "square" + ], + "default": "sine", + "dmx_slots": [ + 3 + ] + }, + { + "id": "count", + "label": "Count", + "type": "int", + "minimum": 1, + "maximum": 64, + "default": 16, + "dmx_slots": [ + 4 + ], + "curve": "linear" + }, + { + "id": "amplitude", + "label": "Amplitude", + "type": "float", + "minimum": 0.0, + "maximum": 1.0, + "default": 0.5, + "dmx_slots": [ + 5 + ], + "curve": "linear" + }, + { + "id": "frequency", + "label": "Frequency", + "type": "float", + "minimum": 0.5, + "maximum": 8.0, + "default": 2.0, + "dmx_slots": [ + 6 + ], + "curve": "linear" + }, + { + "id": "angle", + "label": "Angle", + "type": "float", + "minimum": 0.0, + "maximum": 6.2831853, + "default": 0.0, + "dmx_slots": [ + 7 + ], + "curve": "linear" + }, + { + "id": "phase", + "label": "Phase", + "type": "float", + "minimum": 0.0, + "maximum": 6.2831853, + "default": 0.0, + "dmx_slots": [ + 8 + ], + "curve": "linear" + }, + { + "id": "speed", + "label": "Speed", + "type": "float", + "minimum": 0.0, + "maximum": 4.0, + "default": 1.0, + "curve": "linear" + } + ], + "failure_mode": "bypass" +} diff --git a/plugins/builtin/generators/com.hms.generator.wave_bars/shaders/d3d11/wave_bars.hlsl b/plugins/builtin/generators/com.hms.generator.wave_bars/shaders/d3d11/wave_bars.hlsl new file mode 100644 index 0000000..5274ecf --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.wave_bars/shaders/d3d11/wave_bars.hlsl @@ -0,0 +1,64 @@ +// Wave Bars Generator +// HMS MediaEngine – Generator (kein Eingangstextur-Bezug, cbuffer identisch §14.4) +// D3D11-Pixelshader (PS_5_0). Semantik identisch zu GL/GLES (§12.6, §15.1). + +Texture2D u_input_texture : register(t0); +SamplerState u_sampler : register(s0); + +cbuffer hms_params : register(b0) +{ + float4 u_resolution; // xy = Auflösung in Pixeln + float u_time_seconds; + float u_delta_seconds; + float u_frame_index; + float u_layer_opacity; + float u_audio_rms; + float u_audio_peak; + float u_audio_bass; + float u_audio_mid; + float u_audio_treble; + float u_audio_beat; + float4 param_color_a; + float4 param_color_b; + float param_waveform; + float param_count; + float param_amplitude; + float param_frequency; + float param_angle; + float param_phase; + float param_speed; + float u_quality_samples; // AQ-Variante (Backend-Bindung) + float _pad0; // 16-Byte-Alignment + float _pad1; // 16-Byte-Alignment +}; + +float hash21(float2 p) +{ + p = frac(p * float2(123.34, 456.21)); + p += dot(p, p + 45.32); + return frac(p.x * p.y); +} + +float4 mainPS(float4 pos : SV_POSITION, float2 uv : TEXCOORD0) : SV_Target +{ + float2 p = uv - 0.5; + float ca = cos(param_angle); + float sa = sin(param_angle); + p = float2(ca * p.x - sa * p.y, sa * p.x + ca * p.y); + float t = u_time_seconds * param_speed + param_phase; + float n = clamp(param_count, 1.0, 64.0); + float cell = 1.0 / n; + float fx = frac(p.x / cell); + float wave; + float wf = floor(param_waveform + 0.5); + if (wf < 0.5) { wave = sin(fx * 6.2831853 * param_frequency + t); } + else if (wf < 1.5) { wave = 4.0 * abs(fx - 0.5) - 1.0; } + else if (wf < 2.5) { wave = 2.0 * fx - 1.0; } + else { wave = (fx < 0.5) ? 1.0 : -1.0; } + float h = 0.5 + wave * param_amplitude * 0.5; + float aa = 0.02 / clamp(u_quality_samples, 1.0, 32.0); + float bar = 1.0 - smoothstep(h * 0.5 - aa, h * 0.5 + aa, abs(p.y)); + float colIdx = floor(p.x / cell) / max(n - 1.0, 1.0); + float3 col = lerp(param_color_a.rgb, param_color_b.rgb, colIdx); + return float4(col * bar * u_layer_opacity, bar * u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.wave_bars/shaders/gl/wave_bars.frag b/plugins/builtin/generators/com.hms.generator.wave_bars/shaders/gl/wave_bars.frag new file mode 100644 index 0000000..bdb9768 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.wave_bars/shaders/gl/wave_bars.frag @@ -0,0 +1,59 @@ +// Wave Bars Generator +// HMS MediaEngine – Generator (GLSL, Linux x64). Semantik identisch zu HLSL/GLES (§12.6, §15.1). +#version 330 core + +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float4 param_color_a; +uniform float4 param_color_b; +uniform float param_waveform; +uniform float param_count; +uniform float param_amplitude; +uniform float param_frequency; +uniform float param_angle; +uniform float param_phase; +uniform float param_speed; +uniform float u_quality_samples; + +in vec2 v_uv; +out vec4 fragColor; + +float hash21(vec2 p) +{ + p = fract(p * vec2(123.34, 456.21)); + p += dot(p, p + 45.32); + return fract(p.x * p.y); +} + +void main() +{ + vec2 p = v_uv - 0.5; + float ca = cos(param_angle); + float sa = sin(param_angle); + p = vec2(ca * p.x - sa * p.y, sa * p.x + ca * p.y); + float t = u_time_seconds * param_speed + param_phase; + float n = clamp(param_count, 1.0, 64.0); + float cell = 1.0 / n; + float fx = fract(p.x / cell); + float wave; + float wf = floor(param_waveform + 0.5); + if (wf < 0.5) { wave = sin(fx * 6.2831853 * param_frequency + t); } + else if (wf < 1.5) { wave = 4.0 * abs(fx - 0.5) - 1.0; } + else if (wf < 2.5) { wave = 2.0 * fx - 1.0; } + else { wave = (fx < 0.5) ? 1.0 : -1.0; } + float h = 0.5 + wave * param_amplitude * 0.5; + float aa = 0.02 / clamp(u_quality_samples, 1.0, 32.0); + float bar = 1.0 - smoothstep(h * 0.5 - aa, h * 0.5 + aa, abs(p.y)); + float colIdx = floor(p.x / cell) / max(n - 1.0, 1.0); + vec3 col = mix(param_color_a.rgb, param_color_b.rgb, colIdx); + fragColor = vec4(col * bar * u_layer_opacity, bar * u_layer_opacity); +} diff --git a/plugins/builtin/generators/com.hms.generator.wave_bars/shaders/gles/wave_bars.frag b/plugins/builtin/generators/com.hms.generator.wave_bars/shaders/gles/wave_bars.frag new file mode 100644 index 0000000..ea22df8 --- /dev/null +++ b/plugins/builtin/generators/com.hms.generator.wave_bars/shaders/gles/wave_bars.frag @@ -0,0 +1,59 @@ +// Wave Bars Generator +// HMS MediaEngine – Generator (GLES, Raspberry Pi). ES 2.0: texture2D, gl_FragColor, Schleifen mit konstanter Höchstgrenze. +#version 100 +precision mediump float; + +uniform vec2 u_resolution; +uniform float u_time_seconds; +uniform float u_delta_seconds; +uniform float u_frame_index; +uniform float u_layer_opacity; +uniform float u_audio_rms; +uniform float u_audio_peak; +uniform float u_audio_bass; +uniform float u_audio_mid; +uniform float u_audio_treble; +uniform float u_audio_beat; +uniform float4 param_color_a; +uniform float4 param_color_b; +uniform float param_waveform; +uniform float param_count; +uniform float param_amplitude; +uniform float param_frequency; +uniform float param_angle; +uniform float param_phase; +uniform float param_speed; +uniform float u_quality_samples; + +varying vec2 v_uv; + +float hash21(vec2 p) +{ + p = fract(p * vec2(123.34, 456.21)); + p += dot(p, p + 45.32); + return fract(p.x * p.y); +} + +void main() +{ + vec2 p = v_uv - 0.5; + float ca = cos(param_angle); + float sa = sin(param_angle); + p = vec2(ca * p.x - sa * p.y, sa * p.x + ca * p.y); + float t = u_time_seconds * param_speed + param_phase; + float n = clamp(param_count, 1.0, 64.0); + float cell = 1.0 / n; + float fx = fract(p.x / cell); + float wave; + float wf = floor(param_waveform + 0.5); + if (wf < 0.5) { wave = sin(fx * 6.2831853 * param_frequency + t); } + else if (wf < 1.5) { wave = 4.0 * abs(fx - 0.5) - 1.0; } + else if (wf < 2.5) { wave = 2.0 * fx - 1.0; } + else { wave = (fx < 0.5) ? 1.0 : -1.0; } + float h = 0.5 + wave * param_amplitude * 0.5; + float aa = 0.02 / clamp(u_quality_samples, 1.0, 32.0); + float bar = 1.0 - smoothstep(h * 0.5 - aa, h * 0.5 + aa, abs(p.y)); + float colIdx = floor(p.x / cell) / max(n - 1.0, 1.0); + vec3 col = mix(param_color_a.rgb, param_color_b.rgb, colIdx); + gl_FragColor = vec4(col * bar * u_layer_opacity, bar * u_layer_opacity); +} diff --git a/tests/unit/test_builtin_plugins.py b/tests/unit/test_builtin_plugins.py new file mode 100644 index 0000000..ea1eb74 --- /dev/null +++ b/tests/unit/test_builtin_plugins.py @@ -0,0 +1,172 @@ +"""Tests über ALLE eingebauten Pflicht-Plugins (PLAN.md §15, §29.1). + +Parametrisiert über jedes gefundene Builtin-Plugin: +- Manifest validiert (inkl. Shader-Existenz je Backend) +- kind stimmt zur Kategorie (generator/filter) +- alle drei Backends d3d11/gl/gles deklariert (§12.6 Backend-Vertrag) +- max. 8 DMX-Slots gesamt (§14.7) +- adaptive_quality mit mindestens 3 Varianten (§15.1/§15.2 AQ-Regeln) +- Standard-Uniform-Satz in jedem Shader deklariert (§14.4) + +Vollständigkeitsprüfung (§15.1/§15.2): exakt 10 Generatoren und +14 Filter mit den normativen Plugin-IDs sind vorhanden. +""" + +from __future__ import annotations + +from pathlib import Path + +import pytest +from hms_plugin_sdk import load_manifest + +REPO = Path(__file__).resolve().parents[2] +BUILTIN = REPO / "plugins" / "builtin" + +REQUIRED_GENERATORS = { + "com.hms.generator.solid", + "com.hms.generator.gradient", + "com.hms.generator.checker_grid", + "com.hms.generator.stripes_chaser", + "com.hms.generator.noise_clouds", + "com.hms.generator.plasma", + "com.hms.generator.wave_bars", + "com.hms.generator.shapes", + "com.hms.generator.drops_ripples", + "com.hms.generator.starfield", +} + +REQUIRED_FILTERS = { + "com.hms.fx.transform2d", + "com.hms.fx.color_adjust", + "com.hms.fx.gradient_map", + "com.hms.fx.blur_sharpen", + "com.hms.fx.pixelate_quantize", + "com.hms.fx.mirror_tile", + "com.hms.fx.kaleidoscope", + "com.hms.fx.wave_displace", + "com.hms.fx.rgb_split", + "com.hms.fx.glow_bloom", + "com.hms.fx.edge_emboss", + "com.hms.fx.vignette", + "com.hms.fx.strobe_pulse", + "com.hms.fx.feedback_trails", +} + + +def _plugin_dirs() -> list[Path]: + dirs: list[Path] = [] + for category in ("generators", "filters"): + dirs.extend(sorted((BUILTIN / category).glob("com.*"))) + return dirs + + +def _shaders_of(plugin_dir: Path) -> list[Path]: + return [p for p in (plugin_dir / "shaders").rglob("*") if p.is_file()] + + +# ---------- Vollständigkeit (§15.1, §15.2) ---------- + + +def test_all_mandatory_generators_present() -> None: + found = {d.name for d in _plugin_dirs()} + missing = REQUIRED_GENERATORS - found + assert not missing, f"fehlende Pflicht-Generatoren: {sorted(missing)}" + + +def test_all_mandatory_filters_present() -> None: + found = {d.name for d in _plugin_dirs()} + missing = REQUIRED_FILTERS - found + assert not missing, f"fehlende Pflicht-Filter: {sorted(missing)}" + + +def test_exactly_the_mandatory_set() -> None: + """Keine unautorisierten Extras im Pflicht-Verzeichnis (Verwechselungs- + schutz; Beispiele leben in plugins/examples).""" + found = {d.name for d in _plugin_dirs()} + expected = REQUIRED_GENERATORS | REQUIRED_FILTERS + assert found == expected, ( + f"unerwartete Differenz: extra={sorted(found - expected)} " + f"fehlt={sorted(expected - found)}" + ) + + +# ---------- Parametrisierte Einzelprüfungen ---------- + + +@pytest.mark.parametrize("plugin_dir", _plugin_dirs(), ids=lambda p: p.name) +class TestBuiltinPlugin: + def test_manifest_validates(self, plugin_dir: Path) -> None: + manifest, errors = load_manifest(plugin_dir) + assert errors == [], errors + assert manifest["vendor"] == "HMS" + assert manifest["failure_mode"] == "bypass" + assert manifest["schema_version"] == 1 + assert manifest["api_version"] == 1 + + def test_kind_matches_category(self, plugin_dir: Path) -> None: + manifest, _ = load_manifest(plugin_dir) + category = plugin_dir.parent.name + expected = "generator" if category == "generators" else "filter" + assert manifest["kind"] == expected + + def test_all_backends_declared(self, plugin_dir: Path) -> None: + """§12.6 Backend-Vertrag: V1-Plugins liefern d3d11, gl und gles.""" + manifest, _ = load_manifest(plugin_dir) + assert set(manifest["entrypoints"]) == {"d3d11", "gl", "gles"} + assert set(manifest["capabilities"]["supported_backends"]) == { + "d3d11", + "gl", + "gles", + } + + def test_dmx_slot_footprint_max_8(self, plugin_dir: Path) -> None: + """§14.7: maximal 8 generische DMX-Slots je Plugin.""" + manifest, _ = load_manifest(plugin_dir) + total = sum(len(p.get("dmx_slots", [])) for p in manifest["parameters"]) + assert total <= 8, f"{plugin_dir.name}: {total} DMX-Slots > 8" + + def test_adaptive_quality_variants(self, plugin_dir: Path) -> None: + """§15.1/§15.2: AQ mit mindestens 3 Varianten; Parametersemantik + bleibt über Varianten unverändert (semantic_parameters_unchanged + darf nur AQ-interne Größen nennen, nie semantische Parameter wegnehmen).""" + manifest, _ = load_manifest(plugin_dir) + aq = manifest.get("adaptive_quality") + assert aq is not None, "adaptive_quality fehlt" + ids = [v["id"] for v in aq["variants"]] + assert len(ids) >= 3, f"nur {len(ids)} AQ-Varianten" + + def test_shader_uniform_contract(self, plugin_dir: Path) -> None: + """§14.4: Standard-Inputs sind in jedem Shader deklariert; Filter + sampeln die Eingabetextur, Generatoren deklarieren sie (Uniform-Satz + identisch), auch wenn sie sie nicht nutzen.""" + shaders = _shaders_of(plugin_dir) + assert shaders, "keine Shader-Dateien" + for shader in shaders: + src = shader.read_text(encoding="utf-8") + for uniform in ("u_resolution", "u_time_seconds", "u_layer_opacity"): + assert uniform in src, f"{shader.name}: {uniform} fehlt" + + def test_generator_shader_has_no_input_sampling(self, plugin_dir: Path) -> None: + """Generatoren erzeugen Inhalte ohne Eingangsbild (§12.3): der + Shader darf die Eingabetextur nicht als Quelle sampeln.""" + manifest, _ = load_manifest(plugin_dir) + if manifest["kind"] != "generator": + return + for shader in _shaders_of(plugin_dir): + src = shader.read_text(encoding="utf-8") + sample_calls = ( + src.count("u_input_texture.Sample") + + src.count("texture(u_input_texture") + + src.count("texture2D(u_input_texture") + ) + assert sample_calls == 0, ( + f"{plugin_dir.name}/{shader.name}: Generator sampelt u_input_texture" + ) + + def test_filter_mix_zero_is_free_bypass(self, plugin_dir: Path) -> None: + """§15.3: jeder Filter besitzt mix; Mix 0 bypassed kostengünstig.""" + manifest, _ = load_manifest(plugin_dir) + if manifest["kind"] != "filter": + return + ids = [p["id"] for p in manifest["parameters"]] + assert "mix" in ids, f"{plugin_dir.name}: mix-Parameter fehlt"