diff --git a/shader_wallpaper/README.md b/shader_wallpaper/README.md new file mode 100644 index 0000000..fcc4e53 --- /dev/null +++ b/shader_wallpaper/README.md @@ -0,0 +1,37 @@ +# Shader Wallpaper + +This is an example of using Quickshell as a wallpaper, along with using shaders. + +To commpile the shader, do `qsb --qt6 -o ./Raining.frag.qsb ./Raining.frag` + +I got the shader from [Shadertoy](https://www.shadertoy.com/view/ltffzl). By default, it's not compatible with QSB, so I had to make the following changes: +- convert `void mainImage( out vec4 fragColor, in vec2 fragCoord )` to just `void main()` +- add the following to the top of the file (it was easier than renaming all the uses of the input variables): + +```glsl +#version 440 + +layout(location = 0) in vec2 fragCoord; // this is gotten from the `default.vert` +layout(location = 0) out vec4 fragColor; + +layout(binding = 1) uniform sampler2D source; // background image + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + float time; + vec2 resolution; + vec2 sourceResolution; +} ubuf; + +#define iTime ubuf.time +#define iResolution ubuf.resolution +#define iChannel0 source +// I don't want to deal with mouse input +#define iMouse vec3(0.0, 0.0, 0.0) +``` + +> [!NOTE] +> Not all shadertoy shaders are easily compatible with QSL. You might have to do extra modifications. + +![](./image.png) diff --git a/shader_wallpaper/Raining.frag b/shader_wallpaper/Raining.frag new file mode 100644 index 0000000..7416d88 --- /dev/null +++ b/shader_wallpaper/Raining.frag @@ -0,0 +1,239 @@ +// yoinked from https://www.shadertoy.com/view/ltffzl + +#version 440 + +layout(location = 0) in vec2 fragCoord; +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; + vec2 resolution; + vec2 sourceResolution; +} ubuf; + +#define iTime ubuf.time +#define iResolution ubuf.resolution +#define iChannel0 source +// I don't want to deal with mouse input +#define iMouse vec3(0.0, 0.0, 0.0) + +// Heartfelt - by Martijn Steinrucken aka BigWings - 2017 +// Email:countfrolic@gmail.com Twitter:@The_ArtOfCode +// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. + +// I revisited the rain effect I did for another shader. This one is better in multiple ways: +// 1. The glass gets foggy. +// 2. Drops cut trails in the fog on the glass. +// 3. The amount of rain is adjustable (with Mouse.y) + +// To have full control over the rain, uncomment the HAS_HEART define + +// A video of the effect can be found here: +// https://www.youtube.com/watch?v=uiF5Tlw22PI&feature=youtu.be + +// Music - Alone In The Dark - Vadim Kiselev +// https://soundcloud.com/ahmed-gado-1/sad-piano-alone-in-the-dark +// Rain sounds: +// https://soundcloud.com/elirtmusic/sleeping-sound-rain-and-thunder-1-hours + +#define S(a, b, t) smoothstep(a, b, t) +// #define CHEAP_NORMALS +// #define HAS_HEART +// #define HAS_LIGHTNING +#define USE_POST_PROCESSING + +vec3 N13(float p) { + // from DAVE HOSKINS + vec3 p3 = fract(vec3(p) * vec3(.1031,.11369,.13787)); + p3 += dot(p3, p3.yzx + 19.19); + return fract(vec3((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y, (p3.y+p3.z)*p3.x)); +} + +vec4 N14(float t) { + return fract(sin(t*vec4(123., 1024., 1456., 264.))*vec4(6547., 345., 8799., 1564.)); +} +float N(float t) { + return fract(sin(t*12345.564)*7658.76); +} + +float Saw(float b, float t) { + return S(0., b, t)*S(1., b, t); +} + + +vec2 DropLayer2(vec2 uv, float t) { + vec2 UV = uv; + + uv.y += t*0.75; + vec2 a = vec2(6., 1.); + vec2 grid = a*2.; + vec2 id = floor(uv*grid); + + float colShift = N(id.x); + uv.y += colShift; + + id = floor(uv*grid); + vec3 n = N13(id.x*35.2+id.y*2376.1); + vec2 st = fract(uv*grid)-vec2(.5, 0); + + float x = n.x-.5; + + float y = UV.y*20.; + float wiggle = sin(y+sin(y)); + x += wiggle*(.5-abs(x))*(n.z-.5); + x *= .7; + float ti = fract(t+n.z); + y = (Saw(.85, ti)-.5)*.9+.5; + vec2 p = vec2(x, y); + + float d = length((st-p)*a.yx); + + float mainDrop = S(.4, .0, d); + + float r = sqrt(S(1., y, st.y)); + float cd = abs(st.x-x); + float trail = S(.23*r, .15*r*r, cd); + float trailFront = S(-.02, .02, st.y-y); + trail *= trailFront*r*r; + + y = UV.y; + float trail2 = S(.2*r, .0, cd); + float droplets = max(0., (sin(y*(1.-y)*120.)-st.y))*trail2*trailFront*n.z; + y = fract(y*10.)+(st.y-.5); + float dd = length(st-vec2(x, y)); + droplets = S(.3, 0., dd); + float m = mainDrop+droplets*r*trailFront; + + //m += st.x>a.y*.45 || st.y>a.x*.165 ? 1.2 : 0.; + return vec2(m, trail); +} + +float StaticDrops(vec2 uv, float t) { + uv *= 40.; + + vec2 id = floor(uv); + uv = fract(uv)-.5; + vec3 n = N13(id.x*107.45+id.y*3543.654); + vec2 p = (n.xy-.5)*.7; + float d = length(uv-p); + + float fade = Saw(.025, fract(t+n.z)); + float c = S(.3, 0., d)*fract(n.z*10.)*fade; + return c; +} + +vec2 Drops(vec2 uv, float t, float l0, float l1, float l2) { + float s = StaticDrops(uv, t)*l0; + vec2 m1 = DropLayer2(uv, t)*l1; + vec2 m2 = DropLayer2(uv*1.85, t)*l2; + + float c = s+m1.x+m2.x; + c = S(.3, 1., c); + + return vec2(c, max(m1.y*l0, m2.y*l1)); +} + +void main() +{ + vec2 uv = (fragCoord.xy-.5*iResolution.xy) / iResolution.y; + vec2 UV = fragCoord.xy/iResolution.xy; + vec3 M = vec3(iMouse.xy/iResolution.xy, 0.0); + float T = iTime+M.x*2.; + + #ifdef HAS_HEART + T = mod(iTime, 102.); + T = mix(T, M.x*102., M.z>0.?1.:0.); + #endif + + + float t = T*.2; + + float rainAmount = iMouse.z>0. ? M.y : sin(T*.05)*.3+.7; + + float maxBlur = mix(3., 6., rainAmount); + float minBlur = 2.; + + float story = 0.; + float heart = 0.; + + #ifdef HAS_HEART + story = S(0., 70., T); + + t = min(1., T/70.); // remap drop time so it goes slower when it freezes + t = 1.-t; + t = (1.-t*t)*70.; + + float zoom= mix(.3, 1.2, story); // slowly zoom out + uv *=zoom; + minBlur = 4.+S(.5, 1., story)*3.; // more opaque glass towards the end + maxBlur = 6.+S(.5, 1., story)*1.5; + + vec2 hv = uv-vec2(.0, -.1); // build heart + hv.x *= .5; + float s = S(110., 70., T); // heart gets smaller and fades towards the end + hv.y-=sqrt(abs(hv.x))*.5*s; + heart = length(hv); + heart = S(.4*s, .2*s, heart)*s; + rainAmount = heart; // the rain is where the heart is + + maxBlur-=heart; // inside the heart slighly less foggy + uv *= 1.5; // zoom out a bit more + t *= .25; + #else + float zoom = 0;//-cos(T*.2); + uv *= .7+zoom*.3; + #endif + UV = (UV-.5)*(.9+zoom*.1)+.5; + + float staticDrops = S(-.5, 1., rainAmount)*2.; + float layer1 = S(.25, .75, rainAmount); + float layer2 = S(.0, .5, rainAmount); + + + vec2 c = Drops(uv, t, staticDrops, layer1, layer2); + #ifdef CHEAP_NORMALS + vec2 n = vec2(dFdx(c.x), dFdy(c.x));// cheap normals (3x cheaper, but 2 times shittier ;)) + #else + vec2 e = vec2(.001, 0.); + float cx = Drops(uv+e, t, staticDrops, layer1, layer2).x; + float cy = Drops(uv+e.yx, t, staticDrops, layer1, layer2).x; + vec2 n = vec2(cx-c.x, cy-c.x); // expensive normals + #endif + + + #ifdef HAS_HEART + n *= 1.-S(60., 85., T); + c.y *= 1.-S(80., 100., T)*.8; + #endif + + float focus = mix(maxBlur-c.y, minBlur, S(.1, .2, c.x)); + vec3 col = textureLod(iChannel0, (UV+n) * vec2(1.0, -1.0) + vec2(0.0, 1.0), focus).rgb; + + + #ifdef USE_POST_PROCESSING + t = (T+3.)*.5; // make time sync with first lightnoing + float colFade = sin(t*.2)*.5+.5+story; + col *= mix(vec3(1.), vec3(.8, .9, 1.3), colFade); // subtle color shift + float fade = S(0., 10., T); // fade in at the start + #ifdef HAS_LIGHTNING + float lightning = sin(t*sin(t*10.)); // lighting flicker + lightning *= pow(max(0., sin(t+sin(t))), 10.); // lightning flash + col *= 1.+lightning*fade*mix(1., .1, story*story); // composite lightning + #endif + col *= 1.-dot(UV-=.5, UV); // vignette + + #ifdef HAS_HEART + col = mix(pow(col, vec3(1.2)), col, heart); + fade *= S(102., 97., T); + #endif + + col *= fade; // composite start and end fade + #endif + + //col = vec3(heart); + fragColor = vec4(col, 1.); +} \ No newline at end of file diff --git a/shader_wallpaper/Raining.frag.qsb b/shader_wallpaper/Raining.frag.qsb new file mode 100644 index 0000000..d446579 Binary files /dev/null and b/shader_wallpaper/Raining.frag.qsb differ diff --git a/shader_wallpaper/default.vert b/shader_wallpaper/default.vert new file mode 100644 index 0000000..2c7f425 --- /dev/null +++ b/shader_wallpaper/default.vert @@ -0,0 +1,14 @@ +#version 440 +layout(location = 0) in vec4 qt_Vertex; +layout(location = 1) in vec2 qt_MultiTexCoord0; +layout(location = 0) out vec2 fragCoord; +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + float qt_Opacity; + vec2 resolution; +}; +void main() { + // make it easier to port from Shadertoy + fragCoord = qt_MultiTexCoord0 * resolution * vec2(1.0, -1.0) + resolution * vec2(0.0, 1.0); + gl_Position = qt_Matrix * qt_Vertex; +} \ No newline at end of file diff --git a/shader_wallpaper/default.vert.qsb b/shader_wallpaper/default.vert.qsb new file mode 100644 index 0000000..4464b6a Binary files /dev/null and b/shader_wallpaper/default.vert.qsb differ diff --git a/shader_wallpaper/hollow.jpg b/shader_wallpaper/hollow.jpg new file mode 100644 index 0000000..24ede3f Binary files /dev/null and b/shader_wallpaper/hollow.jpg differ diff --git a/shader_wallpaper/image.png b/shader_wallpaper/image.png new file mode 100644 index 0000000..2fa390a Binary files /dev/null and b/shader_wallpaper/image.png differ diff --git a/shader_wallpaper/shell.qml b/shader_wallpaper/shell.qml new file mode 100644 index 0000000..80b8e1d --- /dev/null +++ b/shader_wallpaper/shell.qml @@ -0,0 +1,46 @@ +import Quickshell +import QtQuick +import Quickshell.Wayland // for WlrLayershell & WlrLayer + +ShellRoot { + PanelWindow { + WlrLayershell.layer: WlrLayer.Background // use this to make it background + + anchors { + top: true + left: true + right: true + bottom: true + } + + color: "transparent" + + Rectangle { + anchors.fill: parent + color: "transparent" + Image { // if you want a gif, use AnimatedImage instead + id: img + source: "./hollow.jpg" + fillMode: Image.PreserveAspectCrop + anchors.fill: parent + visible: false + mipmap: true + } + ShaderEffect { + id: shader + anchors.fill: parent + property vector2d resolution: Qt.vector2d(width, height) + property real time: 0 + property variant source: img + FrameAnimation { + running: true + onTriggered: { + shader.time = this.elapsedTime; + } + } + vertexShader: "default.vert.qsb" + fragmentShader: "Raining.frag.qsb" + } + } + } +}