mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
Add weather effects for clear day and night
This commit is contained in:
@@ -15,15 +15,18 @@ NBox {
|
||||
property bool showEffects: Settings.data.location.weatherShowEffects
|
||||
readonly property bool weatherReady: Settings.data.location.weatherEnabled && (LocationService.data.weather !== null)
|
||||
|
||||
// Test mode: set to "rain", "snow", "cloud" or "fog"
|
||||
// Test mode: set to "clear_day", "clear_night", "rain", "snow", "cloud" or "fog"
|
||||
property string testEffects: ""
|
||||
|
||||
// Weather condition detection
|
||||
readonly property int currentWeatherCode: weatherReady ? LocationService.data.weather.current_weather.weathercode : 0
|
||||
readonly property bool isDayTime: weatherReady ? LocationService.data.weather.current_weather.is_day : true
|
||||
readonly property bool isRaining: testEffects === "rain" || (testEffects === "" && ((currentWeatherCode >= 51 && currentWeatherCode <= 67) || (currentWeatherCode >= 80 && currentWeatherCode <= 82)))
|
||||
readonly property bool isSnowing: testEffects === "snow" || (testEffects === "" && ((currentWeatherCode >= 71 && currentWeatherCode <= 77) || (currentWeatherCode >= 85 && currentWeatherCode <= 86)))
|
||||
readonly property bool isCloudy: testEffects === "cloud" || (testEffects === "" && (currentWeatherCode === 3))
|
||||
readonly property bool isFoggy: testEffects === "fog" || (testEffects === "" && (currentWeatherCode === 45 || currentWeatherCode === 48))
|
||||
readonly property bool isFoggy: testEffects === "fog" || (testEffects === "" && (currentWeatherCode >= 40 && currentWeatherCode <= 49))
|
||||
readonly property bool isClearDay: testEffects === "clear_day" || (testEffects === "" && (currentWeatherCode === 0 && isDayTime))
|
||||
readonly property bool isClearNight: testEffects === "clear_night" || (testEffects === "" && (currentWeatherCode === 0 && !isDayTime))
|
||||
|
||||
visible: Settings.data.location.weatherEnabled
|
||||
implicitHeight: Math.max(100 * Style.uiScaleRatio, content.implicitHeight + (Style.marginXL * 2))
|
||||
@@ -32,7 +35,7 @@ NBox {
|
||||
Loader {
|
||||
id: weatherEffectLoader
|
||||
anchors.fill: parent
|
||||
active: root.showEffects && (root.isRaining || root.isSnowing || root.isCloudy || root.isFoggy)
|
||||
active: root.showEffects && (root.isRaining || root.isSnowing || root.isCloudy || root.isFoggy || root.isClearDay || root.isClearNight)
|
||||
|
||||
sourceComponent: Item {
|
||||
anchors.fill: parent
|
||||
@@ -64,7 +67,14 @@ NBox {
|
||||
property real cornerRadius: root.isRaining ? 0 : (root.radius - root.border.width)
|
||||
property real alternative: root.isFoggy
|
||||
|
||||
fragmentShader: root.isSnowing ? Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/weather_snow.frag.qsb") : root.isRaining ? Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/weather_rain.frag.qsb") : root.isCloudy || root.isFoggy ? Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/weather_cloud.frag.qsb") : ""
|
||||
fragmentShader: root.isSnowing ? Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/weather_snow.frag.qsb") : root.isRaining ? Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/weather_rain.frag.qsb") : root.isCloudy || root.isFoggy ? Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/weather_cloud.frag.qsb") : root.isClearDay ? Qt.resolvedUrl(
|
||||
Quickshell.shellDir
|
||||
+ "/Shaders/qsb/weather_sun.frag.qsb") :
|
||||
root.isClearNight
|
||||
? Qt.resolvedUrl(
|
||||
Quickshell.shellDir
|
||||
+ "/Shaders/qsb/weather_stars.frag.qsb") :
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user