neiam
/neiam-devices
public · Issues · Pulls · Labels · Forks · Compare · Actions success · Badges · Packages
⭐
Log in to mark this repository.
moire: make the starfield its own pattern, with depth and halos
c2e4eb1 · Gabriel Morell · 2026-09-29 21:33
Message
{commit_body(@commit)}
Files changed
modified
README.md
+4
−4
@@ -53,12 +53,12 @@ adds to its size.
| 53 | 53 | |
| 54 | 54 | `plasma-wallpaper-neiam-moire` is a Plasma wallpaper plugin ("neiam |
| 55 | 55 | moire" under *Wallpaper type*, for the desktop or the lock screen) |
| 56 | −that draws its patterns live: the moire, a halftone field, both, or | |
| 56 | +that draws its patterns live: the moire, a halftone field, both, | |
| 57 | 57 | bokeh (soft out-of-focus discs drifting upward and fading in and out |
| 58 | 58 | as they rise, 4 to 64 of them, each its own shade from the primary |
| 59 | −towards the accent and from muted to vivid). It can also fade back and | |
| 60 | −forth between any two of those every few minutes. Effects, each a checkbox and usable with any pattern: twinkling stars | |
| 61 | −behind it; a glint, a band of light sweeping across the rings and dots | |
| 59 | +towards the accent and from muted to vivid), or a starfield (sparse | |
| 60 | +stars, mostly faint and a few bright, each twinkling at its own rate). It can also fade back and | |
| 61 | +forth between any two of those every few minutes. Effects, each a checkbox and usable with any pattern: a glint, a band of light sweeping across the rings and dots | |
| 62 | 62 | every minute and a half; breathing, the pattern slowly swelling and |
| 63 | 63 | easing; a few large defocused discs drifting past in front now and |
| 64 | 64 | then, like a light close to the lens; film grain; and dither (on by |
modified
moire/shaders/field.frag
+16
−8
@@ -82,21 +82,29 @@ float hash12(vec2 p)
| 82 | 82 | // Stars on a jittered grid, most faint and a few bright, each |
| 83 | 83 | // twinkling at a whole multiple of the twinkle phase so the wrap is |
| 84 | 84 | // seamless. A star sits well inside its cell, so only one cell needs |
| 85 | −// checking per pixel. | |
| 86 | −float starLight(vec2 p, float px) | |
| 85 | +// checking per pixel. The brightest carry a soft halo. | |
| 86 | +float starLayer(vec2 p, float px, float spacing, float salt) | |
| 87 | 87 | { |
| 88 | − const float spacing = 0.035; | |
| 89 | − vec2 id = floor(p / spacing); | |
| 90 | − if (hash12(id) > 0.4) | |
| 91 | − return 0.0; // 40% of cells hold a star | |
| 92 | − vec2 at = (id + 0.15 + 0.7 * vec2(hash12(id + 17.0), hash12(id + 31.0))) * spacing; | |
| 88 | + vec2 id = floor(p / spacing) + salt; | |
| 89 | + if (hash12(id) > 0.45) | |
| 90 | + return 0.0; // 45% of cells hold a star | |
| 91 | + vec2 at = (floor(p / spacing) + 0.15 + 0.7 * vec2(hash12(id + 17.0), hash12(id + 31.0))) * spacing; | |
| 93 | 92 | float bright = hash12(id + 47.0); |
| 94 | 93 | bright *= bright; |
| 95 | 94 | float rate = 1.0 + floor(hash12(id + 59.0) * 4.0); |
| 96 | 95 | float shimmer = 0.55 + 0.45 * sin(6.2831853 * (rate * twinkle + hash12(id + 71.0))); |
| 97 | 96 | float r = px * (0.8 + 1.4 * bright); |
| 98 | 97 | float d = length(p - at); |
| 99 | − return exp(-d * d / (r * r)) * (0.25 + 0.75 * bright) * shimmer; | |
| 98 | + float core = exp(-d * d / (r * r)) * (0.25 + 0.75 * bright); | |
| 99 | + float halo = exp(-d / (7.0 * px)) * 0.18 * bright * bright * bright; | |
| 100 | + return (core + halo) * shimmer; | |
| 101 | +} | |
| 102 | + | |
| 103 | +// Two layers: the stars proper, and a denser scatter of fainter, | |
| 104 | +// smaller ones behind them that gives the field depth. | |
| 105 | +float starLight(vec2 p, float px) | |
| 106 | +{ | |
| 107 | + return starLayer(p, px, 0.035, 0.0) + 0.4 * starLayer(p, px, 0.016, 113.0); | |
| 100 | 108 | } |
| 101 | 109 | |
| 102 | 110 | // A band of light sweeping diagonally across the screen during the |
modified
moire/theme/contents/config/main.xml
+2
−6
@@ -7,11 +7,11 @@
| 7 | 7 | |
| 8 | 8 | <group name="General"> |
| 9 | 9 | <entry name="Pattern" type="String"> |
| 10 | − <label>moire, halftone, both, bokeh, or alternate</label> | |
| 10 | + <label>moire, halftone, both, bokeh, stars, or alternate</label> | |
| 11 | 11 | <default>both</default> |
| 12 | 12 | </entry> |
| 13 | 13 | <entry name="FadeFrom" type="String"> |
| 14 | − <label>With Pattern alternate, the pattern faded from: moire, halftone, both, or bokeh</label> | |
| 14 | + <label>With Pattern alternate, the pattern faded from: moire, halftone, both, bokeh, or stars</label> | |
| 15 | 15 | <default>moire</default> |
| 16 | 16 | </entry> |
| 17 | 17 | <entry name="FadeTo" type="String"> |
@@ -35,10 +35,6 @@
| 35 | 35 | <label>Now and then, large defocused lights drifting past in front</label> |
| 36 | 36 | <default>false</default> |
| 37 | 37 | </entry> |
| 38 | − <entry name="Stars" type="Bool"> | |
| 39 | − <label>Twinkling stars behind the pattern</label> | |
| 40 | − <default>false</default> | |
| 41 | − </entry> | |
| 42 | 38 | <entry name="Glints" type="Bool"> |
| 43 | 39 | <label>A band of light sweeping across the rings and dots now and then</label> |
| 44 | 40 | <default>false</default> |
modified
moire/theme/contents/ui/Field.qml
+6
−6
@@ -10,16 +10,15 @@ import QtQuick 2.15
| 10 | 10 | Item { |
| 11 | 11 | id: field |
| 12 | 12 | |
| 13 | − // "moire", "halftone", "both" (moire over halftone), "bokeh", or | |
| 14 | − // "alternate": fading between fadeFrom and fadeTo, which are any of | |
| 15 | − // the others, every few minutes. | |
| 13 | + // "moire", "halftone", "both" (moire over halftone), "bokeh", | |
| 14 | + // "stars", or "alternate": fading between fadeFrom and fadeTo, which | |
| 15 | + // are any of the others, every few minutes. | |
| 16 | 16 | property string pattern: "both" |
| 17 | 17 | property string fadeFrom: "moire" |
| 18 | 18 | property string fadeTo: "halftone" |
| 19 | 19 | property int bokehLights: 14 |
| 20 | 20 | // Now and then, a few large defocused discs drifting past in front. |
| 21 | 21 | property bool foreground: false |
| 22 | − property bool stars: false | |
| 23 | 22 | property bool glints: false |
| 24 | 23 | property bool breathing: false |
| 25 | 24 | property bool grain: false |
@@ -56,6 +55,7 @@ Item {
| 56 | 55 | moire: name === "moire" || name === "both" ? 1 : 0, |
| 57 | 56 | halftone: name === "halftone" || name === "both" ? 1 : 0, |
| 58 | 57 | bokeh: name === "bokeh" ? 1 : 0, |
| 58 | + stars: name === "stars" ? 1 : 0, | |
| 59 | 59 | } |
| 60 | 60 | } |
| 61 | 61 |
@@ -108,23 +108,23 @@ Item {
| 108 | 108 | property real moire: field.amount("moire") |
| 109 | 109 | property real halftone: field.amount("halftone") |
| 110 | 110 | property real bokeh: field.amount("bokeh") |
| 111 | + property real stars: field.amount("stars") | |
| 111 | 112 | property real lights: field.bokehLights |
| 112 | 113 | property real flare: field.foreground ? 1 : 0 |
| 113 | 114 | Behavior on flare { NumberAnimation { duration: 1500 } } |
| 114 | 115 | |
| 115 | − property real stars: field.stars ? 1 : 0 | |
| 116 | 116 | property real twinkle: (field.clock / 12) % 1 |
| 117 | 117 | property real glints: field.glints ? 1 : 0 |
| 118 | 118 | property real glintPhase: (field.clock / 90) % 1 |
| 119 | 119 | property real grain: field.grain ? 1 : 0 |
| 120 | 120 | property real dither: field.dither ? 1 : 0 |
| 121 | 121 | property real seed: (field.frame * 0.618034) % 1 |
| 122 | − Behavior on stars { NumberAnimation { duration: 1500 } } | |
| 123 | 122 | Behavior on glints { NumberAnimation { duration: 1500 } } |
| 124 | 123 | Behavior on grain { NumberAnimation { duration: 1500 } } |
| 125 | 124 | Behavior on moire { enabled: field.pattern !== "alternate"; NumberAnimation { duration: 1500 } } |
| 126 | 125 | Behavior on halftone { enabled: field.pattern !== "alternate"; NumberAnimation { duration: 1500 } } |
| 127 | 126 | Behavior on bokeh { enabled: field.pattern !== "alternate"; NumberAnimation { duration: 1500 } } |
| 127 | + Behavior on stars { enabled: field.pattern !== "alternate"; NumberAnimation { duration: 1500 } } | |
| 128 | 128 | |
| 129 | 129 | property color base: field.base |
| 130 | 130 | property color deep: field.deep |
modified
moire/theme/contents/ui/config.qml
+1
−6
@@ -19,7 +19,6 @@ Kirigami.FormLayout {
| 19 | 19 | property int cfg_FrameRate |
| 20 | 20 | property bool cfg_Wordmark |
| 21 | 21 | property bool cfg_ForegroundBokeh |
| 22 | − property bool cfg_Stars | |
| 23 | 22 | property bool cfg_Glints |
| 24 | 23 | property bool cfg_Breathing |
| 25 | 24 | property bool cfg_Grain |
@@ -42,6 +41,7 @@ Kirigami.FormLayout {
| 42 | 41 | { value: "moire", text: "Moire" }, |
| 43 | 42 | { value: "halftone", text: "Halftone" }, |
| 44 | 43 | { value: "bokeh", text: "Bokeh" }, |
| 44 | + { value: "stars", text: "Starfield" }, | |
| 45 | 45 | ] |
| 46 | 46 | readonly property var patterns: layers.concat([{ value: "alternate", text: "Fade between two…" }]) |
| 47 | 47 |
@@ -135,11 +135,6 @@ Kirigami.FormLayout {
| 135 | 135 | |
| 136 | 136 | QQC2.CheckBox { |
| 137 | 137 | Kirigami.FormData.label: "Effects:" |
| 138 | − text: "Twinkling stars" | |
| 139 | − checked: root.cfg_Stars | |
| 140 | − onToggled: root.cfg_Stars = checked | |
| 141 | − } | |
| 142 | − QQC2.CheckBox { | |
| 143 | 138 | text: "Light glinting across the pattern" |
| 144 | 139 | checked: root.cfg_Glints |
| 145 | 140 | onToggled: root.cfg_Glints = checked |
modified
?
+0
−0
Binary file — not rendered.
modified
moire/theme/contents/ui/main.qml
+0
−1
@@ -33,7 +33,6 @@ WallpaperItem {
| 33 | 33 | fadeTo: root.cfg.FadeTo |
| 34 | 34 | bokehLights: root.cfg.BokehLights |
| 35 | 35 | foreground: root.cfg.ForegroundBokeh |
| 36 | − stars: root.cfg.Stars | |
| 37 | 36 | glints: root.cfg.Glints |
| 38 | 37 | breathing: root.cfg.Breathing |
| 39 | 38 | grain: root.cfg.Grain |
Parents: cced89e