diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 07:41:35 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:16 -0300 |
| commit | 411d0b6059192b75853e220e1242b694ae461a3e (patch) | |
| tree | 50f8bb207f3cdda497bdd84658a2d3ea74b4bc2b /shaders/prebuilt/post-vignette.frag.glsl | |
| parent | da920ca49e3f5d1649e811bbdc3ddba482c6aae4 (diff) | |
| parent | 5526c68beec454cfe607a579723b97b4848e7d3a (diff) | |
| download | pardes-411d0b6059192b75853e220e1242b694ae461a3e.tar.gz pardes-411d0b6059192b75853e220e1242b694ae461a3e.zip | |
Merge: 9P rounds 9-17 + render DPI, dark tints, G4-G10
Diffstat (limited to 'shaders/prebuilt/post-vignette.frag.glsl')
| -rw-r--r-- | shaders/prebuilt/post-vignette.frag.glsl | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/shaders/prebuilt/post-vignette.frag.glsl b/shaders/prebuilt/post-vignette.frag.glsl new file mode 100644 index 00000000..d5737901 --- /dev/null +++ b/shaders/prebuilt/post-vignette.frag.glsl @@ -0,0 +1,139 @@ +#version 450 +// pardes's Shadertoy prefix (docs/render-pipeline.md ยง6), put before every +// post pass: a bundled one in shaders/post/ at build time, a user's file at +// run time. The uniform names, types and std140 order are ghostty 1.3.2's +// (src/renderer/shaders/shadertoy_prefix.glsl), so a shader written for +// ghostty runs here; only the bindings are SDL GPU's (a fragment shader's +// textures in set 2, its uniform blocks in set 3). Y is down everywhere, as +// on ghostty's Metal renderer: fragCoord, iChannel0's uv and the cursor +// rectangles share one top-left space, and iCurrentCursor is (left x, +// BOTTOM-edge y, width, height) in framebuffer pixels. + +layout(set = 3, binding = 0, std140) uniform Globals { + uniform vec3 iResolution; + uniform float iTime; + uniform float iTimeDelta; + uniform float iFrameRate; + uniform int iFrame; + uniform float iChannelTime[4]; + uniform vec3 iChannelResolution[4]; + uniform vec4 iMouse; + uniform vec4 iDate; + uniform float iSampleRate; + uniform vec4 iCurrentCursor; + uniform vec4 iPreviousCursor; + uniform vec4 iCurrentCursorColor; + uniform vec4 iPreviousCursorColor; + uniform int iCurrentCursorStyle; + uniform int iPreviousCursorStyle; + uniform int iCursorVisible; + uniform float iTimeCursorChange; + uniform float iTimeFocus; + uniform int iFocus; +}; +// ghostty's one block, cut in three: SDL GPU binds at most 4 KiB of a +// uniform block (Vulkan), and the palette alone is 4 KiB. The names and +// their order are ghostty's; each block is a slice of the same memory. +layout(set = 3, binding = 1, std140) uniform Palette { + uniform vec3 iPalette[256]; +}; +layout(set = 3, binding = 2, std140) uniform Colors { + uniform vec3 iBackgroundColor; + uniform vec3 iForegroundColor; + uniform vec3 iCursorColor; + uniform vec3 iCursorText; + uniform vec3 iSelectionForegroundColor; + uniform vec3 iSelectionBackgroundColor; +}; + +// pardes's own: pardesOpaque is 1 when the window is opaque, so a pass +// cannot punch holes in it through its alpha; pardesLevel is a bundled +// pass's strength, 1 to 3 (`Crt 3`), and 0 for a user's file; pardesScale +// is the display's density (2 on a 2x screen), for a pass whose pattern is +// in logical pixels. iResolution, fragCoord and iMouse stay in the display's +// pixels, as ghostty's do. pardesCap is the most a bundled pass may do on +// this theme and still leave its text, and its selection's, at their own +// contrast or 4.5 (docs/effects.md); pardesSpare holds pardesSpareCount +// rectangles (x0, y0, x1, y1 in fragCoord's pixels) a bundled pass leaves +// alone: every tag, grip and notice, and the cursor, so no pass ever touches +// a focus indicator. +layout(set = 3, binding = 3, std140) uniform PardesPost { + float pardesOpaque; + float pardesLevel; + float pardesScale; + float pardesCap; + float pardesSpareCount; + vec4 pardesSpare[32]; +}; + +bool pardesSpared(vec2 p) { + for (int i = 0; i < int(pardesSpareCount); i++) { + vec4 r = pardesSpare[i]; + if (p.x >= r.x && p.y >= r.y && p.x < r.z && p.y < r.w) return true; + } + return false; +} + +// A bundled pass's constant at levels 1, 2 and 3. +float pardesByLevel(float one, float two, float three) { + return pardesLevel < 1.5 ? one : (pardesLevel < 2.5 ? two : three); +} + +#define CURSORSTYLE_BLOCK 0 +#define CURSORSTYLE_BLOCK_HOLLOW 1 +#define CURSORSTYLE_BAR 2 +#define CURSORSTYLE_UNDERLINE 3 +#define CURSORSTYLE_LOCK 4 + +layout(set = 2, binding = 0) uniform sampler2D iChannel0; + +layout(location = 0) out vec4 _fragColor; + +#define texture2D texture + +void mainImage(out vec4 fragColor, in vec2 fragCoord); +void main() { + mainImage(_fragColor, gl_FragCoord.xy); + if (pardesOpaque > 0.5) _fragColor.a = 1.0; +} + +// The shader's own lines count from 1, so glslc's errors name them. +#line 1 +// Vignette: the window's corners fall a little into shade, in linear light, +// so the eye settles on the middle. Round, whatever the window's shape; +// nothing within half the way to a corner is touched. At most 28% at the +// far corners (level 3), and never more than pardesCap, so text and its +// selection keep their own contrast or 4.5. Tags, grips, notices and the +// cursor are left alone (pardesSpare): the focus indicators never darken. +// It is a Shadertoy file: pardes puts its prefix before it. + +float strength() { return min(pardesByLevel(0.10, 0.18, 0.28), pardesCap); } + +vec3 toLinear(vec3 c) { + return mix(pow((c + 0.055) / 1.055, vec3(2.4)), c / 12.92, lessThanEqual(c, vec3(0.04045))); +} + +vec3 toSrgb(vec3 c) { + c = max(c, vec3(0.0)); + return mix(1.055 * pow(c, vec3(1.0 / 2.4)) - 0.055, c * 12.92, lessThanEqual(c, vec3(0.0031308))); +} + +float ign(vec2 p) { + return fract(52.9829189 * fract(dot(p, vec2(0.06711056, 0.00583715)))); +} + +void mainImage(out vec4 fragColor, in vec2 fragCoord) { + vec2 uv = fragCoord / iResolution.xy; + vec4 frame = texture(iChannel0, uv); + if (pardesSpared(fragCoord)) { + fragColor = frame; + return; + } + // 0 at the middle, 1 at a corner, on a circle's measure. + vec2 half_size = 0.5 * iResolution.xy; + float r = length(fragCoord - half_size) / length(half_size); + float shade = strength() * pow(smoothstep(0.5, 1.0, r), 1.5); + vec3 srgb = toSrgb(toLinear(frame.rgb) * (1.0 - shade)); + srgb += (ign(fragCoord) - 0.5) / 255.0 * step(1e-4, shade); + fragColor = vec4(clamp(srgb, 0.0, 1.0), frame.a); +} |
