mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-05-27 08:12:28 +00:00
Weather: rain and snow shaders.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Services.Location
|
||||
@@ -13,14 +14,54 @@ NBox {
|
||||
property bool showLocation: true
|
||||
readonly property bool weatherReady: Settings.data.location.weatherEnabled && (LocationService.data.weather !== null)
|
||||
|
||||
// Test mode: set to "rain" or "snow"
|
||||
property string testWeatherEffect: ""
|
||||
|
||||
// Weather condition detection
|
||||
readonly property int currentWeatherCode: weatherReady ? LocationService.data.weather.current_weather.weathercode : 0
|
||||
readonly property bool isRaining: testWeatherEffect === "rain" || (testWeatherEffect === "" && currentWeatherCode >= 51 && currentWeatherCode <= 67)
|
||||
readonly property bool isSnowing: testWeatherEffect === "snow" || (testWeatherEffect === "" && ((currentWeatherCode >= 71 && currentWeatherCode <= 77) || (currentWeatherCode >= 85 && currentWeatherCode <= 86)))
|
||||
|
||||
// Animated time for shaders
|
||||
property real shaderTime: 0
|
||||
NumberAnimation on shaderTime {
|
||||
running: root.isRaining || root.isSnowing
|
||||
loops: Animation.Infinite
|
||||
from: 0
|
||||
to: 1000
|
||||
duration: 100000
|
||||
}
|
||||
|
||||
visible: Settings.data.location.weatherEnabled
|
||||
implicitHeight: Math.max(100 * Style.uiScaleRatio, content.implicitHeight + (Style.marginXL * 2))
|
||||
|
||||
// Weather effect layer (rain/snow)
|
||||
ShaderEffect {
|
||||
id: weatherEffect
|
||||
anchors.fill: parent
|
||||
// Snow fills the box, rain matches content margins
|
||||
anchors.margins: root.isSnowing ? root.border.width : Style.marginXL
|
||||
visible: root.isRaining || root.isSnowing
|
||||
|
||||
property var source: ShaderEffectSource {
|
||||
sourceItem: content
|
||||
hideSource: root.isRaining // Only hide for rain (distortion), show for snow
|
||||
}
|
||||
|
||||
property real time: root.shaderTime
|
||||
property real itemWidth: weatherEffect.width
|
||||
property real itemHeight: weatherEffect.height
|
||||
property color bgColor: root.color
|
||||
property real cornerRadius: root.isSnowing ? (root.radius - root.border.width) : 0
|
||||
|
||||
fragmentShader: root.isSnowing ?
|
||||
Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/weather_snow.frag.qsb") :
|
||||
Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/weather_rain.frag.qsb")
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: content
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginXL
|
||||
spacing: Style.marginM
|
||||
clip: true
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user