add noctalia and fuzzel

This commit is contained in:
2026-05-23 21:20:58 +02:00
parent 364801f1a3
commit 9a3eceb3ab
599 changed files with 204318 additions and 0 deletions
@@ -0,0 +1,40 @@
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D source;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
vec4 targetColor;
float colorizeMode; // 0.0 = dock mode (grayscale), 1.0 = tray mode (intensity), 2.0 = distro mode (luminance with better contrast)
} ubuf;
void main() {
vec4 tex = texture(source, qt_TexCoord0);
float intensity;
if (ubuf.colorizeMode < 0.5) {
// Dock mode: Convert to grayscale using proper luminance weights
intensity = dot(tex.rgb, vec3(0.299, 0.587, 0.114));
} else if (ubuf.colorizeMode < 1.5) {
// Tray mode: Use the maximum RGB channel value as intensity
intensity = max(max(tex.r, tex.g), tex.b);
// Normalize intensity to make all icons more uniform
intensity = smoothstep(0.1, 0.9, intensity);
} else {
// Distro mode: Brightness boost with proper alpha handling
float maxChannel = max(max(tex.r, tex.g), tex.b);
intensity = maxChannel * 1.5;
intensity = min(intensity, 1.0);
intensity = intensity * 0.7 + 0.3;
intensity = intensity * tex.a;
fragColor = vec4(ubuf.targetColor.rgb * intensity, tex.a) * ubuf.qt_Opacity;
}
fragColor = vec4(ubuf.targetColor.rgb * intensity, tex.a) * ubuf.qt_Opacity;
}
@@ -0,0 +1,59 @@
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf
{
mat4 qt_Matrix;
vec4 params; // x=opacity, y=fixedVal, z=mode, w=padding
};
// Compatibility function for GLSL ES to avoid % operator issues
int mod_compat(float a, float b) {
return int(a - floor(a / b) * b);
}
vec3 hsv2rgb(vec3 c)
{
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
void main()
{
float x = qt_TexCoord0.x;
float y = 1.0 - qt_TexCoord0.y; // Flip Y direction
// Unpack vector
float opacity = params.x;
float fixedVal = params.y;
float mode = params.z + 0.1; // +0.1 safely handles float rounding
// Build permutation matrices for swizzling
vec3 base = vec3(x, y, fixedVal);
// Determine Mode Logic
// 0: (Red/Hue) 1: (Green/Sat) 2: (Blue/Val)
int perm = mod_compat(mode, 3.0);
float isHSV = step(3.0, mode);
// Branchless Selection
vec3 mask = vec3(
float(perm == 0),
float(perm == 1),
float(perm == 2));
// Swizzle fo shizzle
// If perm 0: base.zxy -> (fixedVal, x, y)
// If perm 1: base.xzy -> (x, fixedVal, y)
// If perm 2: base.xyz -> (x, y, fixedVal)
vec3 rgb_base = (base.zxy * mask.x) + (base.xzy * mask.y) + (base.xyz * mask.z);
// Final mix
vec3 finalColor = mix(rgb_base, hsv2rgb(rgb_base), isHSV);
fragColor = vec4(finalColor, 1.0) * opacity;
}
@@ -0,0 +1,147 @@
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D dataSource;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
vec4 lineColor1;
vec4 lineColor2;
float count1;
float count2;
float scroll1;
float scroll2;
float lineWidth;
float graphFillOpacity;
float texWidth;
float resY;
float aaSize;
};
// Sample normalized value from data texture
// channel 0 = primary (R), channel 1 = secondary (G)
float fetchData(float idx, int ch) {
float i = clamp(idx, 0.0, texWidth - 1.0);
float u = (floor(i) + 0.5) / texWidth;
vec4 t = texture(dataSource, vec2(u, 0.5));
return ch == 0 ? t.r : t.g;
}
// Cubic Hermite interpolation with reduced tangent scale for smooth curves
float cubicHermite(float y0, float y1, float y2, float y3, float t) {
float m1 = (y2 - y0) * 0.25;
float m2 = (y3 - y1) * 0.25;
float t2 = t * t;
float t3 = t2 * t;
return (2.0 * t3 - 3.0 * t2 + 1.0) * y1
+ (t3 - 2.0 * t2 + t) * m1
+ (-2.0 * t3 + 3.0 * t2) * y2
+ (t3 - t2) * m2;
}
// Evaluate curve at fractional data index
float evalCurve(float dataIdx, int ch) {
float i = floor(dataIdx);
float t = dataIdx - i;
return cubicHermite(
fetchData(i - 1.0, ch),
fetchData(i, ch),
fetchData(i + 1.0, ch),
fetchData(i + 2.0, ch),
t
);
}
// Squared distance from point p to line segment a→b
float segDistSq(vec2 p, vec2 a, vec2 b) {
vec2 ab = b - a;
float len2 = dot(ab, ab);
float t = len2 > 0.0 ? clamp(dot(p - a, ab) / len2, 0.0, 1.0) : 0.0;
vec2 proj = a + t * ab;
vec2 d = p - proj;
return dot(d, d);
}
// Minimum distance from fragment to curve via multi-segment sampling.
// Samples the curve at 9 half-pixel-spaced x-positions (±2px neighborhood)
// and returns the minimum distance to the 8 line segments between them.
float curveDistance(float dataIdx, float pixStep, float normY, int ch) {
vec2 frag = vec2(0.0, normY * resY);
float px = -2.0;
float py = evalCurve(dataIdx - 2.0 * pixStep, ch) * resY;
vec2 d0 = frag - vec2(px, py);
float best = dot(d0, d0);
for (int i = 1; i <= 8; i++) {
float cx = -2.0 + float(i) * 0.5;
float cy = evalCurve(dataIdx + cx * pixStep, ch) * resY;
best = min(best, segDistSq(frag, vec2(px, py), vec2(cx, cy)));
px = cx;
py = cy;
}
return sqrt(best);
}
// Premultiplied alpha over compositing
vec4 blendOver(vec4 src, vec4 dst) {
return src + dst * (1.0 - src.a);
}
void main() {
vec2 uv = qt_TexCoord0;
float normY = 1.0 - uv.y; // 0 = bottom, 1 = top
vec4 result = vec4(0.0);
float halfW = lineWidth * 0.5;
// Primary line
if (count1 >= 4.0) {
float segs = count1 - 3.0;
float di = 2.0 + scroll1 + uv.x * segs;
float pixStep = dFdx(di);
float cy = evalCurve(di, 0);
float cyNext = evalCurve(di + pixStep, 0);
// Fill below curve (gradient: opaque at top, transparent at bottom)
if (graphFillOpacity > 0.0 && normY <= cy) {
float a = graphFillOpacity * normY * lineColor1.a;
result = blendOver(vec4(lineColor1.rgb * a, a), result);
}
// Multi-segment distance for accurate AA at peaks and steep sections.
// AA width derived analytically from curve slope: (|sinθ|+|cosθ|)
// gives the ideal SDF fwidth (~1.01.41) without GPU derivative noise.
float dist = curveDistance(di, pixStep, normY, 0);
float slope1 = (cyNext - cy) * resY;
float aa = (abs(slope1) + 1.0) * inversesqrt(slope1 * slope1 + 1.0) * aaSize * 2.0;
float sa = smoothstep(halfW + aa, halfW, dist) * lineColor1.a;
result = blendOver(vec4(lineColor1.rgb * sa, sa), result);
}
// Secondary line
if (count2 >= 4.0) {
float segs = count2 - 3.0;
float di = 2.0 + scroll2 + uv.x * segs;
float pixStep = dFdx(di);
float cy = evalCurve(di, 1);
float cyNext = evalCurve(di + pixStep, 1);
if (graphFillOpacity > 0.0 && normY <= cy) {
float a = graphFillOpacity * normY * lineColor2.a;
result = blendOver(vec4(lineColor2.rgb * a, a), result);
}
float dist = curveDistance(di, pixStep, normY, 1);
float slope2 = (cyNext - cy) * resY;
float aa = (abs(slope2) + 1.0) * inversesqrt(slope2 * slope2 + 1.0) * aaSize * 2.0;
float sa = smoothstep(halfW + aa, halfW, dist) * lineColor2.a;
result = blendOver(vec4(lineColor2.rgb * sa, sa), result);
}
fragColor = result * qt_Opacity;
}
@@ -0,0 +1,67 @@
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D source;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float progress;
float borderWidth;
vec4 progressColor;
vec4 borderColor;
vec4 backgroundColor;
float borderRadius;
} ubuf;
void main() {
vec2 coord = qt_TexCoord0;
float p = clamp(ubuf.progress, 0.0, 1.0);
if (ubuf.borderRadius > 0.0) {
// Circular progress
vec2 center = vec2(0.5, 0.5);
vec2 dir = coord - center;
float dist = length(dir);
float outerRadius = 0.5;
float innerRadius = outerRadius - ubuf.borderWidth;
float angle = atan(dir.y, dir.x) + radians(90.0);
if (angle < 0.0) angle += radians(360.0);
float maxAngle = radians(360.0) * p;
bool inBorder = dist >= innerRadius && dist <= outerRadius;
bool inProgress = inBorder && angle <= maxAngle;
if (inProgress) {
fragColor = ubuf.progressColor * ubuf.qt_Opacity;
} else if (inBorder) {
fragColor = ubuf.borderColor * ubuf.qt_Opacity;
} else if (dist < innerRadius) {
fragColor = ubuf.backgroundColor * ubuf.qt_Opacity;
} else {
fragColor = vec4(0.0, 0.0, 0.0, 0.0);
}
} else {
// Rectangular progress
bool inBorder =
coord.x < ubuf.borderWidth ||
coord.x > (1.0 - ubuf.borderWidth) ||
coord.y < ubuf.borderWidth ||
coord.y > (1.0 - ubuf.borderWidth);
float progressPos = p;
bool inProgress = inBorder && coord.x <= progressPos;
if (inProgress) {
fragColor = ubuf.progressColor * ubuf.qt_Opacity;
} else if (inBorder) {
fragColor = ubuf.borderColor * ubuf.qt_Opacity;
} else {
fragColor = ubuf.backgroundColor * ubuf.qt_Opacity;
}
}
}
@@ -0,0 +1,101 @@
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D source;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
// Custom properties with non-conflicting names
float itemWidth;
float itemHeight;
float sourceWidth;
float sourceHeight;
float cornerRadius;
float imageOpacity;
int fillMode;
} ubuf;
// Function to calculate the signed distance from a point to a rounded box
float roundedBoxSDF(vec2 centerPos, vec2 boxSize, float radius) {
vec2 d = abs(centerPos) - boxSize + radius;
return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0) - radius;
}
void main() {
// Get size from uniforms
vec2 itemSize = vec2(ubuf.itemWidth, ubuf.itemHeight);
vec2 sourceSize = vec2(ubuf.sourceWidth, ubuf.sourceHeight);
float cornerRadius = ubuf.cornerRadius;
float itemOpacity = ubuf.imageOpacity;
int fillMode = ubuf.fillMode;
// Work in pixel space for accurate rounded rectangle calculation
vec2 pixelPos = qt_TexCoord0 * itemSize;
// Calculate UV coordinates based on fill mode
vec2 imageUV = qt_TexCoord0;
// fillMode constants from Qt:
// Image.Stretch = 0
// Image.PreserveAspectFit = 1
// Image.PreserveAspectCrop = 2
// Image.Tile = 3
// Image.TileVertically = 4
// Image.TileHorizontally = 5
// Image.Pad = 6
// Rounded corners always apply to full item bounds
vec2 roundedSize = itemSize;
vec2 roundedCenter = itemSize * 0.5;
// Track if pixel is in letterbox area (for PreserveAspectFit)
bool inLetterbox = false;
if (fillMode == 1) { // PreserveAspectFit
float itemAspect = itemSize.x / itemSize.y;
float sourceAspect = sourceSize.x / sourceSize.y;
if (sourceAspect > itemAspect) {
// Image is wider than item, letterbox top/bottom
imageUV.y = (qt_TexCoord0.y - 0.5) * (sourceAspect / itemAspect) + 0.5;
} else {
// Image is taller than item, letterbox left/right
imageUV.x = (qt_TexCoord0.x - 0.5) * (itemAspect / sourceAspect) + 0.5;
}
// Check if in letterbox area
inLetterbox = (imageUV.x < 0.0 || imageUV.x > 1.0 || imageUV.y < 0.0 || imageUV.y > 1.0);
} else if (fillMode == 2) { // PreserveAspectCrop
float itemAspect = itemSize.x / itemSize.y;
float sourceAspect = sourceSize.x / sourceSize.y;
if (sourceAspect > itemAspect) {
// Image is wider than item, crop left/right.
imageUV.x = (qt_TexCoord0.x - 0.5) * (itemAspect / sourceAspect) + 0.5;
} else {
// Image is taller than item, crop top/bottom.
imageUV.y = (qt_TexCoord0.y - 0.5) * (sourceAspect / itemAspect) + 0.5;
}
}
// For Stretch (0) or other modes, use qt_TexCoord0 as-is
// Calculate distance to rounded rectangle edge using the correct bounds
vec2 centerOffset = pixelPos - roundedCenter;
float distance = roundedBoxSDF(centerOffset, roundedSize * 0.5, cornerRadius);
// Create smooth alpha mask for edge with anti-aliasing
float alpha = 1.0 - smoothstep(-0.5, 0.5, distance);
// Sample the texture (or use transparent for letterbox)
// Clamp UV to prevent texture wrapping artifacts from floating-point imprecision
vec4 color = inLetterbox ? vec4(0.0) : texture(source, clamp(imageUV, vec2(0.0), vec2(1.0)));
// Apply the rounded mask and opacity
// Qt textures use premultiplied alpha (color.rgb already contains rgb * alpha),
// so we only multiply by the external mask factors, not by color.a again
float mask = alpha * itemOpacity * ubuf.qt_Opacity;
fragColor = color * mask;
}
@@ -0,0 +1,82 @@
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D dataSource;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
vec4 fillColor;
float count;
float texWidth;
float vertical;
float mirrored;
};
// Sample amplitude from data texture (R channel)
float fetchData(float idx) {
float i = clamp(idx, 0.0, texWidth - 1.0);
float u = (floor(i) + 0.5) / texWidth;
return texture(dataSource, vec2(u, 0.5)).r;
}
// Cubic Hermite interpolation for smooth wave curves
float cubicHermite(float y0, float y1, float y2, float y3, float t) {
float m1 = (y2 - y0) * 0.25;
float m2 = (y3 - y1) * 0.25;
float t2 = t * t;
float t3 = t2 * t;
return (2.0 * t3 - 3.0 * t2 + 1.0) * y1
+ (t3 - 2.0 * t2 + t) * m1
+ (-2.0 * t3 + 3.0 * t2) * y2
+ (t3 - t2) * m2;
}
// Evaluate interpolated amplitude at fractional data index
float evalCurve(float dataIdx) {
float i = floor(dataIdx);
float t = dataIdx - i;
return cubicHermite(
fetchData(i - 1.0),
fetchData(i),
fetchData(i + 1.0),
fetchData(i + 2.0),
t
);
}
void main() {
vec2 uv = qt_TexCoord0;
// Swap axes for vertical mode
float axisPos = (vertical > 0.5) ? uv.y : uv.x;
float crossPos = (vertical > 0.5) ? uv.x : uv.y;
// Map axis position to data index
float dataIdx;
if (mirrored > 0.5) {
// Mirror: value[0] at center, value[count-1] at edges
float distFromCenter = abs(axisPos - 0.5) * 2.0;
dataIdx = distFromCenter * max(count - 1.0, 1.0);
} else {
// Linear: value[0] at left/top, value[count-1] at right/bottom
dataIdx = axisPos * max(count - 1.0, 1.0);
}
// Interpolated amplitude, clamped to valid range
float amplitude = clamp(evalCurve(dataIdx), 0.0, 1.0);
// Wave fills center ± amplitude/2 in the cross axis
float halfAmp = amplitude * 0.5;
float distFromMid = abs(crossPos - 0.5);
// Antialiased edge (~1px smooth transition)
float edge = fwidth(crossPos) * 1.5;
float mask = smoothstep(halfAmp + edge, halfAmp - edge, distFromMid);
// Premultiplied alpha output
float a = mask * fillColor.a;
fragColor = vec4(fillColor.rgb * a, a) * qt_Opacity;
}
@@ -0,0 +1,123 @@
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float time;
float itemWidth;
float itemHeight;
vec4 bgColor;
float cornerRadius;
float alternative;
} ubuf;
// Signed distance function for rounded rectangle
float roundedBoxSDF(vec2 center, vec2 size, float radius) {
vec2 q = abs(center) - size + radius;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
}
float hash(vec2 p) {
p = fract(p * vec2(234.34, 435.345));
p += dot(p, p + 34.23);
return fract(p.x * p.y);
}
// Perlin-like noise
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
f = f * f * (3.0 - 2.0 * f); // Smooth interpolation
float a = hash(i);
float b = hash(i + vec2(1.0, 0.0));
float c = hash(i + vec2(0.0, 1.0));
float d = hash(i + vec2(1.0, 1.0));
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}
// Turbulent noise for natural fog
float turbulence(vec2 p, float iTime) {
float t = 0.0;
float scale = 1.0;
for(int i = 0; i < 5; i++) {
t += abs(noise(p * scale + iTime * 0.1 * scale)) / scale;
scale *= 2.0;
}
return t;
}
void main() {
vec2 uv = qt_TexCoord0;
vec4 col = vec4(ubuf.bgColor.rgb, 1.0);
// Different parameters for fog vs clouds
float timeSpeed, layerScale1, layerScale2, layerScale3;
float flowSpeed1, flowSpeed2;
float densityMin, densityMax;
float baseOpacity;
float pulseAmount;
if (ubuf.alternative > 0.5) {
// Fog: slower, larger scale, more uniform
timeSpeed = 0.03;
layerScale1 = 1.0;
layerScale2 = 2.5;
layerScale3 = 2.0;
flowSpeed1 = 0.00;
flowSpeed2 = 0.02;
densityMin = 0.1;
densityMax = 0.9;
baseOpacity = 0.75;
pulseAmount = 0.05;
} else {
// Clouds: faster, smaller scale, puffier
timeSpeed = 0.08;
layerScale1 = 2.0;
layerScale2 = 4.0;
layerScale3 = 6.0;
flowSpeed1 = 0.03;
flowSpeed2 = 0.04;
densityMin = 0.35;
densityMax = 0.75;
baseOpacity = 0.4;
pulseAmount = 0.15;
}
float iTime = ubuf.time * timeSpeed;
// Create flowing patterns with multiple layers
vec2 flow1 = vec2(iTime * flowSpeed1, iTime * flowSpeed1 * 0.7);
vec2 flow2 = vec2(-iTime * flowSpeed2, iTime * flowSpeed2 * 0.8);
float fog1 = noise(uv * layerScale1 + flow1);
float fog2 = noise(uv * layerScale2 + flow2);
float fog3 = turbulence(uv * layerScale3, iTime);
float fogPattern = fog1 * 0.5 + fog2 * 0.3 + fog3 * 0.2;
float fogDensity = smoothstep(densityMin, densityMax, fogPattern);
// Gentle pulsing
float pulse = sin(iTime * 0.4) * pulseAmount + (1.0 - pulseAmount);
fogDensity *= pulse;
vec3 hazeColor = vec3(0.88, 0.90, 0.93);
float hazeOpacity = fogDensity * baseOpacity;
vec3 fogContribution = hazeColor * hazeOpacity;
float fogAlpha = hazeOpacity;
vec3 resultRGB = fogContribution + col.rgb * (1.0 - fogAlpha);
float resultAlpha = fogAlpha + col.a * (1.0 - fogAlpha);
// Calculate corner mask
vec2 pixelPos = qt_TexCoord0 * vec2(ubuf.itemWidth, ubuf.itemHeight);
vec2 center = pixelPos - vec2(ubuf.itemWidth, ubuf.itemHeight) * 0.5;
vec2 halfSize = vec2(ubuf.itemWidth, ubuf.itemHeight) * 0.5;
float dist = roundedBoxSDF(center, halfSize, ubuf.cornerRadius);
float cornerMask = 1.0 - smoothstep(-1.0, 0.0, dist);
// Apply global opacity and corner mask
float finalAlpha = resultAlpha * ubuf.qt_Opacity * cornerMask;
fragColor = vec4(resultRGB * (finalAlpha / max(resultAlpha, 0.001)), finalAlpha);
}
@@ -0,0 +1,84 @@
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D source;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float time;
float itemWidth;
float itemHeight;
vec4 bgColor;
float cornerRadius;
} ubuf;
// Signed distance function for rounded rectangle
float roundedBoxSDF(vec2 center, vec2 size, float radius) {
vec2 q = abs(center) - size + radius;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
}
vec3 hash3(vec2 p) {
vec3 q = vec3(dot(p, vec2(127.1, 311.7)),
dot(p, vec2(269.5, 183.3)),
dot(p, vec2(419.2, 371.9)));
return fract(sin(q) * 43758.5453);
}
float noise(vec2 x, float iTime) {
vec2 p = floor(x);
vec2 f = fract(x);
float va = 0.0;
for (int j = -2; j <= 2; j++) {
for (int i = -2; i <= 2; i++) {
vec2 g = vec2(float(i), float(j));
vec3 o = hash3(p + g);
vec2 r = g - f + o.xy;
float d = sqrt(dot(r, r));
float ripple = max(mix(smoothstep(0.99, 0.999, max(cos(d - iTime * 2.0 + (o.x + o.y) * 5.0), 0.0)), 0.0, d), 0.0);
va += ripple;
}
}
return va;
}
void main() {
vec2 uv = qt_TexCoord0;
float iTime = ubuf.time * 0.07;
// Aspect ratio correction for circular ripples
float aspect = ubuf.itemWidth / ubuf.itemHeight;
vec2 uvAspect = vec2(uv.x * aspect, uv.y);
float f = noise(6.0 * uvAspect, iTime) * smoothstep(0.0, 0.2, sin(uv.x * 3.141592) * sin(uv.y * 3.141592));
// Calculate normal from noise for distortion
float normalScale = 0.5;
vec2 e = normalScale / vec2(ubuf.itemWidth, ubuf.itemHeight);
vec2 eAspect = vec2(e.x * aspect, e.y);
float cx = noise(6.0 * (uvAspect + eAspect), iTime) * smoothstep(0.0, 0.2, sin((uv.x + e.x) * 3.141592) * sin(uv.y * 3.141592));
float cy = noise(6.0 * (uvAspect + eAspect.yx), iTime) * smoothstep(0.0, 0.2, sin(uv.x * 3.141592) * sin((uv.y + e.y) * 3.141592));
vec2 n = vec2(cx - f, cy - f);
// Scale distortion back to texture space (undo aspect correction for X)
vec2 distortion = vec2(n.x / aspect, n.y);
// Sample source with distortion
vec4 col = texture(source, uv + distortion);
// Apply rounded corner mask
vec2 pixelPos = qt_TexCoord0 * vec2(ubuf.itemWidth, ubuf.itemHeight);
vec2 center = pixelPos - vec2(ubuf.itemWidth, ubuf.itemHeight) * 0.5;
vec2 halfSize = vec2(ubuf.itemWidth, ubuf.itemHeight) * 0.5;
float dist = roundedBoxSDF(center, halfSize, ubuf.cornerRadius);
float cornerMask = 1.0 - smoothstep(-1.0, 0.0, dist);
// Output with premultiplied alpha
float finalAlpha = col.a * ubuf.qt_Opacity * cornerMask;
fragColor = vec4(col.rgb * finalAlpha, finalAlpha);
}
@@ -0,0 +1,75 @@
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float time;
float itemWidth;
float itemHeight;
vec4 bgColor;
float cornerRadius;
} ubuf;
// Signed distance function for rounded rectangle
float roundedBoxSDF(vec2 center, vec2 size, float radius) {
vec2 q = abs(center) - size + radius;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
}
void main() {
// Aspect ratio correction
float aspect = ubuf.itemWidth / ubuf.itemHeight;
vec2 uv = qt_TexCoord0;
uv.x *= aspect;
uv.y = 1.0 - uv.y;
float iTime = ubuf.time * 0.15;
float snow = 0.0;
for (int k = 0; k < 6; k++) {
for (int i = 0; i < 12; i++) {
float cellSize = 2.0 + (float(i) * 3.0);
float downSpeed = 0.3 + (sin(iTime * 0.4 + float(k + i * 20)) + 1.0) * 0.00008;
vec2 uvAnim = uv + vec2(
0.01 * sin((iTime + float(k * 6185)) * 0.6 + float(i)) * (5.0 / float(i + 1)),
downSpeed * (iTime + float(k * 1352)) * (1.0 / float(i + 1))
);
vec2 uvStep = (ceil((uvAnim) * cellSize - vec2(0.5, 0.5)) / cellSize);
float x = fract(sin(dot(uvStep.xy, vec2(12.9898 + float(k) * 12.0, 78.233 + float(k) * 315.156))) * 43758.5453 + float(k) * 12.0) - 0.5;
float y = fract(sin(dot(uvStep.xy, vec2(62.2364 + float(k) * 23.0, 94.674 + float(k) * 95.0))) * 62159.8432 + float(k) * 12.0) - 0.5;
float randomMagnitude1 = sin(iTime * 2.5) * 0.7 / cellSize;
float randomMagnitude2 = cos(iTime * 1.65) * 0.7 / cellSize;
float d = 5.0 * distance((uvStep.xy + vec2(x * sin(y), y) * randomMagnitude1 + vec2(y, x) * randomMagnitude2), uvAnim.xy);
float omiVal = fract(sin(dot(uvStep.xy, vec2(32.4691, 94.615))) * 31572.1684);
if (omiVal < 0.03) {
float newd = (x + 1.0) * 0.4 * clamp(1.9 - d * (15.0 + (x * 6.3)) * (cellSize / 1.4), 0.0, 1.0);
snow += newd;
}
}
}
// Blend white snow over background color
float snowAlpha = clamp(snow * 2.0, 0.0, 1.0);
vec3 snowColor = vec3(1.0);
vec3 blended = mix(ubuf.bgColor.rgb, snowColor, snowAlpha);
// Apply rounded corner mask
vec2 pixelPos = qt_TexCoord0 * vec2(ubuf.itemWidth, ubuf.itemHeight);
vec2 center = pixelPos - vec2(ubuf.itemWidth, ubuf.itemHeight) * 0.5;
vec2 halfSize = vec2(ubuf.itemWidth, ubuf.itemHeight) * 0.5;
float dist = roundedBoxSDF(center, halfSize, ubuf.cornerRadius);
float cornerMask = 1.0 - smoothstep(-1.0, 0.0, dist);
// Output with premultiplied alpha
float finalAlpha = ubuf.qt_Opacity * cornerMask;
fragColor = vec4(blended * finalAlpha, finalAlpha);
}
@@ -0,0 +1,130 @@
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float time;
float itemWidth;
float itemHeight;
vec4 bgColor;
float cornerRadius;
} ubuf;
// Signed distance function for rounded rectangle
float roundedBoxSDF(vec2 center, vec2 size, float radius) {
vec2 q = abs(center) - size + radius;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
}
float hash(vec2 p) {
p = fract(p * vec2(234.34, 435.345));
p += dot(p, p + 34.23);
return fract(p.x * p.y);
}
vec2 hash2(vec2 p) {
p = fract(p * vec2(234.34, 435.345));
p += dot(p, p + 34.23);
return fract(vec2(p.x * p.y, p.y * p.x));
}
float stars(vec2 uv, float density, float iTime) {
vec2 gridUV = uv * density;
vec2 gridID = floor(gridUV);
vec2 gridPos = fract(gridUV);
float starField = 0.0;
// Check neighboring cells for stars
for (int y = -1; y <= 1; y++) {
for (int x = -1; x <= 1; x++) {
vec2 offset = vec2(float(x), float(y));
vec2 cellID = gridID + offset;
// Random position within cell
vec2 starPos = hash2(cellID);
// Only create a star for some cells (sparse distribution)
float starChance = hash(cellID + vec2(12.345, 67.890));
if (starChance > 0.85) {
// Star position in grid space
vec2 toStar = (offset + starPos - gridPos);
float dist = length(toStar) * density; // Scale distance to pixel space
float starSize = 1.5;
// Star brightness variation
float brightness = hash(cellID + vec2(23.456, 78.901)) * 0.6 + 0.4;
// Twinkling effect
float twinkleSpeed = hash(cellID + vec2(34.567, 89.012)) * 3.0 + 2.0;
float twinklePhase = iTime * twinkleSpeed + hash(cellID) * 6.28;
float twinkle = pow(sin(twinklePhase) * 0.5 + 0.5, 3.0); // Sharp on/off
// Sharp star core
float star = 0.0;
if (dist < starSize) {
star = 1.0 * brightness * (0.3 + twinkle * 0.7);
// Add tiny cross-shaped glow for brighter stars
if (brightness > 0.7) {
float crossGlow = max(
exp(-abs(toStar.x) * density * 5.0),
exp(-abs(toStar.y) * density * 5.0)
) * 0.3 * twinkle;
star += crossGlow;
}
}
starField += star;
}
}
}
return starField;
}
void main() {
vec2 uv = qt_TexCoord0;
float iTime = ubuf.time * 0.01;
// Base background color
vec4 col = vec4(ubuf.bgColor.rgb, 1.0);
// Aspect ratio for consistent stars
float aspect = ubuf.itemWidth / ubuf.itemHeight;
vec2 uvAspect = vec2(uv.x * aspect, uv.y);
// Generate multiple layers of stars at different densities
float stars1 = stars(uvAspect, 40.0, iTime); // Tiny distant stars
float stars2 = stars(uvAspect + vec2(0.5, 0.3), 25.0, iTime * 1.3); // Small stars
float stars3 = stars(uvAspect + vec2(0.25, 0.7), 15.0, iTime * 0.9); // Bigger stars
// Star colors with slight variation
vec3 starColor1 = vec3(0.85, 0.9, 1.0); // Faint blue-white
vec3 starColor2 = vec3(0.95, 0.97, 1.0); // White
vec3 starColor3 = vec3(1.0, 0.98, 0.95); // Warm white
// Combine star layers
vec3 starsRGB = starColor1 * stars1 * 0.6 +
starColor2 * stars2 * 0.8 +
starColor3 * stars3 * 1.0;
float starsAlpha = clamp(stars1 * 0.6 + stars2 * 0.8 + stars3, 0.0, 1.0);
// Apply rounded corner mask
vec2 pixelPos = qt_TexCoord0 * vec2(ubuf.itemWidth, ubuf.itemHeight);
vec2 center = pixelPos - vec2(ubuf.itemWidth, ubuf.itemHeight) * 0.5;
vec2 halfSize = vec2(ubuf.itemWidth, ubuf.itemHeight) * 0.5;
float dist = roundedBoxSDF(center, halfSize, ubuf.cornerRadius);
float cornerMask = 1.0 - smoothstep(-1.0, 0.0, dist);
// Add stars on top
vec3 resultRGB = starsRGB * starsAlpha + col.rgb * (1.0 - starsAlpha);
float resultAlpha = starsAlpha + col.a * (1.0 - starsAlpha);
// Apply global opacity and corner mask
float finalAlpha = resultAlpha * ubuf.qt_Opacity * cornerMask;
fragColor = vec4(resultRGB * (finalAlpha / max(resultAlpha, 0.001)), finalAlpha);
}
@@ -0,0 +1,148 @@
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float time;
float itemWidth;
float itemHeight;
vec4 bgColor;
float cornerRadius;
} ubuf;
// Signed distance function for rounded rectangle
float roundedBoxSDF(vec2 center, vec2 size, float radius) {
vec2 q = abs(center) - size + radius;
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
}
float hash(vec2 p) {
p = fract(p * vec2(234.34, 435.345));
p += dot(p, p + 34.23);
return fract(p.x * p.y);
}
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
f = f * f * (3.0 - 2.0 * f);
float a = hash(i);
float b = hash(i + vec2(1.0, 0.0));
float c = hash(i + vec2(0.0, 1.0));
float d = hash(i + vec2(1.0, 1.0));
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}
// God rays originating from sun position
float sunRays(vec2 uv, vec2 sunPos, float iTime) {
vec2 toSun = uv - sunPos;
float angle = atan(toSun.y, toSun.x);
float dist = length(toSun);
float rayCount = 7;
// Radial pattern
float rays = sin(angle * rayCount + sin(iTime * 0.25)) * 0.5 + 0.5;
rays = pow(rays, 3.0);
// Fade with distance
float falloff = 1.0 - smoothstep(0.0, 1.2, dist);
return rays * falloff * 0.15;
}
// Atmospheric shimmer / heat haze
float atmosphericShimmer(vec2 uv, float iTime) {
// Multiple layers of noise for complexity
float n1 = noise(uv * 5.0 + vec2(iTime * 0.1, iTime * 0.05));
float n2 = noise(uv * 8.0 - vec2(iTime * 0.08, iTime * 0.12));
float n3 = noise(uv * 12.0 + vec2(iTime * 0.15, -iTime * 0.1));
return (n1 * 0.5 + n2 * 0.3 + n3 * 0.2) * 0.15;
}
float sunCore(vec2 uv, vec2 sunPos, float iTime) {
vec2 toSun = uv - sunPos;
float dist = length(toSun);
// Main bright spot
float mainFlare = exp(-dist * 15.0) * 2.0;
// Secondary reflection spots along the line
float flares = 0.0;
for (int i = 1; i <= 3; i++) {
vec2 flarePos = sunPos + toSun * float(i) * 0.3;
float flareDist = length(uv - flarePos);
float flareSize = 0.02 + float(i) * 0.01;
flares += smoothstep(flareSize * 2.0, flareSize * 0.5, flareDist) * (0.3 / float(i));
}
// Pulsing effect
float pulse = sin(iTime) * 0.1 + 0.9;
return (mainFlare + flares) * pulse;
}
void main() {
vec2 uv = qt_TexCoord0;
float iTime = ubuf.time * 0.08;
// Sample the source
vec4 col = vec4(ubuf.bgColor.rgb, 1.0);
vec2 sunPos = vec2(0.85, 0.2);
// Aspect ratio correction
float aspect = ubuf.itemWidth / ubuf.itemHeight;
vec2 uvAspect = vec2(uv.x * aspect, uv.y);
vec2 sunPosAspect = vec2(sunPos.x * aspect, sunPos.y);
// Generate sunny effects
float rays = sunRays(uvAspect, sunPosAspect, iTime);
float shimmerEffect = atmosphericShimmer(uv, iTime);
float flare = sunCore(uvAspect, sunPosAspect, iTime);
// Warm sunny colors
vec3 sunColor = vec3(1.0, 0.95, 0.7); // Warm golden yellow
vec3 skyColor = vec3(0.9, 0.95, 1.0); // Light blue tint
vec3 shimmerColor = vec3(1.0, 0.98, 0.85); // Subtle warm shimmer
// Apply rounded corner mask
vec2 pixelPos = qt_TexCoord0 * vec2(ubuf.itemWidth, ubuf.itemHeight);
vec2 center = pixelPos - vec2(ubuf.itemWidth, ubuf.itemHeight) * 0.5;
vec2 halfSize = vec2(ubuf.itemWidth, ubuf.itemHeight) * 0.5;
float dist = roundedBoxSDF(center, halfSize, ubuf.cornerRadius);
float cornerMask = 1.0 - smoothstep(-1.0, 0.0, dist);
vec3 resultRGB = col.rgb;
float resultAlpha = col.a;
// Add sun rays
vec3 raysContribution = sunColor * rays;
float raysAlpha = rays * 0.4;
resultRGB = raysContribution + resultRGB * (1.0 - raysAlpha);
resultAlpha = raysAlpha + resultAlpha * (1.0 - raysAlpha);
// Add atmospheric shimmer
vec3 shimmerContribution = shimmerColor * shimmerEffect;
float shimmerAlpha = shimmerEffect * 0.1;
resultRGB = shimmerContribution + resultRGB * (1.0 - shimmerAlpha);
resultAlpha = shimmerAlpha + resultAlpha * (1.0 - shimmerAlpha);
// Add bright sun core
vec3 flareContribution = sunColor * flare;
float flareAlpha = clamp(flare, 0.0, 1.0) * 0.6;
resultRGB = flareContribution + resultRGB * (1.0 - flareAlpha);
resultAlpha = flareAlpha + resultAlpha * (1.0 - flareAlpha);
// Overall warm sunny tint
resultRGB = mix(resultRGB, resultRGB * vec3(1.08, 1.04, 0.98), 0.15);
// Apply global opacity and corner mask
float finalAlpha = resultAlpha * ubuf.qt_Opacity * cornerMask;
fragColor = vec4(resultRGB * (finalAlpha / max(resultAlpha, 0.001)), finalAlpha);
}
@@ -0,0 +1,149 @@
// ===== wp_disc.frag =====
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D source1; // Current wallpaper
layout(binding = 2) uniform sampler2D source2; // Next wallpaper
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float progress; // Transition progress (0.0 to 1.0)
float centerX; // X coordinate of disc center (0.0 to 1.0)
float centerY; // Y coordinate of disc center (0.0 to 1.0)
float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth)
float aspectRatio; // Width / Height of the screen
// Fill mode parameters
float fillMode; // 0=center, 1=crop, 2=fit, 3=stretch, 4=repeat
float imageWidth1; // Width of source1 image
float imageHeight1; // Height of source1 image
float imageWidth2; // Width of source2 image
float imageHeight2; // Height of source2 image
float screenWidth; // Screen width
float screenHeight; // Screen height
vec4 fillColor; // Fill color for empty areas (default: black)
// Solid color mode
float isSolid1; // 1.0 if source1 is solid color, 0.0 otherwise
float isSolid2; // 1.0 if source2 is solid color, 0.0 otherwise
vec4 solidColor1; // Solid color for source1
vec4 solidColor2; // Solid color for source2
} ubuf;
// Calculate UV coordinates based on fill mode
vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) {
float imageAspect = imgWidth / imgHeight;
float screenAspect = ubuf.screenWidth / ubuf.screenHeight;
vec2 transformedUV = uv;
if (ubuf.fillMode < 0.5) {
// Mode 0: no (center) - No resize, center image at original size
// Convert UV to pixel coordinates, offset, then back to UV in image space
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5;
vec2 imagePixel = screenPixel - imageOffset;
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
}
else if (ubuf.fillMode < 1.5) {
// Mode 1: crop (fill/cover) - Fill screen, crop excess (default)
float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize;
transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5;
}
else if (ubuf.fillMode < 2.5) {
// Mode 2: fit (contain) - Fit inside screen, maintain aspect ratio
float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5;
// Convert screen UV to pixel coordinates
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
// Adjust for offset and scale
vec2 imagePixel = (screenPixel - offset) / scale;
// Convert back to UV coordinates in image space
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
}
else if (ubuf.fillMode < 3.5) {
// Mode 3: stretch - Use original UV (stretches to fit)
// No transformation needed for stretch mode
}
else {
// Mode 4: repeat (tile) - Tile image at original size
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
transformedUV = screenPixel / vec2(imgWidth, imgHeight);
}
return transformedUV;
}
// Sample texture with fill mode and handle out-of-bounds
// If isSolid > 0.5, returns solidColor instead of sampling texture
vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight, float isSolid, vec4 solidColor) {
// Return solid color if in solid color mode
if (isSolid > 0.5) {
return solidColor;
}
vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight);
// Mode 4 (repeat): use fract() to tile the image
if (ubuf.fillMode > 3.5) {
return texture(tex, fract(transformedUV));
}
// Check if UV is out of bounds
if (transformedUV.x < 0.0 || transformedUV.x > 1.0 ||
transformedUV.y < 0.0 || transformedUV.y > 1.0) {
return ubuf.fillColor;
}
return texture(tex, transformedUV);
}
void main() {
vec2 uv = qt_TexCoord0;
// Sample textures with fill mode (handles solid colors)
vec4 color1 = sampleWithFillMode(source1, uv, ubuf.imageWidth1, ubuf.imageHeight1, ubuf.isSolid1, ubuf.solidColor1);
vec4 color2 = sampleWithFillMode(source2, uv, ubuf.imageWidth2, ubuf.imageHeight2, ubuf.isSolid2, ubuf.solidColor2);
// Map smoothness from 0.0-1.0 to 0.001-0.5 range
// Using a non-linear mapping for better control
float mappedSmoothness = mix(0.001, 0.5, ubuf.smoothness * ubuf.smoothness);
// Adjust UV coordinates to compensate for aspect ratio
// This makes distances circular instead of elliptical
vec2 adjustedUV = vec2(uv.x * ubuf.aspectRatio, uv.y);
vec2 adjustedCenter = vec2(ubuf.centerX * ubuf.aspectRatio, ubuf.centerY);
// Calculate distance in aspect-corrected space
float dist = distance(adjustedUV, adjustedCenter);
// Calculate the maximum possible distance (corner to corner)
// This ensures the disc can cover the entire screen
float maxDistX = max(ubuf.centerX * ubuf.aspectRatio,
(1.0 - ubuf.centerX) * ubuf.aspectRatio);
float maxDistY = max(ubuf.centerY, 1.0 - ubuf.centerY);
float maxDist = length(vec2(maxDistX, maxDistY));
// Scale progress to cover the maximum distance
// Add extra range for smoothness to ensure complete coverage
// Adjust smoothness for aspect ratio to maintain consistent visual appearance
float adjustedSmoothness = mappedSmoothness * max(1.0, ubuf.aspectRatio);
// Start the radius from -adjustedSmoothness, this ensures the disc is completely hidden when progress=0
float totalDistance = maxDist + 2.0 * adjustedSmoothness;
float radius = -adjustedSmoothness + ubuf.progress * totalDistance;
// Use smoothstep for a smooth edge transition
float factor = smoothstep(radius - adjustedSmoothness, radius + adjustedSmoothness, dist);
// Mix the textures (factor = 0 inside disc, 1 outside)
fragColor = mix(color2, color1, factor);
fragColor *= ubuf.qt_Opacity;
}
@@ -0,0 +1,112 @@
// ===== wp_fade.frag =====
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D source1;
layout(binding = 2) uniform sampler2D source2;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float progress;
// Fill mode parameters
float fillMode; // 0=center, 1=crop, 2=fit, 3=stretch, 4=repeat
float imageWidth1; // Width of source1 image
float imageHeight1; // Height of source1 image
float imageWidth2; // Width of source2 image
float imageHeight2; // Height of source2 image
float screenWidth; // Screen width
float screenHeight; // Screen height
vec4 fillColor; // Fill color for empty areas (default: black)
// Solid color mode
float isSolid1; // 1.0 if source1 is solid color, 0.0 otherwise
float isSolid2; // 1.0 if source2 is solid color, 0.0 otherwise
vec4 solidColor1; // Solid color for source1
vec4 solidColor2; // Solid color for source2
} ubuf;
// Calculate UV coordinates based on fill mode
vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) {
float imageAspect = imgWidth / imgHeight;
float screenAspect = ubuf.screenWidth / ubuf.screenHeight;
vec2 transformedUV = uv;
if (ubuf.fillMode < 0.5) {
// Mode 0: no (center) - No resize, center image at original size
// Convert UV to pixel coordinates, offset, then back to UV in image space
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5;
vec2 imagePixel = screenPixel - imageOffset;
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
}
else if (ubuf.fillMode < 1.5) {
// Mode 1: crop (fill/cover) - Fill screen, crop excess (default)
float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize;
transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5;
}
else if (ubuf.fillMode < 2.5) {
// Mode 2: fit (contain) - Fit inside screen, maintain aspect ratio
float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5;
// Convert screen UV to pixel coordinates
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
// Adjust for offset and scale
vec2 imagePixel = (screenPixel - offset) / scale;
// Convert back to UV coordinates in image space
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
}
else if (ubuf.fillMode < 3.5) {
// Mode 3: stretch - Use original UV (stretches to fit)
// No transformation needed for stretch mode
}
else {
// Mode 4: repeat (tile) - Tile image at original size
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
transformedUV = screenPixel / vec2(imgWidth, imgHeight);
}
return transformedUV;
}
// Sample texture with fill mode and handle out-of-bounds
// If isSolid > 0.5, returns solidColor instead of sampling texture
vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight, float isSolid, vec4 solidColor) {
// Return solid color if in solid color mode
if (isSolid > 0.5) {
return solidColor;
}
vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight);
// Mode 4 (repeat): use fract() to tile the image
if (ubuf.fillMode > 3.5) {
return texture(tex, fract(transformedUV));
}
// Check if UV is out of bounds
if (transformedUV.x < 0.0 || transformedUV.x > 1.0 ||
transformedUV.y < 0.0 || transformedUV.y > 1.0) {
return ubuf.fillColor;
}
return texture(tex, transformedUV);
}
void main() {
vec2 uv = qt_TexCoord0;
// Sample textures with fill mode (handles solid colors)
vec4 color1 = sampleWithFillMode(source1, uv, ubuf.imageWidth1, ubuf.imageHeight1, ubuf.isSolid1, ubuf.solidColor1);
vec4 color2 = sampleWithFillMode(source2, uv, ubuf.imageWidth2, ubuf.imageHeight2, ubuf.isSolid2, ubuf.solidColor2);
// Mix the two textures based on progress value
fragColor = mix(color1, color2, ubuf.progress) * ubuf.qt_Opacity;
}
@@ -0,0 +1,170 @@
// ===== wp_honeycomb.frag =====
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D source1;
layout(binding = 2) uniform sampler2D source2;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float progress;
float cellSize; // Size of hexagonal cells in UV space (default 0.04)
float centerX; // X coordinate of wave origin (0.0 to 1.0)
float centerY; // Y coordinate of wave origin (0.0 to 1.0)
float aspectRatio; // Width / Height of the screen
// Fill mode parameters
float fillMode; // 0=center, 1=crop, 2=fit, 3=stretch, 4=repeat
float imageWidth1; // Width of source1 image
float imageHeight1; // Height of source1 image
float imageWidth2; // Width of source2 image
float imageHeight2; // Height of source2 image
float screenWidth; // Screen width
float screenHeight; // Screen height
vec4 fillColor; // Fill color for empty areas (default: black)
// Solid color mode
float isSolid1; // 1.0 if source1 is solid color, 0.0 otherwise
float isSolid2; // 1.0 if source2 is solid color, 0.0 otherwise
vec4 solidColor1; // Solid color for source1
vec4 solidColor2; // Solid color for source2
} ubuf;
// Calculate UV coordinates based on fill mode
vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) {
float imageAspect = imgWidth / imgHeight;
float screenAspect = ubuf.screenWidth / ubuf.screenHeight;
vec2 transformedUV = uv;
if (ubuf.fillMode < 0.5) {
// Mode 0: no (center) - No resize, center image at original size
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5;
vec2 imagePixel = screenPixel - imageOffset;
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
}
else if (ubuf.fillMode < 1.5) {
// Mode 1: crop (fill/cover) - Fill screen, crop excess (default)
float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize;
transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5;
}
else if (ubuf.fillMode < 2.5) {
// Mode 2: fit (contain) - Fit inside screen, maintain aspect ratio
float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5;
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
vec2 imagePixel = (screenPixel - offset) / scale;
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
}
else if (ubuf.fillMode < 3.5) {
// Mode 3: stretch - Use original UV (stretches to fit)
}
else {
// Mode 4: repeat (tile) - Tile image at original size
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
transformedUV = screenPixel / vec2(imgWidth, imgHeight);
}
return transformedUV;
}
// Sample texture with fill mode and handle out-of-bounds
vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight, float isSolid, vec4 solidColor) {
if (isSolid > 0.5) {
return solidColor;
}
vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight);
if (ubuf.fillMode > 3.5) {
return texture(tex, fract(transformedUV));
}
if (transformedUV.x < 0.0 || transformedUV.x > 1.0 ||
transformedUV.y < 0.0 || transformedUV.y > 1.0) {
return ubuf.fillColor;
}
return texture(tex, transformedUV);
}
// Convert cartesian to axial hex coordinates and round to nearest hex center
vec2 hexRound(vec2 axial) {
// Convert axial (q,r) to cube (x,y,z) where x+y+z=0
float x = axial.x;
float z = axial.y;
float y = -x - z;
// Round each
float rx = round(x);
float ry = round(y);
float rz = round(z);
// Fix rounding errors by resetting the component with largest diff
float dx = abs(rx - x);
float dy = abs(ry - y);
float dz = abs(rz - z);
if (dx > dy && dx > dz) {
rx = -ry - rz;
} else if (dy > dz) {
ry = -rx - rz;
} else {
rz = -rx - ry;
}
return vec2(rx, rz);
}
void main() {
vec2 uv = qt_TexCoord0;
// Sample both textures at original UV
vec4 color1 = sampleWithFillMode(source1, uv, ubuf.imageWidth1, ubuf.imageHeight1, ubuf.isSolid1, ubuf.solidColor1);
vec4 color2 = sampleWithFillMode(source2, uv, ubuf.imageWidth2, ubuf.imageHeight2, ubuf.isSolid2, ubuf.solidColor2);
// Aspect-correct the UV for hex grid so cells appear as regular hexagons
vec2 aspectUV = vec2(uv.x * ubuf.aspectRatio, uv.y);
// Convert to axial hex coordinates
// Hex grid: q axis along x, r axis at 60 degrees
float size = max(ubuf.cellSize, 0.01);
float q = (aspectUV.x * (2.0 / 3.0)) / size;
float r = ((-aspectUV.x / 3.0) + (sqrt(3.0) / 3.0) * aspectUV.y) / size;
// Round to nearest hex center
vec2 hex = hexRound(vec2(q, r));
// Convert hex center back to aspect-corrected UV space
vec2 hexCenter;
hexCenter.x = size * (3.0 / 2.0) * hex.x;
hexCenter.y = size * (sqrt(3.0) * (hex.y + 0.5 * hex.x));
// Calculate distance from this cell's center to the wave origin (aspect-corrected)
vec2 origin = vec2(ubuf.centerX * ubuf.aspectRatio, ubuf.centerY);
float dist = distance(hexCenter, origin);
// Maximum distance from origin to any corner (for normalization)
float maxDistX = max(ubuf.centerX * ubuf.aspectRatio, (1.0 - ubuf.centerX) * ubuf.aspectRatio);
float maxDistY = max(ubuf.centerY, 1.0 - ubuf.centerY);
float maxDist = length(vec2(maxDistX, maxDistY));
// Wave expansion (same approach as disc shader):
// Start radius behind the origin so the smoothstep zone is fully off-screen at progress=0
float softEdge = 0.15 * maxDist;
float totalDistance = maxDist + 2.0 * softEdge;
float radius = -softEdge + ubuf.progress * totalDistance;
// factor = 0 inside the wave (revealed), 1 outside (not yet reached)
float factor = smoothstep(radius - softEdge, radius + softEdge, dist);
float cellProgress = 1.0 - factor;
fragColor = mix(color1, color2, cellProgress) * ubuf.qt_Opacity;
}
@@ -0,0 +1,119 @@
// ===== wp_pixelate.frag =====
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D source1;
layout(binding = 2) uniform sampler2D source2;
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float progress;
float maxBlockSize; // Maximum block size in pixels (default 64)
// Fill mode parameters
float fillMode; // 0=center, 1=crop, 2=fit, 3=stretch, 4=repeat
float imageWidth1; // Width of source1 image
float imageHeight1; // Height of source1 image
float imageWidth2; // Width of source2 image
float imageHeight2; // Height of source2 image
float screenWidth; // Screen width
float screenHeight; // Screen height
vec4 fillColor; // Fill color for empty areas (default: black)
// Solid color mode
float isSolid1; // 1.0 if source1 is solid color, 0.0 otherwise
float isSolid2; // 1.0 if source2 is solid color, 0.0 otherwise
vec4 solidColor1; // Solid color for source1
vec4 solidColor2; // Solid color for source2
} ubuf;
// Calculate UV coordinates based on fill mode
vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) {
float imageAspect = imgWidth / imgHeight;
float screenAspect = ubuf.screenWidth / ubuf.screenHeight;
vec2 transformedUV = uv;
if (ubuf.fillMode < 0.5) {
// Mode 0: no (center) - No resize, center image at original size
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5;
vec2 imagePixel = screenPixel - imageOffset;
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
}
else if (ubuf.fillMode < 1.5) {
// Mode 1: crop (fill/cover) - Fill screen, crop excess (default)
float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize;
transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5;
}
else if (ubuf.fillMode < 2.5) {
// Mode 2: fit (contain) - Fit inside screen, maintain aspect ratio
float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5;
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
vec2 imagePixel = (screenPixel - offset) / scale;
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
}
else if (ubuf.fillMode < 3.5) {
// Mode 3: stretch - Use original UV (stretches to fit)
}
else {
// Mode 4: repeat (tile) - Tile image at original size
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
transformedUV = screenPixel / vec2(imgWidth, imgHeight);
}
return transformedUV;
}
// Sample texture with fill mode and handle out-of-bounds
vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight, float isSolid, vec4 solidColor) {
if (isSolid > 0.5) {
return solidColor;
}
vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight);
if (ubuf.fillMode > 3.5) {
return texture(tex, fract(transformedUV));
}
if (transformedUV.x < 0.0 || transformedUV.x > 1.0 ||
transformedUV.y < 0.0 || transformedUV.y > 1.0) {
return ubuf.fillColor;
}
return texture(tex, transformedUV);
}
void main() {
vec2 uv = qt_TexCoord0;
// Triangle wave: pixelation peaks at progress=0.5
float pixelIntensity = 1.0 - abs(2.0 * ubuf.progress - 1.0);
// Compute the sampling UV — snap to block grid when pixelating, pass through otherwise
vec2 sampleUV = uv;
if (pixelIntensity > 0.001) {
float blockSize = ubuf.maxBlockSize * pixelIntensity;
vec2 screenSize = vec2(ubuf.screenWidth, ubuf.screenHeight);
vec2 pixelCoord = uv * screenSize;
vec2 snappedPixel = floor(pixelCoord / blockSize) * blockSize + blockSize * 0.5;
sampleUV = snappedPixel / screenSize;
}
// Sample both textures at the (possibly snapped) UV
vec4 color1 = sampleWithFillMode(source1, sampleUV, ubuf.imageWidth1, ubuf.imageHeight1, ubuf.isSolid1, ubuf.solidColor1);
vec4 color2 = sampleWithFillMode(source2, sampleUV, ubuf.imageWidth2, ubuf.imageHeight2, ubuf.isSolid2, ubuf.solidColor2);
// Crossfade concentrated around the peak pixelation (progress ~0.5)
float blend = smoothstep(0.4, 0.6, ubuf.progress);
fragColor = mix(color1, color2, blend) * ubuf.qt_Opacity;
}
@@ -0,0 +1,224 @@
// ===== wp_stripes.frag =====
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D source1; // Current wallpaper
layout(binding = 2) uniform sampler2D source2; // Next wallpaper
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float progress; // Transition progress (0.0 to 1.0)
float stripeCount; // Number of stripes (default 12.0)
float angle; // Angle of stripes in degrees (default 30.0)
float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth)
float aspectRatio; // Width / Height of the screen
// Fill mode parameters
float fillMode; // 0=center, 1=crop, 2=fit, 3=stretch, 4=repeat
float imageWidth1; // Width of source1 image
float imageHeight1; // Height of source1 image
float imageWidth2; // Width of source2 image
float imageHeight2; // Height of source2 image
float screenWidth; // Screen width
float screenHeight; // Screen height
vec4 fillColor; // Fill color for empty areas (default: black)
// Solid color mode
float isSolid1; // 1.0 if source1 is solid color, 0.0 otherwise
float isSolid2; // 1.0 if source2 is solid color, 0.0 otherwise
vec4 solidColor1; // Solid color for source1
vec4 solidColor2; // Solid color for source2
} ubuf;
// Calculate UV coordinates based on fill mode
vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) {
float imageAspect = imgWidth / imgHeight;
float screenAspect = ubuf.screenWidth / ubuf.screenHeight;
vec2 transformedUV = uv;
if (ubuf.fillMode < 0.5) {
// Mode 0: no (center) - No resize, center image at original size
// Convert UV to pixel coordinates, offset, then back to UV in image space
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5;
vec2 imagePixel = screenPixel - imageOffset;
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
}
else if (ubuf.fillMode < 1.5) {
// Mode 1: crop (fill/cover) - Fill screen, crop excess (default)
float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize;
transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5;
}
else if (ubuf.fillMode < 2.5) {
// Mode 2: fit (contain) - Fit inside screen, maintain aspect ratio
float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5;
// Convert screen UV to pixel coordinates
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
// Adjust for offset and scale
vec2 imagePixel = (screenPixel - offset) / scale;
// Convert back to UV coordinates in image space
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
}
else if (ubuf.fillMode < 3.5) {
// Mode 3: stretch - Use original UV (stretches to fit)
// No transformation needed for stretch mode
}
else {
// Mode 4: repeat (tile) - Tile image at original size
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
transformedUV = screenPixel / vec2(imgWidth, imgHeight);
}
return transformedUV;
}
// Sample texture with fill mode and handle out-of-bounds
// If isSolid > 0.5, returns solidColor instead of sampling texture
vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight, float isSolid, vec4 solidColor) {
// Return solid color if in solid color mode
if (isSolid > 0.5) {
return solidColor;
}
vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight);
// Mode 4 (repeat): use fract() to tile the image
if (ubuf.fillMode > 3.5) {
return texture(tex, fract(transformedUV));
}
// Check if UV is out of bounds
if (transformedUV.x < 0.0 || transformedUV.x > 1.0 ||
transformedUV.y < 0.0 || transformedUV.y > 1.0) {
return ubuf.fillColor;
}
return texture(tex, transformedUV);
}
void main() {
vec2 uv = qt_TexCoord0;
// Sample textures with fill mode (handles solid colors)
vec4 color1 = sampleWithFillMode(source1, uv, ubuf.imageWidth1, ubuf.imageHeight1, ubuf.isSolid1, ubuf.solidColor1);
vec4 color2 = sampleWithFillMode(source2, uv, ubuf.imageWidth2, ubuf.imageHeight2, ubuf.isSolid2, ubuf.solidColor2);
// Map smoothness from 0.0-1.0 to 0.001-0.3 range
// Using a non-linear mapping for better control at low values
float mappedSmoothness = mix(0.001, 0.3, ubuf.smoothness * ubuf.smoothness);
// Use values directly without forcing defaults
float stripes = (ubuf.stripeCount > 0.0) ? ubuf.stripeCount : 12.0;
float angleRad = radians(ubuf.angle);
float edgeSmooth = mappedSmoothness;
// Create a coordinate system for stripes based on angle
// At 0°: vertical stripes (divide by x)
// At 45°: diagonal stripes
// At 90°: horizontal stripes (divide by y)
// Transform coordinates based on angle
float cosA = cos(angleRad);
float sinA = sin(angleRad);
// Project the UV position onto the stripe direction
// This gives us the position along the stripe direction
float stripeCoord = uv.x * cosA + uv.y * sinA;
// Perpendicular coordinate (for edge movement)
float perpCoord = -uv.x * sinA + uv.y * cosA;
// Calculate the range of perpCoord based on angle
// This determines how far edges need to travel to fully cover the screen
float minPerp = min(min(0.0 * -sinA + 0.0 * cosA, 1.0 * -sinA + 0.0 * cosA),
min(0.0 * -sinA + 1.0 * cosA, 1.0 * -sinA + 1.0 * cosA));
float maxPerp = max(max(0.0 * -sinA + 0.0 * cosA, 1.0 * -sinA + 0.0 * cosA),
max(0.0 * -sinA + 1.0 * cosA, 1.0 * -sinA + 1.0 * cosA));
// Determine which stripe we're in
float stripePos = stripeCoord * stripes;
int stripeIndex = int(floor(stripePos));
// Determine if this is an odd or even stripe
bool isOddStripe = mod(float(stripeIndex), 2.0) != 0.0;
// Calculate the progress for this specific stripe with wave delay
// Use absolute stripe position for consistent delay across all stripes
float normalizedStripePos = clamp(stripePos / stripes, 0.0, 1.0);
// Increased delay and better distribution
float maxDelay = 0.1;
float stripeDelay = normalizedStripePos * maxDelay;
// Better progress mapping that uses the full 0.0-1.0 range
// Map progress so that:
// - First stripe starts at progress = 0.0
// - Last stripe finishes at progress = 1.0
float stripeProgress;
if (ubuf.progress <= stripeDelay) {
stripeProgress = 0.0;
} else if (ubuf.progress >= (stripeDelay + (1.0 - maxDelay))) {
stripeProgress = 1.0;
} else {
// Scale the progress within the active window for this stripe
float activeStart = stripeDelay;
float activeEnd = stripeDelay + (1.0 - maxDelay);
stripeProgress = (ubuf.progress - activeStart) / (activeEnd - activeStart);
}
// Use the perpendicular coordinate for edge comparison
float yPos = perpCoord;
// Calculate edge position for this stripe
// Use the actual perpendicular coordinate range for this angle
float perpRange = maxPerp - minPerp;
float margin = edgeSmooth; // Just enough buffer for the smoothstep zone to clear the edge
float edgePosition;
if (isOddStripe) {
// Odd stripes: edge moves from max to min
edgePosition = maxPerp + margin - stripeProgress * (perpRange + margin * 2.0);
} else {
// Even stripes: edge moves from min to max
edgePosition = minPerp - margin + stripeProgress * (perpRange + margin * 2.0);
}
// Determine which wallpaper to show based on rotated position
float mask;
if (isOddStripe) {
// Odd stripes reveal new wallpaper from bottom
mask = smoothstep(edgePosition - edgeSmooth, edgePosition + edgeSmooth, yPos);
} else {
// Even stripes reveal new wallpaper from top
mask = 1.0 - smoothstep(edgePosition - edgeSmooth, edgePosition + edgeSmooth, yPos);
}
// Mix the wallpapers
fragColor = mix(color1, color2, mask);
// Force exact values at start and end to prevent any bleed-through
if (ubuf.progress <= 0.0) {
fragColor = color1; // Only show old wallpaper at start
} else if (ubuf.progress >= 1.0) {
fragColor = color2; // Only show new wallpaper at end
} else {
// Add manga-style edge shadow only during transition
float edgeDist = abs(yPos - edgePosition);
float shadowStrength = 1.0 - smoothstep(0.0, edgeSmooth * 2.5, edgeDist);
shadowStrength *= 0.2 * (1.0 - abs(stripeProgress - 0.5) * 2.0);
fragColor.rgb *= (1.0 - shadowStrength);
// Add slight vignette during transition for dramatic effect
float vignette = 1.0 - ubuf.progress * 0.1 * (1.0 - abs(stripeProgress - 0.5) * 2.0);
fragColor.rgb *= vignette;
}
fragColor *= ubuf.qt_Opacity;
}
@@ -0,0 +1,151 @@
// ===== wp_wipe.frag =====
#version 450
layout(location = 0) in vec2 qt_TexCoord0;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D source1; // Current wallpaper
layout(binding = 2) uniform sampler2D source2; // Next wallpaper
layout(std140, binding = 0) uniform buf {
mat4 qt_Matrix;
float qt_Opacity;
float progress; // Transition progress (0.0 to 1.0)
float direction; // 0=left, 1=right, 2=up, 3=down
float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth)
// Fill mode parameters
float fillMode; // 0=center, 1=crop, 2=fit, 3=stretch, 4=repeat
float imageWidth1; // Width of source1 image
float imageHeight1; // Height of source1 image
float imageWidth2; // Width of source2 image
float imageHeight2; // Height of source2 image
float screenWidth; // Screen width
float screenHeight; // Screen height
vec4 fillColor; // Fill color for empty areas (default: black)
// Solid color mode
float isSolid1; // 1.0 if source1 is solid color, 0.0 otherwise
float isSolid2; // 1.0 if source2 is solid color, 0.0 otherwise
vec4 solidColor1; // Solid color for source1
vec4 solidColor2; // Solid color for source2
} ubuf;
// Calculate UV coordinates based on fill mode
vec2 calculateUV(vec2 uv, float imgWidth, float imgHeight) {
float imageAspect = imgWidth / imgHeight;
float screenAspect = ubuf.screenWidth / ubuf.screenHeight;
vec2 transformedUV = uv;
if (ubuf.fillMode < 0.5) {
// Mode 0: no (center) - No resize, center image at original size
// Convert UV to pixel coordinates, offset, then back to UV in image space
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
vec2 imageOffset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - vec2(imgWidth, imgHeight)) * 0.5;
vec2 imagePixel = screenPixel - imageOffset;
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
}
else if (ubuf.fillMode < 1.5) {
// Mode 1: crop (fill/cover) - Fill screen, crop excess (default)
float scale = max(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
vec2 offset = (scaledImageSize - vec2(ubuf.screenWidth, ubuf.screenHeight)) / scaledImageSize;
transformedUV = uv * (vec2(1.0) - offset) + offset * 0.5;
}
else if (ubuf.fillMode < 2.5) {
// Mode 2: fit (contain) - Fit inside screen, maintain aspect ratio
float scale = min(ubuf.screenWidth / imgWidth, ubuf.screenHeight / imgHeight);
vec2 scaledImageSize = vec2(imgWidth, imgHeight) * scale;
vec2 offset = (vec2(ubuf.screenWidth, ubuf.screenHeight) - scaledImageSize) * 0.5;
// Convert screen UV to pixel coordinates
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
// Adjust for offset and scale
vec2 imagePixel = (screenPixel - offset) / scale;
// Convert back to UV coordinates in image space
transformedUV = imagePixel / vec2(imgWidth, imgHeight);
}
else if (ubuf.fillMode < 3.5) {
// Mode 3: stretch - Use original UV (stretches to fit)
// No transformation needed for stretch mode
}
else {
// Mode 4: repeat (tile) - Tile image at original size
vec2 screenPixel = uv * vec2(ubuf.screenWidth, ubuf.screenHeight);
transformedUV = screenPixel / vec2(imgWidth, imgHeight);
}
return transformedUV;
}
// Sample texture with fill mode and handle out-of-bounds
// If isSolid > 0.5, returns solidColor instead of sampling texture
vec4 sampleWithFillMode(sampler2D tex, vec2 uv, float imgWidth, float imgHeight, float isSolid, vec4 solidColor) {
// Return solid color if in solid color mode
if (isSolid > 0.5) {
return solidColor;
}
vec2 transformedUV = calculateUV(uv, imgWidth, imgHeight);
// Mode 4 (repeat): use fract() to tile the image
if (ubuf.fillMode > 3.5) {
return texture(tex, fract(transformedUV));
}
// Check if UV is out of bounds
if (transformedUV.x < 0.0 || transformedUV.x > 1.0 ||
transformedUV.y < 0.0 || transformedUV.y > 1.0) {
return ubuf.fillColor;
}
return texture(tex, transformedUV);
}
void main() {
vec2 uv = qt_TexCoord0;
// Sample textures with fill mode (handles solid colors)
vec4 color1 = sampleWithFillMode(source1, uv, ubuf.imageWidth1, ubuf.imageHeight1, ubuf.isSolid1, ubuf.solidColor1);
vec4 color2 = sampleWithFillMode(source2, uv, ubuf.imageWidth2, ubuf.imageHeight2, ubuf.isSolid2, ubuf.solidColor2);
// Map smoothness from 0.0-1.0 to 0.001-0.5 range
// Using a non-linear mapping for better control
float mappedSmoothness = mix(0.001, 0.5, ubuf.smoothness * ubuf.smoothness);
float edge = 0.0;
float factor = 0.0;
// Extend the progress range to account for smoothness
// This ensures the transition completes fully at the edges
float extendedProgress = ubuf.progress * (1.0 + 2.0 * mappedSmoothness) - mappedSmoothness;
// Calculate edge position based on direction
// As progress goes from 0 to 1, we reveal source2 (new wallpaper)
if (ubuf.direction < 0.5) {
// Wipe from right to left (new image enters from right)
edge = 1.0 - extendedProgress;
factor = smoothstep(edge - mappedSmoothness, edge + mappedSmoothness, uv.x);
fragColor = mix(color1, color2, factor);
}
else if (ubuf.direction < 1.5) {
// Wipe from left to right (new image enters from left)
edge = extendedProgress;
factor = smoothstep(edge - mappedSmoothness, edge + mappedSmoothness, uv.x);
fragColor = mix(color2, color1, factor);
}
else if (ubuf.direction < 2.5) {
// Wipe from bottom to top (new image enters from bottom)
edge = 1.0 - extendedProgress;
factor = smoothstep(edge - mappedSmoothness, edge + mappedSmoothness, uv.y);
fragColor = mix(color1, color2, factor);
}
else {
// Wipe from top to bottom (new image enters from top)
edge = extendedProgress;
factor = smoothstep(edge - mappedSmoothness, edge + mappedSmoothness, uv.y);
fragColor = mix(color2, color1, factor);
}
fragColor *= ubuf.qt_Opacity;
}