From 4452a8f467765cbfa0dd0444a9bf416ac8bd0bf5 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Tue, 29 Sep 2026 05:44:11 -0300 Subject: G4: bloom, vignette and grain in the post chain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three bundled Shadertoy passes beside Crt, each `Bloom|Vignette|Grain 0..3` (off by default, nothing drawn while off), in shaders/post/: - Bloom: what is brighter than the page (a channel's linear peak over 0.75/0.7/0.6 and over the page's own + 0.15, so a light page does not bloom) glows, 2/3.5/5% (§8.2's budget). The glow is a dual Kawase blur in RGBA16F textures at half size and down, 3/4/5 steps by level (shaders/bloom-down, bloom-up; Post.bloomChain), composited in linear light and dithered. - Vignette: round, from halfway to a corner, 10/18/28% at the far corners, in linear light, dithered. - Grain: a still, triangular-spread grain of 1.5/3/5 of 255 on page-coloured pixels only, a logical pixel big; still, so it asks for no frames. Focus rule: the shell passes every bundled pass the rectangles of the tags with their grips, the column and workspace tags, the notices and the cursor (pardesSpare, at most 32, neighbours on one row merged), and a bundled pass leaves them as they are; and a per-theme ceiling (pardesCap, Post.capsOf) keeps the page's text and the selection's text at min(their contrast, 4.5) under each pass at its strongest. User files get no ceiling and no spared rectangles. Fix found on the way: SDL GPU binds at most 4 KiB of a uniform block, so every Shadertoy colour after iPalette (iBackgroundColor, iForegroundColor, the cursor and selection colours, iPalette[>=4]) read zero. The prefix's block is cut in three (Globals, Palette, Colors) with ghostty's names and order, each pushed as a slice of the one struct; pardes's own block moves to binding 3. Tests: the caps hold over every native theme; each uniform slice fits 4 KiB; the prefix's members in ghostty's order across its blocks. Gates: tty/gui unit-test and core-test pass; snap fails only nested-optout; GUI goldens 01-08 byte-identical (09-18 wait on wkzlsqvy, as in somtrmsz); web builds. Feel review material: .scratch/render/{bloom,vignette,grain}/ (levels on forge, dusk, acme, crops, a levels mp4). --- docs/effects.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'docs/effects.md') diff --git a/docs/effects.md b/docs/effects.md index c8e5c7a7..8073c50a 100644 --- a/docs/effects.md +++ b/docs/effects.md @@ -37,6 +37,7 @@ why. The verdict is the user's. | Ripple, Glitch | — | removed | live window | dropped by the user after a live look | | G1 lift | `Lift shadow\|rim\|auto` (off), `InactiveDim ` | opt-in until the focus-lift default is decided | lift-shots-2 (off, shadow, rim, auto on acme, dusk, forge); frame series + mp4 per Motion flavour | shadow on acme: keep as is. Round 1 dropped glow and surface (surface lowered the focused text's contrast) and made rim a hairline just above the focused tag (light on a dark page, shade on a light one). auto on a dark page recedes the others (InactiveDim 30) | | G3 cursor | follows `Motion`; `CursorBlink on\|off` (on) | glide on by default through the default flavour (smooth); blink on (decision 6) | .scratch/render/cursor/cursor-flavours-grid.mp4 (and quarter speed), raw/// | pending | +| G4 bloom, vignette, grain | `Bloom`, `Vignette`, `Grain` `0..3` (off) | bundled post passes; none touches a tag, grip, notice or the cursor (pardesSpare); each capped per theme (pardesCap) | .scratch/render/{bloom,vignette,grain}/ (levels 0-3 on forge, dusk, acme; crops; mp4 stepping through the levels) | pending | ## Motion flavours @@ -96,3 +97,20 @@ forge's text goes from 15.5:1 to 11.2:1 and dusk's from 6.4:1 to 4.8:1, plainly quieter and still easy reading; at 10 forge barely moves (14.1:1). On a light page the same percent bites far harder (acme: 7.0:1 at 10, the 4.5 floor at 30), which is why `auto` shades there instead of dimming. + +Notes on G4: the three are post passes, so each is a frame of cost only +when it is in the chain. None moves (grain is still, by choice: moving +grain would redraw every frame for a texture), so none asks for frames. +The focus rule is kept by construction: the shell hands every pass the +rectangles of the tags (with their grips), the column and workspace tags, +the notices and the cursor, and a bundled pass leaves them as they are; its +strength is capped per theme so the text on the page and the selection's +text keep min(their contrast, 4.5) (Post.capsOf, tested over the native +themes). Bloom takes only what is brighter than the page, so a light page +does not bloom at all; its glow is a dual Kawase blur in float textures at +half size and down (3 to 5 steps by level), within §8.2's 5%. The vignette +is round, starts halfway to a corner and reaches 10, 18 and 28%. Grain is +1.5, 3 and 5 of 255 on page-coloured pixels only, a logical pixel big. +Found on the way: SDL GPU binds at most 4 KiB of a uniform block, so every +Shadertoy colour after the palette (iBackgroundColor and on) read zero; the +block is now cut in three with ghostty's names and order kept. -- cgit v1.3