diff --git a/Bin/shaders-compile.sh b/Bin/shaders-compile.sh new file mode 100755 index 0000000..b9f76a8 --- /dev/null +++ b/Bin/shaders-compile.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +# Directory containing the source shaders. +SOURCE_DIR="Shaders/frag/" + +# Directory where the compiled shaders will be saved. +DEST_DIR="Shaders/qsb/" + +# Check if the source directory exists. +if [ ! -d "$SOURCE_DIR" ]; then + echo "Source directory $SOURCE_DIR not found!" + exit 1 +fi + +# Create the destination directory if it doesn't exist. +mkdir -p "$DEST_DIR" + +# Loop through all files in the source directory ending with .frag +for shader in "$SOURCE_DIR"*.frag; do + # Check if a file was found (to handle the case of no .frag files). + if [ -f "$shader" ]; then + # Get the base name of the file (e.g., wp_fade). + shader_name=$(basename "$shader" .frag) + + # Construct the output path for the compiled shader. + output_path="$DEST_DIR$shader_name.frag.qsb" + + # Construct and run the qsb command. + qsb --qt6 -o "$output_path" "$shader" + + # Print a message to confirm compilation. + echo "Compiled $shader to $output_path" + fi +done + +echo "Shader compilation complete." \ No newline at end of file diff --git a/Commons/Settings.qml b/Commons/Settings.qml index df60967..59f78e0 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -170,6 +170,7 @@ Singleton { property int randomIntervalSec: 300 // 5 min property int transitionDuration: 1500 // 1500 ms property string transitionType: "random" + property real transitionEdgeSmoothness: 0.05 property list monitors: [] } diff --git a/Modules/Background/Background.qml b/Modules/Background/Background.qml index 780e17b..fdca504 100644 --- a/Modules/Background/Background.qml +++ b/Modules/Background/Background.qml @@ -12,82 +12,42 @@ Variants { required property ShellScreen modelData - active: Settings.isLoaded && WallpaperService.getWallpaper(modelData.name) + active: Settings.isLoaded sourceComponent: PanelWindow { id: root // Internal state management property bool firstWallpaper: true - property string transitionType: 'fade' - property bool transitioning: false - property real transitionProgress: 0.0 + property string transitionType: "fade" + property real transitionProgress: 0 + + readonly property real edgeSmoothness: Settings.data.wallpaper.transitionEdgeSmoothness readonly property var allTransitions: WallpaperService.allTransitions + readonly property bool transitioning: transitionAnimation.running // Wipe direction: 0=left, 1=right, 2=up, 3=down property real wipeDirection: 0 - property real wipeSmoothness: 0.05 // Disc property real discCenterX: 0.5 property real discCenterY: 0.5 - property real discSmoothness: 0.05 + + // Stripe + property real stripesCount: 16 + property real stripesAngle: 0 // External state management property string servicedWallpaper: WallpaperService.getWallpaper(modelData.name) + property string futureWallpaper: "" onServicedWallpaperChanged: { - if (servicedWallpaper && servicedWallpaper !== currentWallpaper.source) { - - // Set wallpaper immediately on startup - if (firstWallpaper) { - firstWallpaper = false - setWallpaperImmediate(servicedWallpaper) - return - } - - // Get the transitionType from the settings - transitionType = Settings.data.wallpaper.transitionType - - if (transitionType == 'random') { - var index = Math.floor(Math.random() * allTransitions.length) - transitionType = allTransitions[index] - } - - // Ensure the transition type really exists - if (transitionType !== "none" && !allTransitions.includes(transitionType)) { - transitionType = 'fade' - } - - //Logger.log("Background", "Using transition:", transitionType) - switch (transitionType) { - case "none": - setWallpaperImmediate(servicedWallpaper) - break - case "wipe_left": - wipeDirection = 0 - setWallpaperWithTransition(servicedWallpaper) - break - case "wipe_right": - wipeDirection = 1 - setWallpaperWithTransition(servicedWallpaper) - break - case "wipe_up": - wipeDirection = 2 - setWallpaperWithTransition(servicedWallpaper) - break - case "wipe_down": - wipeDirection = 3 - setWallpaperWithTransition(servicedWallpaper) - break - case "disc": - discCenterX = Math.random() - discCenterY = Math.random() - setWallpaperWithTransition(servicedWallpaper) - break - default: - setWallpaperWithTransition(servicedWallpaper) - break - } + // Set wallpaper immediately on startup + if (firstWallpaper) { + firstWallpaper = false + setWallpaperImmediate(servicedWallpaper) + } else { + futureWallpaper = servicedWallpaper + debounceTimer.restart() } } @@ -104,6 +64,16 @@ Variants { left: true } + Timer { + id: debounceTimer + interval: 333 + running: false + repeat: false + onTriggered: { + changeWallpaper() + } + } + Image { id: currentWallpaper anchors.fill: parent @@ -113,6 +83,8 @@ Variants { mipmap: false visible: false cache: false + // currentWallpaper should not be asynchronous to avoid flickering when swapping next to current. + asynchronous: false } Image { @@ -124,17 +96,18 @@ Variants { mipmap: false visible: false cache: false + asynchronous: true } // Fade or None transition shader ShaderEffect { id: fadeShader anchors.fill: parent - visible: transitionType === 'fade' || transitionType === 'none' + visible: transitionType === "fade" || transitionType === "none" property variant source1: currentWallpaper property variant source2: nextWallpaper - property real fade: transitionProgress + property real progress: root.transitionProgress fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_fade.frag.qsb") } @@ -142,13 +115,14 @@ Variants { ShaderEffect { id: wipeShader anchors.fill: parent - visible: transitionType.startsWith('wipe_') + visible: transitionType === "wipe" property variant source1: currentWallpaper property variant source2: nextWallpaper - property real progress: transitionProgress - property real direction: wipeDirection - property real smoothness: wipeSmoothness + property real progress: root.transitionProgress + property real smoothness: root.edgeSmoothness + property real direction: root.wipeDirection + fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_wipe.frag.qsb") } @@ -156,18 +130,36 @@ Variants { ShaderEffect { id: discShader anchors.fill: parent - visible: transitionType === 'disc' + visible: transitionType === "disc" property variant source1: currentWallpaper property variant source2: nextWallpaper - property real progress: transitionProgress - property real centerX: discCenterX - property real centerY: discCenterY - property real smoothness: discSmoothness - property real aspectRatio: width / height + property real progress: root.transitionProgress + property real smoothness: root.edgeSmoothness + property real aspectRatio: root.width / root.height + property real centerX: root.discCenterX + property real centerY: root.discCenterY + fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_disc.frag.qsb") } + // Diagonal stripes transition shader + ShaderEffect { + id: stripesShader + anchors.fill: parent + visible: transitionType === "stripes" + + property variant source1: currentWallpaper + property variant source2: nextWallpaper + property real progress: root.transitionProgress + property real smoothness: root.edgeSmoothness + property real aspectRatio: root.width / root.height + property real stripeCount: root.stripesCount + property real angle: root.stripesAngle + + fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_stripes.frag.qsb") + } + // Animation for the transition progress NumberAnimation { id: transitionAnimation @@ -175,29 +167,29 @@ Variants { property: "transitionProgress" from: 0.0 to: 1.0 - duration: Settings.data.wallpaper.transitionDuration ?? 1000 - easing.type: transitionType.startsWith('wipe_') ? Easing.InOutCubic : Easing.OutQuad + // The stripes shader feels faster visually, we make it a bit slower here. + duration: transitionType == "stripes" ? Settings.data.wallpaper.transitionDuration + * 1.6 : Settings.data.wallpaper.transitionDuration + easing.type: Easing.InOutCubic onFinished: { // Swap images after transition completes currentWallpaper.source = nextWallpaper.source nextWallpaper.source = "" transitionProgress = 0.0 - transitioning = false } } function startTransition() { if (!transitioning && nextWallpaper.source != currentWallpaper.source) { - transitioning = true transitionAnimation.start() } } function setWallpaperImmediate(source) { + transitionAnimation.stop() + transitionProgress = 0.0 currentWallpaper.source = source nextWallpaper.source = "" - transitionProgress = 0.0 - transitioning = false } function setWallpaperWithTransition(source) { @@ -206,16 +198,55 @@ Variants { if (transitioning) { // We are interrupting a transition transitionAnimation.stop() + transitionProgress = 0 currentWallpaper.source = nextWallpaper.source nextWallpaper.source = "" - transitionProgress = 0 - transitioning = false } nextWallpaper.source = source startTransition() } } + + // Main method that actually trigger the wallpaper change + function changeWallpaper() { + // Get the transitionType from the settings + transitionType = Settings.data.wallpaper.transitionType + + if (transitionType == "random") { + var index = Math.floor(Math.random() * allTransitions.length) + transitionType = allTransitions[index] + } + + // Ensure the transition type really exists + if (transitionType !== "none" && !allTransitions.includes(transitionType)) { + transitionType = "fade" + } + + //Logger.log("Background", "New wallpaper: ", futureWallpaper, "On:", modelData.name, "Transition:", transitionType) + switch (transitionType) { + case "none": + setWallpaperImmediate(futureWallpaper) + break + case "wipe": + wipeDirection = Math.random() * 4 + setWallpaperWithTransition(futureWallpaper) + break + case "disc": + discCenterX = Math.random() + discCenterY = Math.random() + setWallpaperWithTransition(futureWallpaper) + break + case "stripes": + stripesCount = Math.round(Math.random() * 20 + 4) + stripesAngle = Math.random() * 360 + setWallpaperWithTransition(futureWallpaper) + break + default: + setWallpaperWithTransition(futureWallpaper) + break + } + } } } } diff --git a/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml b/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml index b708f9d..fcd9e50 100644 --- a/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml +++ b/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml @@ -8,6 +8,7 @@ import qs.Widgets ColumnLayout { id: root + width: parent.width spacing: Style.marginL * scaling @@ -94,12 +95,10 @@ ColumnLayout { GridView { id: wallpaperGridView anchors.fill: parent - clip: true model: wallpapersList - boundsBehavior: Flickable.StopAtBounds - flickableDirection: Flickable.VerticalFlick interactive: false + clip: true property int columns: 5 property int itemSize: Math.floor((width - leftMargin - rightMargin - (4 * Style.marginS * scaling)) / columns) @@ -180,7 +179,8 @@ ColumnLayout { anchors.fill: parent acceptedButtons: Qt.LeftButton hoverEnabled: true - onClicked: { + // Use on pressed instead of clicked to better register clicks + onPressed: { if (Settings.data.wallpaper.setWallpaperOnAllMonitors) { WallpaperService.changeWallpaper(undefined, wallpaperPath) } else { diff --git a/Modules/SettingsPanel/Tabs/WallpaperTab.qml b/Modules/SettingsPanel/Tabs/WallpaperTab.qml index 546a4ca..8212b4a 100644 --- a/Modules/SettingsPanel/Tabs/WallpaperTab.qml +++ b/Modules/SettingsPanel/Tabs/WallpaperTab.qml @@ -88,47 +88,14 @@ ColumnLayout { // Random Wallpaper NToggle { label: "Random Wallpaper" - description: "Automatically select random wallpapers from the folder." + description: "Schedule random wallpaper changes at regular intervals." checked: Settings.data.wallpaper.randomEnabled onToggled: checked => Settings.data.wallpaper.randomEnabled = checked } - // Transition Type - NComboBox { - label: "Transition Type" - description: "Animation type when switching between wallpapers." - model: WallpaperService.transitionsModel - currentKey: Settings.data.wallpaper.transitionType - onSelected: key => Settings.data.wallpaper.transitionType = key - } - - // Transition Duration - ColumnLayout { - NLabel { - label: "Transition Duration" - description: "Duration of transition animations in seconds." - } - - RowLayout { - spacing: Style.marginL * scaling - NSlider { - Layout.fillWidth: true - from: 100 - to: 5000 - stepSize: 100 - value: Settings.data.wallpaper.transitionDuration - onMoved: Settings.data.wallpaper.transitionDuration = value - cutoutColor: Color.mSurface - } - NText { - text: (Settings.data.wallpaper.transitionDuration / 1000).toFixed(2) + "s" - Layout.alignment: Qt.AlignVCenter | Qt.AlignRight - } - } - } - - // Interval (slider + H:M inputs) + // Interval ColumnLayout { + visible: Settings.data.wallpaper.randomEnabled RowLayout { NLabel { label: "Wallpaper Interval" @@ -224,6 +191,64 @@ ColumnLayout { } } } + + // Transition Type + NComboBox { + label: "Transition Type" + description: "Animation type when switching between wallpapers." + model: WallpaperService.transitionsModel + currentKey: Settings.data.wallpaper.transitionType + onSelected: key => Settings.data.wallpaper.transitionType = key + } + + // Transition Duration + ColumnLayout { + NLabel { + label: "Transition Duration" + description: "Duration of transition animations in seconds." + } + + RowLayout { + spacing: Style.marginL * scaling + NSlider { + Layout.fillWidth: true + from: 100 + to: 5000 + stepSize: 100 + value: Settings.data.wallpaper.transitionDuration + onMoved: Settings.data.wallpaper.transitionDuration = value + cutoutColor: Color.mSurface + } + NText { + text: (Settings.data.wallpaper.transitionDuration / 1000).toFixed(2) + "s" + Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + } + } + } + + // Edge Smoothness + ColumnLayout { + NLabel { + label: "Transition Edge Smoothness" + description: "Duration of transition animations in seconds." + } + + RowLayout { + spacing: Style.marginL * scaling + NSlider { + Layout.fillWidth: true + from: 0.0 + to: 1.0 + value: Settings.data.wallpaper.transitionEdgeSmoothness + onMoved: Settings.data.wallpaper.transitionEdgeSmoothness = value + cutoutColor: Color.mSurface + } + NText { + text: Math.round(Settings.data.wallpaper.transitionEdgeSmoothness * 100) + "%" + Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + } + } + } } // Reusable component for interval preset chips diff --git a/Services/BluetoothService.qml b/Services/BluetoothService.qml index 368534c..2e88f5b 100644 --- a/Services/BluetoothService.qml +++ b/Services/BluetoothService.qml @@ -116,7 +116,7 @@ Singleton { } function getBattery(device) { - return `Battery: ${Math.round(device.battery * 100)}` + return `Battery: ${Math.round(device.battery * 100)}%` } function getSignalIcon(device) { diff --git a/Services/WallpaperService.qml b/Services/WallpaperService.qml index 472e317..b66ce2a 100644 --- a/Services/WallpaperService.qml +++ b/Services/WallpaperService.qml @@ -32,24 +32,16 @@ Singleton { name: "Disc" } ListElement { - key: "wipe_left" - name: "Wipe Left" + key: "stripes" + name: "Stripes" } ListElement { - key: "wipe_right" - name: "Wipe Right" - } - ListElement { - key: "wipe_up" - name: "Wipe Up" - } - ListElement { - key: "wipe_down" - name: "Wipe Down" + key: "wipe" + name: "Wipe" } } - // All transition keys but filter out "random" + // All transition keys but filter out "none" and "random" so we are left with the real transitions readonly property var allTransitions: Array.from({ "length": transitionsModel.count }, (_, i) => transitionsModel.get(i).key).filter( @@ -129,17 +121,17 @@ Singleton { // ------------------------------------------------------------------- function changeWallpaper(screenName, path) { if (screenName !== undefined) { - setWallpaper(screenName, path) + _setWallpaper(screenName, path) } else { // If no screenName specified change for all screens for (var i = 0; i < Quickshell.screens.length; i++) { - setWallpaper(Quickshell.screens[i].name, path) + _setWallpaper(Quickshell.screens[i].name, path) } } } // ------------------------------------------------------------------- - function setWallpaper(screenName, path) { + function _setWallpaper(screenName, path) { if (path === "" || path === undefined) { return } @@ -149,8 +141,7 @@ Singleton { return } - Logger.log("Wallpaper", "setWallpaper on", screenName, ": ", path) - + //Logger.log("Wallpaper", "setWallpaper on", screenName, ": ", path) var wallpaperChanged = false var monitor = getMonitorConfig(screenName) diff --git a/Shaders/frag/wp_disc.frag b/Shaders/frag/wp_disc.frag index 292ce59..4bde549 100644 --- a/Shaders/frag/wp_disc.frag +++ b/Shaders/frag/wp_disc.frag @@ -1,3 +1,4 @@ +// ===== wp_disc.frag ===== #version 450 layout(location = 0) in vec2 qt_TexCoord0; @@ -12,7 +13,7 @@ layout(std140, binding = 0) uniform buf { 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.01 to 0.5, default 0.05) + float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth) float aspectRatio; // Width / Height of the screen } ubuf; @@ -21,6 +22,10 @@ void main() { vec4 color1 = texture(source1, uv); // Current (old) wallpaper vec4 color2 = texture(source2, uv); // Next (new) wallpaper + // 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); @@ -39,7 +44,7 @@ void main() { // 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 = ubuf.smoothness * max(1.0, ubuf.aspectRatio); + float adjustedSmoothness = mappedSmoothness * max(1.0, ubuf.aspectRatio); float radius = ubuf.progress * (maxDist + adjustedSmoothness); // Use smoothstep for a smooth edge transition @@ -49,4 +54,4 @@ void main() { fragColor = mix(color2, color1, factor); fragColor *= ubuf.qt_Opacity; -} \ No newline at end of file +} diff --git a/Shaders/frag/wp_fade.frag b/Shaders/frag/wp_fade.frag index 10c3a63..24917a5 100644 --- a/Shaders/frag/wp_fade.frag +++ b/Shaders/frag/wp_fade.frag @@ -7,16 +7,13 @@ layout(binding = 2) uniform sampler2D source2; layout(std140, binding = 0) uniform buf { mat4 qt_Matrix; float qt_Opacity; - float fade; + float progress; }; void main() { vec4 color1 = texture(source1, qt_TexCoord0); vec4 color2 = texture(source2, qt_TexCoord0); - // Smooth cross-fade using smoothstep for better visual quality - float smoothFade = smoothstep(0.0, 1.0, fade); - - // Mix the two textures based on fade value - fragColor = mix(color1, color2, smoothFade) * qt_Opacity; + // Mix the two textures based on progress value + fragColor = mix(color1, color2, progress) * qt_Opacity; } \ No newline at end of file diff --git a/Shaders/frag/wp_stripes.frag b/Shaders/frag/wp_stripes.frag new file mode 100644 index 0000000..34df964 --- /dev/null +++ b/Shaders/frag/wp_stripes.frag @@ -0,0 +1,138 @@ +// ===== 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 +} ubuf; + +void main() { + vec2 uv = qt_TexCoord0; + vec4 color1 = texture(source1, uv); // Current (old) wallpaper + vec4 color2 = texture(source2, uv); // Next (new) wallpaper + + // 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 = (stripeIndex % 2) == 1; + + // 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 gentler easing curve + stripeProgress = stripeProgress * stripeProgress * (3.0 - 2.0 * stripeProgress); // Smootherstep instead of smoothstep + + // 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 * 2.0; // Simplified margin calculation + 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; +} \ No newline at end of file diff --git a/Shaders/frag/wp_wipe.frag b/Shaders/frag/wp_wipe.frag index f6db80e..10b948a 100644 --- a/Shaders/frag/wp_wipe.frag +++ b/Shaders/frag/wp_wipe.frag @@ -1,3 +1,5 @@ + +// ===== wp_wipe.frag ===== #version 450 layout(location = 0) in vec2 qt_TexCoord0; @@ -11,7 +13,7 @@ layout(std140, binding = 0) uniform buf { 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.01 to 0.5, default 0.05) + float smoothness; // Edge smoothness (0.0 to 1.0, 0=sharp, 1=very smooth) } ubuf; void main() { @@ -19,37 +21,41 @@ void main() { vec4 color1 = texture(source1, uv); // Current (old) wallpaper vec4 color2 = texture(source2, uv); // Next (new) wallpaper + // 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 * ubuf.smoothness) - ubuf.smoothness; + 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 - ubuf.smoothness, edge + ubuf.smoothness, uv.x); + 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 - ubuf.smoothness, edge + ubuf.smoothness, uv.x); + 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 - ubuf.smoothness, edge + ubuf.smoothness, uv.y); + 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 - ubuf.smoothness, edge + ubuf.smoothness, uv.y); + factor = smoothstep(edge - mappedSmoothness, edge + mappedSmoothness, uv.y); fragColor = mix(color2, color1, factor); } diff --git a/Shaders/qsb/wp_disc.frag.qsb b/Shaders/qsb/wp_disc.frag.qsb index 485186e..9c66bfb 100644 Binary files a/Shaders/qsb/wp_disc.frag.qsb and b/Shaders/qsb/wp_disc.frag.qsb differ diff --git a/Shaders/qsb/wp_fade.frag.qsb b/Shaders/qsb/wp_fade.frag.qsb index e06417c..89641d8 100644 Binary files a/Shaders/qsb/wp_fade.frag.qsb and b/Shaders/qsb/wp_fade.frag.qsb differ diff --git a/Shaders/qsb/wp_stripes.frag.qsb b/Shaders/qsb/wp_stripes.frag.qsb new file mode 100644 index 0000000..4466bce Binary files /dev/null and b/Shaders/qsb/wp_stripes.frag.qsb differ diff --git a/Shaders/qsb/wp_wipe.frag.qsb b/Shaders/qsb/wp_wipe.frag.qsb index e1977ce..478c27d 100644 Binary files a/Shaders/qsb/wp_wipe.frag.qsb and b/Shaders/qsb/wp_wipe.frag.qsb differ