forked from 0xhckr/ghostty-shaders
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauto-tracking-spotlight
More file actions
64 lines (51 loc) · 2.4 KB
/
auto-tracking-spotlight
File metadata and controls
64 lines (51 loc) · 2.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// Spotlight that follows the terminal caret (cursor) with correct bright-in/dark-out
// Fix: bright inside circle (=1.0), darkened outside by AMBIENT_LIGHT; fallback to random wander when no cursor
// ---------- Parameters ----------
const float SPOTLIGHT_RADIUS = 0.25; // Radius (normalized distance based on short edge)
const float EDGE_WIDTH = 0.020; // Edge transition width (0.01~0.04 for more natural effect)
const float AMBIENT_LIGHT = 0.50; // Minimum brightness outside circle (0=pure black, 1=no darkening)
const float FOLLOW_DURATION = 0.18; // Follow transition duration (seconds)
// --------------------------
float easeOutExpo(float t) {
return (t <= 0.0) ? 0.0 : 1.0 - pow(2.0, -10.0 * t);
}
vec2 fallbackWanderCenter() {
float t = iTime;
return vec2(
0.5 + 0.4 * sin(t * 1.0 + 0.0),
0.5 + 0.4 * sin(t * 1.3 + 3.14159265)
);
}
// Note: cursor rect in repo defines y as "top edge" → center y = y - h/2
vec2 rectCenterPx(vec4 r) { return vec2(r.x + 0.5*r.z, r.y - 0.5*r.w); }
// Returns UV center (0..1), or vec2(-1) if invalid
vec2 cursorCenterUV_smooth() {
vec4 cur = iCurrentCursor;
vec4 prv = iPreviousCursor;
bool usable = (cur.z > 0.0 && cur.w > 0.0);
if (!usable) return vec2(-1.0);
vec2 c0 = rectCenterPx(prv) / iResolution.xy;
vec2 c1 = rectCenterPx(cur) / iResolution.xy;
float t = clamp((iTime - iTimeCursorChange) / FOLLOW_DURATION, 0.0, 1.0);
float k = easeOutExpo(t);
vec2 c = mix(c0, c1, k);
return clamp(c, vec2(0.0), vec2(1.0));
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec4 tex = texture(iChannel0, uv);
// Calculate center: prioritize cursor, otherwise random wander
vec2 center = cursorCenterUV_smooth();
if (center.x < 0.0) {
center = fallbackWanderCenter();
}
// "Circular distance" after aspect ratio correction
vec2 ratio = vec2(iResolution.x / iResolution.y, 1.0);
float d = length((uv - center) * ratio);
// Key fix: use canonical smoothstep order to get "inside circle mask" (center=1, outside=0)
// maskInside = 1 - smoothstep(R, R+W, d)
float maskInside = 1.0 - smoothstep(SPOTLIGHT_RADIUS, SPOTLIGHT_RADIUS + EDGE_WIDTH, d);
// Brightness: inside circle=1.0, outside=AMBIENT_LIGHT
float brightness = mix(AMBIENT_LIGHT, 1.0, maskInside);
fragColor = vec4(tex.rgb * brightness, tex.a);
}