🔧 Streamline Aura Glow effect

This commit is contained in:
Simon Schneegans
2025-02-12 12:52:16 +01:00
parent 87b9f5eab8
commit 8b4b256231
4 changed files with 112 additions and 379 deletions
+57 -90
View File
@@ -10,6 +10,7 @@
//////////////////////////////////////////////////////////////////////////////////////////
// SPDX-FileCopyrightText: Justin Garza <JGarza9788@gmail.com>
// SPDX-FileCopyrightText: Simon Schneegans <code@simonschneegans.de>
// SPDX-License-Identifier: GPL-3.0-or-later
// The content from common.glsl is automatically prepended to each shader effect. This
@@ -30,121 +31,87 @@
// vec4 getInputColor(vec2 coords)
// void setOutputColor(vec4 outColor)
// The width of the fading effect is loaded from the settings.
uniform float uColorSpeed;
uniform bool uRandomColorOffset;
uniform float uColorOffset;
uniform float uColorSaturation;
uniform float uFadeOut;
uniform float uBlur;
uniform vec2 uSeed;
/*
this controls the end shape
-5.0 large Star
-3.0 Star
1.0 Dimond
2.0 Circle
3.0 Squircle
5.0 Square
*/
uniform float uEdgeShape;
// the size of the edge color
uniform float uEdgeSize;
// soft <--> hard
uniform float uEdgeHardness;
void main() {
// This gradually dissolves from [1..0] from the outside to the center. We
// switch the direction for opening and closing.
// We simply inverse the progress for opening windows.
float progress = uForOpening ? uProgress : 1.0 - uProgress;
// adjusting for Gnome
if (uPadding > 0.0) {
progress = remap(progress, 0.0, ((uSize.x - (uPadding * 2.0)) / uSize.x), 0.0, 1.0);
// Map UV to window content (without the shadow padding).
vec2 uv = (iTexCoord.st - 0.5) * uSize / (vec2(1.0) - vec2(uPadding * 2.0) / uSize);
uv = uv / uSize + 0.5;
// We want the shapes of the masks at the end of the animation to have a 1:1 aspect
// ratio. For this, we scale the UV coordinates based on the aspect ratio of the window
// and later blend between the original UV and the 1:1 UV based on the progress.
vec2 oneToOneUV = uv;
oneToOneUV -= 0.5;
if (uSize.x > uSize.y) {
oneToOneUV.y *= uSize.y / uSize.x;
} else {
oneToOneUV.x *= uSize.x / uSize.y;
}
// Get the color from the window texture.
vec4 oColor = getInputColor(iTexCoord.st);
oneToOneUV += 0.5;
// Calculate the aspect ratio of the render area
float aspect = uSize.x / uSize.y;
uv = mix(oneToOneUV, uv, progress);
// standard uv
vec2 uv = iTexCoord.st;
// This blends between a square-shaped gradient towards the window edges and a circular
// gradient.
float shape = mix(2.0, 100.0, pow(progress, 5.0));
float gradient = pow(abs(uv.x - 0.5) * 2.0, shape) + pow(abs(uv.y - 0.5) * 2.0, shape);
gradient += simplex2D((iTexCoord + uSeed)) * 0.5;
// tuv is for when progress is near 0
vec2 tuv = uv;
tuv -= 0.5; // Shift UV coordinates to center (from [-0.5 to 0.5])
tuv.x *= aspect; // Scale x-coordinate to match aspect ratio
tuv += 0.5; // Shift UV coordinates back (from [0 to 1])
// This mask is used for the color overlay.
float glowMask = (progress - gradient) / mix(uEdgeSize, 0.5, progress);
glowMask = 1.0 - clamp(glowMask - progress, 0.0, 1.0);
// mixing the UVs
uv = mix(tuv, uv, easeOutExpo(progress));
// Quickly fade out the glow mask at the end of the animation.
glowMask *= easeOutSine(min(1.0, (1.0 - progress) * 4.0));
// this controls the shape
// -1.0 would be a diamond-ish
// 0.0 would be a rounded diamond
// 1.0 would be a circle
// 2.0 will be sqircle
// 1000.0 will be very square
float p = mix(uEdgeShape, 1000.0, easeInExpo(progress));
// this will be used later to make a mask
float m =
mix(0.0, 1.0,
clamp(pow(abs(uv.x - 0.5) * 2.0, p) + pow(abs(uv.y - 0.5) * 2.0, p), 0.0, 1.0));
// this calculates the edge of the effect
float edge = abs(m - progress);
float e = mix(0.0, uEdgeSize, 1.0 - progress);
edge = remap(edge, 0.0, e, 0.0, 1.0);
edge = clamp(edge, 0.0, 1.0);
edge = 1.0 - edge;
// this is the mask
float mask = (m > progress) ? 0.0 : 1.0;
// we need two of these
float mask0 = mix(mask, mask + edge, 1.0 - uEdgeHardness);
float mask1 = mix(edge, edge * mask, uEdgeHardness);
// calculate color
vec3 color = cos(progress * uColorSpeed + uv.xyx + vec3(0, 2, 4)).xyz;
// coloroffset
float colorOffset = (uRandomColorOffset) ? hash12(uSeed) : uColorOffset;
color = offsetHue(color, colorOffset + 0.10);
// clamp and saturate
color = clamp(color * uColorSaturation, vec3(0.0), vec3(1.0));
// save this for later
float oColorAlpha = oColor.a;
// blur-ify
// Get the color from the window texture. We gradually blur the input color based on
// the progress of the animation. We also slightly scale the UV coordinates to make
// the window scale up during the animation.
vec2 windowUV = (iTexCoord.st - 0.5) * mix(1.1, 1.0, easeOutCubic(progress)) + 0.5;
vec4 windowColor;
if (uBlur > 0.0) {
// used for blur later ...
// calculate this before saturating it
float b = (color.r + color.g + color.b) / 3.0;
oColor = getBlurredInputColor(iTexCoord.st, b * uBlur * mask1, 7.0);
windowColor = getBlurredInputColor(windowUV, (1.0 - progress) * uBlur, 3.0);
} else {
windowColor = getInputColor(windowUV);
}
glowMask *= windowColor.a;
// apply masks and colors
oColor.a *= mask0;
vec3 glowColor = cos(progress * uColorSpeed + uv.xyx + vec3(0, 2, 4)).xyz;
float colorOffset = (uRandomColorOffset) ? hash12(uSeed) : uColorOffset;
glowColor = offsetHue(glowColor, colorOffset + 0.1);
glowColor = clamp(glowColor * uColorSaturation, vec3(0.0), vec3(1.0));
oColor += mask1 * vec4(color.rgb, 1.0);
oColor.a *= oColorAlpha;
windowColor.rgb += glowColor * glowMask;
// i want to fade out the last ~10% of the animation
float lastfade = remap(progress, 0.0, uFadeOut, 0.0, 1.0);
lastfade = clamp(lastfade, 0.0, 1.0);
lastfade = easeInSine(lastfade);
// To make this look better on light themes, we also add some color non-additively.
windowColor = alphaOver(windowColor, vec4(glowColor, glowMask * 0.2));
// apply the lastfade
oColor.a *= lastfade;
// A mask which will crop the window gradually into a circle.
float softCrop = 1.0 - smoothstep(progress - 0.5, progress + 0.5, gradient);
float hardCrop = 1.0 - smoothstep(progress - 0.05, progress + 0.05, gradient);
float cropMask = mix(softCrop, hardCrop, uEdgeHardness);
setOutputColor(oColor);
// Quickly fade in the alpha mask at the beginning of the animation.
cropMask *= easeInSine(min(1.0, progress * 2.0));
// Quickly fade out the alpha mask at the end of the animation.
cropMask = max(cropMask, 1.0 - easeOutSine(min(1.0, (1.0 - progress) * 4.0)));
windowColor.a *= cropMask;
setOutputColor(windowColor);
}
+5 -103
View File
@@ -66,15 +66,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
<child>
<object class="AdwActionRow" id="aura-glow-action-row">
<property name="title" translatable="yes">Color Offset</property>
<property name="subtitle" translatable="yes">Red</property>
<!-- <child>
<object class="GtkCheckButton" id="aura-glow-random-color">
<property name="label" translatable="yes">random</property>
<property name="valign">center</property>
<property name="active">true</property>
</object>
</child> -->
<property name="title" translatable="yes">Start Color Hue</property>
<child>
<object class="GtkScale" id="aura-glow-color-offset-scale">
<property name="valign">center</property>
@@ -117,8 +110,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
<property name="width-request">300</property>
<property name="adjustment">
<object class="GtkAdjustment" id="aura-glow-color-speed">
<property name="upper">10.0</property>
<property name="lower">-10.0</property>
<property name="upper">5.0</property>
<property name="lower">-5.0</property>
<property name="step-increment">0.25</property>
<property name="page-increment">1.0</property>
</object>
@@ -171,28 +164,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
</object>
</child>
<!-- <child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Adjust For Light Theme</property>
<property name="activatable-widget">aura-glow-adjust-light-theme</property>
<child>
<object class="GtkSwitch" id="aura-glow-adjust-light-theme">
<property name="valign">center</property>
</object>
</child>
<child>
<object class="GtkButton" id="reset-aura-glow-adjust-light-theme">
<property name="icon-name">edit-clear-symbolic</property>
<property name="valign">center</property>
<property name="tooltip-text" translatable="yes">Reset to Default Value</property>
<style>
<class name="flat" />
</style>
</object>
</child>
</object>
</child> -->
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Edge Size</property>
@@ -226,44 +197,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Edge Shape</property>
<property name="subtitle" translatable="yes">-1.0 (Star-ish) to 2.0 (Square)</property>
<child>
<object class="GtkScale">
<property name="valign">center</property>
<property name="draw-value">1</property>
<property name="digits">2</property>
<property name="value-pos">left</property>
<property name="width-request">300</property>
<property name="adjustment">
<object class="GtkAdjustment" id="aura-glow-edge-shape">
<property name="upper">2.0</property>
<property name="lower">-1.0</property>
<property name="step-increment">0.10</property>
<property name="page-increment">0.25</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkButton" id="reset-aura-glow-edge-shape">
<property name="icon-name">edit-clear-symbolic</property>
<property name="valign">center</property>
<property name="tooltip-text" translatable="yes">Reset to Default Value</property>
<style>
<class name="flat" />
</style>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Edge Hardness</property>
<property name="subtitle" translatable="yes">0.0 (soft/blurry) to 1.0 (sharp/hard)</property>
<child>
<object class="GtkScale">
<property name="valign">center</property>
@@ -297,7 +233,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Blur Amount</property>
<property name="subtitle" translatable="yes">0 is Off</property>
<child>
<object class="GtkScale">
<property name="valign">center</property>
@@ -307,7 +242,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<property name="width-request">300</property>
<property name="adjustment">
<object class="GtkAdjustment" id="aura-glow-blur">
<property name="upper">100.0</property>
<property name="upper">30.0</property>
<property name="lower">0.0</property>
<property name="step-increment">0.50</property>
<property name="page-increment">1.00</property>
@@ -328,39 +263,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Fade Out</property>
<child>
<object class="GtkScale">
<property name="valign">center</property>
<property name="draw-value">1</property>
<property name="digits">2</property>
<property name="value-pos">left</property>
<property name="width-request">300</property>
<property name="adjustment">
<object class="GtkAdjustment" id="aura-glow-fade-out">
<property name="upper">0.75</property>
<property name="lower">0.0</property>
<property name="step-increment">0.10</property>
<property name="page-increment">0.25</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkButton" id="reset-aura-glow-fade-out">
<property name="icon-name">edit-clear-symbolic</property>
<property name="valign">center</property>
<property name="tooltip-text" translatable="yes">Reset to Default Value</property>
<style>
<class name="flat" />
</style>
</object>
</child>
</object>
</child>
</object>
</interface>
@@ -968,7 +968,7 @@ SPDX-License-Identifier: CC0-1.0
</key>
<key name="aura-glow-animation-time" type="i">
<default>1500</default>
<default>750</default>
<summary>Aura Glow Animation Time</summary>
<description>The time the Aura Glow effect takes.</description>
</key>
@@ -986,53 +986,35 @@ SPDX-License-Identifier: CC0-1.0
</key>
<key name="aura-glow-color-speed" type="d">
<default>5.0</default>
<default>2.5</default>
<summary>Aura Glow Color Speed</summary>
<description>The Speed at which the color changes</description>
</key>
<key name="aura-glow-color-saturation" type="d">
<default>0.5</default>
<default>0.75</default>
<summary>Aura Glow Color Saturation</summary>
<description>Amount of color in the effect</description>
</key>
<!-- <key name="aura-glow-adjust-light-theme" type="b">
<default>false</default>
<summary>Aura Glow Adjust for light theme</summary>
<description>darken the window before adding color for light theme</description>
</key> -->
<key name="aura-glow-edge-size" type="d">
<default>1.0</default>
<default>0.75</default>
<summary>Aura Glow Size of the Color Edge</summary>
<description>Size of the Color Edge</description>
</key>
<key name="aura-glow-edge-shape" type="d">
<default>-1.0</default>
<summary>Aura Glow Edge Shape</summary>
<description>The Shape at the end of the animation</description>
</key>
<key name="aura-glow-edge-hardness" type="d">
<default>0.0</default>
<default>0.25</default>
<summary>Aura Glow Edge Hardness</summary>
<description>The Hardness of the Edge</description>
</key>
<key name="aura-glow-blur" type="d">
<default>50.0</default>
<default>15.0</default>
<summary>Aura Glow Blur</summary>
<description>Changes the Blue Amount</description>
</key>
<key name="aura-glow-fade-out" type="d">
<default>0.0</default>
<summary>Aura Glow Fade Out</summary>
<description>The Percent at which the window will fade out</description>
</key>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- RGBWarp Effect Options -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+12 -130
View File
@@ -10,6 +10,7 @@
//////////////////////////////////////////////////////////////////////////////////////////
// SPDX-FileCopyrightText: Justin Garza <JGarza9788@gmail.com>
// SPDX-FileCopyrightText: Simon Schneegans <code@simonschneegans.de>
// SPDX-License-Identifier: GPL-3.0-or-later
'use strict';
@@ -36,7 +37,6 @@ export default class Effect {
// GLSL file in resources/shaders/<nick>.glsl. The callback will be called for each
// newly created shader instance.
constructor() {
this.shaderFactory = new ShaderFactory(Effect.getNick(), (shader) => {
// Store uniform locations of newly created shaders.
@@ -44,57 +44,21 @@ export default class Effect {
shader._uRandomColorOffset = shader.get_uniform_location('uRandomColorOffset');
shader._uColorOffset = shader.get_uniform_location('uColorOffset');
shader._uColorSaturation = shader.get_uniform_location('uColorSaturation');
shader._uEdgeSize = shader.get_uniform_location('uEdgeSize');
shader._uEdgeShape = shader.get_uniform_location('uEdgeShape');
shader._uEdgeHardness = shader.get_uniform_location('uEdgeHardness');
shader._uBlur = shader.get_uniform_location('uBlur');
shader._uFadeOut = shader.get_uniform_location('uFadeOut');
shader._uSeed = shader.get_uniform_location('uSeed');
// Write all uniform values at the start of each animation.
shader.connect('begin-animation', (shader, settings) => {
shader.set_uniform_float(shader._uColorSpeed, 1, [
settings.get_double('aura-glow-color-speed'),
]);
shader.set_uniform_float(shader._uRandomColorOffset, 1,
[settings.get_boolean('aura-glow-random-color')]);
shader.set_uniform_float(shader._uColorOffset, 1, [
settings.get_double('aura-glow-color-offset'),
]);
shader.set_uniform_float(shader._uColorSaturation, 1, [
settings.get_double('aura-glow-color-saturation'),
]);
shader.set_uniform_float(shader._uEdgeSize, 1, [
settings.get_double('aura-glow-edge-size'),
]);
shader.set_uniform_float(shader._uEdgeShape, 1, [
settings.get_double('aura-glow-edge-shape'),
]);
shader.set_uniform_float(shader._uEdgeHardness, 1, [
settings.get_double('aura-glow-edge-hardness'),
]);
shader.set_uniform_float(shader._uBlur, 1, [
settings.get_double('aura-glow-blur'),
]);
shader.set_uniform_float(shader._uFadeOut, 1, [
settings.get_double('aura-glow-fade-out'),
]);
// shader.set_uniform_float(shader._uLightTheme, 1, [
// Effect.getCurrentThemeMode(),
// ]);
// this will be used with a has function to get a random number
// clang-format off
shader.set_uniform_float(shader._uColorSpeed, 1, [settings.get_double('aura-glow-color-speed')]);
shader.set_uniform_float(shader._uRandomColorOffset, 1, [settings.get_boolean('aura-glow-random-color')]);
shader.set_uniform_float(shader._uColorOffset, 1, [settings.get_double('aura-glow-color-offset')]);
shader.set_uniform_float(shader._uColorSaturation, 1, [settings.get_double('aura-glow-color-saturation')]);
shader.set_uniform_float(shader._uEdgeSize, 1, [settings.get_double('aura-glow-edge-size')]);
shader.set_uniform_float(shader._uEdgeHardness, 1, [settings.get_double('aura-glow-edge-hardness')]);
shader.set_uniform_float(shader._uBlur, 1, [settings.get_double('aura-glow-blur')]);
shader.set_uniform_float(shader._uSeed, 2, [Math.random(), Math.random()]);
// clang-format on
});
@@ -130,117 +94,35 @@ export default class Effect {
// This is called by the preferences dialog whenever a new effect profile is loaded. It
// binds all user interface elements to the respective settings keys of the profile.
static bindPreferences(dialog) {
// Empty for now... Code is added here later in the tutorial!
dialog.bindAdjustment('aura-glow-animation-time');
dialog.bindAdjustment('aura-glow-color-speed');
dialog.bindSwitch('aura-glow-random-color');
dialog.bindAdjustment('aura-glow-color-offset');
dialog.bindAdjustment('aura-glow-color-saturation');
dialog.bindAdjustment('aura-glow-edge-size');
dialog.bindAdjustment('aura-glow-edge-shape');
dialog.bindAdjustment('aura-glow-edge-hardness');
dialog.bindAdjustment('aura-glow-blur');
dialog.bindAdjustment('aura-glow-fade-out');
// enable and disable the one slider
function EnableDisablePref(dialog, state) {
function enableDisablePref(dialog, state) {
dialog.getBuilder()
.get_object('aura-glow-color-offset-scale')
.set_sensitive(!state);
}
// if we use a checkbox instead of a switch
/*
const randColor = dialog.getBuilder().get_object('aura-glow-random-color');
if (randColor) {
// Connect to the "toggled" signal to update preferences dynamically
randColor.connect('toggled', (widget) => {
const state = widget.get_active(); // Retrieve the state directly
EnableDisablePref(dialog, state); // Update sensitivity when the state changes
});
// Manually call the update function on startup, using the initial state of the
// switch
const initialState =
randColor.get_active(); // Get the initial state of the check button
EnableDisablePref(dialog, initialState);
} else {
// Log an error if the switch widget is not found in the UI
log('Error: \'aura-glow-random-color\' switch widget not found.');
}
*/
// if we use a switch instead of a checkbox
const switchWidget = dialog.getBuilder().get_object('aura-glow-random-color');
if (switchWidget) {
// Connect to the "state-set" signal to update preferences dynamically based on
// the switch state.
switchWidget.connect('state-set', (widget, state) => {
EnableDisablePref(dialog, state); // Update sensitivity when the state changes.
enableDisablePref(dialog, state); // Update sensitivity when the state changes.
});
// Manually call the update function on startup, using the initial state of the
// switch.
const initialState =
switchWidget.get_active(); // Get the current state of the switch.
EnableDisablePref(dialog, initialState);
} else {
// Log an error if the switch widget is not found in the UI.
log('Error: \'aura-glow-random-color\' switch widget not found.');
}
// Retrieve the necessary objects
const colorOffset = dialog.getBuilder().get_object('aura-glow-color-offset-scale');
const actionRow = dialog.getBuilder().get_object('aura-glow-action-row');
if (colorOffset == null) {
log('colorOffset is null');
}
// Define an array of color names based on the slider value
const colorNames = [
'Red', // 0.00
'Reddish-Orange', // 0.05
'Orange', // 0.10
'Yellow-Orange', // 0.15
'Yellow-Green', // 0.20
'Lime Green', // 0.25
'Green', // 0.30
'Greenish-Cyan', // 0.35
'Aqua', // 0.40
'Light Cyan', // 0.45
'Cyan', // 0.50
'Sky Cyan', // 0.55
'Sky Blue', // 0.60
'Light Blue', // 0.65
'Blue', // 0.70
'Indigo', // 0.75
'Purple', // 0.80
'Magenta', // 0.85
'Pinkish-Red', // 0.90
'Crimson', // 0.95
'Red' // 1.00 (wraps around)
];
// Function to update the subtitle based on the slider value
const updateSubtitle = () => {
const value = colorOffset.get_value(); // Get the current slider value
const index = Math.round(value * 20); // Map value [0.0, 1.0] to index [0, 10]
actionRow.set_subtitle(
colorNames[index]); // Update subtitle with the corresponding color
};
// Connect the value-changed signal to the updateSubtitle function
// Connect the value-changed signal to the updateSubtitle function
colorOffset.connect('value-changed', updateSubtitle);
// Initialize the subtitle on load
updateSubtitle();
enableDisablePref(dialog, initialState);
}
// ---------------------------------------------------------------- API for extension.js