#version 440 layout(location = 0) in vec2 qt_TexCoord0; layout(location = 0) out vec4 fragColor; // NOTE: std140 layout. Order/types must match the ShaderEffect properties by name. layout(std140, binding = 0) uniform buf { mat4 qt_Matrix; float qt_Opacity; vec2 itemSize; // full ShaderEffect size in px (rect + glow padding) vec2 rectSize; // size of the target rectangle in px float radius; // corner radius of the target rectangle in px float glowSize; // how far the glow extends outside the rectangle (px) float innerGlow; // how far the glow bleeds inside (px), 0 = none float lineWidth; // width of the bright edge line (px), 0 = none float phase; // 0..1, loops seamlessly float intensity; // overall brightness vec4 color1; vec4 color2; vec4 color3; vec4 color4; }; const float TAU = 6.28318530718; // Signed distance to a rounded box centred at the origin. <0 inside, >0 outside. float sdRoundBox(vec2 p, vec2 halfSize, float r) { vec2 q = abs(p) - (halfSize - r); return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r; } // Cyclic 4-colour gradient, t is wrapped to [0,1) vec3 palette(float t) { t = fract(t) * 4.0; float f = smoothstep(0.0, 1.0, fract(t)); if (t < 1.0) return mix(color1.rgb, color2.rgb, f); if (t < 2.0) return mix(color2.rgb, color3.rgb, f); if (t < 3.0) return mix(color3.rgb, color4.rgb, f); return mix(color4.rgb, color1.rgb, f); } // Cheap hash for dithering (avoids banding in soft gradients) float hash(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); } void main() { // Pixel position relative to the centre of the item vec2 p = (qt_TexCoord0 - 0.5) * itemSize; float r = clamp(radius, 0.0, min(rectSize.x, rectSize.y) * 0.5); float d = sdRoundBox(p, rectSize * 0.5, r); // ---- colour: rotates around the rectangle, slightly wobbling ---- float ang = atan(p.y, p.x); float a2 = phase * TAU; float u = ang / TAU + 0.5 + phase; u += 0.07 * sin(ang * 2.0 + a2) + 0.04 * sin(ang * 3.0 - a2 * 2.0); vec3 col = palette(u); // ---- intensity falloff ---- float g = 0.0; if (d > 0.0) { float t = clamp(d / max(glowSize, 0.001), 0.0, 1.0); g = pow(1.0 - t, 3.0); } else if (innerGlow > 0.0) { g = exp(d / innerGlow * 3.0); } // Slow "breathing" so the glow feels alive float pulse = 0.9 + 0.1 * sin(a2 * 2.0 + ang); g *= pulse; // Crisp bright line along the edge float line = 0.0; if (lineWidth > 0.0) line = 1.0 - smoothstep(0.0, lineWidth, abs(d)); float a = clamp(g * intensity + line * 0.9, 0.0, 1.0); // Brighten the colour a bit where the line is col = mix(col, vec3(1.0), line * 0.25); a += (hash(gl_FragCoord.xy) - 0.5) / 255.0; a = clamp(a, 0.0, 1.0); // Premultiplied alpha output fragColor = vec4(col * a, a) * qt_Opacity; }