🚚 Move fire shader to its own file

This commit is contained in:
Simon Schneegans
2021-12-22 05:40:42 +01:00
parent e32b11a010
commit 3149371a04
5 changed files with 212 additions and 141 deletions
+44
View File
@@ -0,0 +1,44 @@
//////////////////////////////////////////////////////////////////////////////////////////
// ) ( //
// ( /( ( ( ) ( ( ( ( )\ ) ( ( //
// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( //
// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ //
// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) //
// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< //
// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ //
// |__/ //
// Copyright (c) 2021 Simon Schneegans //
// Released under the GPLv3 or later. See LICENSE file for details. //
//////////////////////////////////////////////////////////////////////////////////////////
'use strict';
const {Gtk} = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = imports.misc.extensionUtils.getCurrentExtension();
// This method can be used to write a message to GNOME Shell's log. This is enhances
// the standard log() functionality by prepending the extension's name and the location
// where the message was logged. As the extensions name is part of the location, you
// can more effectively watch the log output of GNOME Shell:
// journalctl -f -o cat | grep -E 'particle-effects|'
// This method is based on a similar script from the Fly-Pie GNOME Shell extension which
// os published under the MIT License (https://github.com/Schneegans/Fly-Pie).
function debug(message) {
const stack = new Error().stack.split('\n');
// Remove debug() function call from stack.
stack.shift();
// Find the index of the extension directory (e.g. particles@schneegans.github.com) in
// the stack entry. We do not want to print the entire absolute file path.
const extensionRoot = stack[0].indexOf(Me.metadata.uuid);
log('[' + stack[0].slice(extensionRoot) + '] ' + message);
}
// This method simply returns true if we are currently using GTK4.
function gtk4() {
return Gtk.get_major_version() == 4;
}
+101
View File
@@ -0,0 +1,101 @@
//////////////////////////////////////////////////////////////////////////////////////////
// ) ( //
// ( /( ( ( ) ( ( ( ( )\ ) ( ( //
// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( //
// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ //
// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) //
// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< //
// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ //
// |__/ //
// Copyright (c) 2021 Simon Schneegans //
// Released under the GPLv3 or later. See LICENSE file for details. //
//////////////////////////////////////////////////////////////////////////////////////////
'use strict';
const {Clutter, GObject} = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = imports.misc.extensionUtils.getCurrentExtension();
const shaderSnippets = Me.imports.src.extension.shaderSnippets;
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
var FireShader = GObject.registerClass({Properties: {}, Signals: {}},
class FireShader extends Clutter.ShaderEffect {
_init(settings) {
super._init({shader_type: Clutter.ShaderType.FRAGMENT_SHADER});
// Load the gradient values from the settings. We directly inject the values in the
// GLSL code below. The shader is compiled once for each window-closing anyways. In
// the future, we my want to prevent this frequent recompilations of shaders,
// though.
const gradient = [];
for (let i = 1; i <= 5; i++) {
const color = Clutter.Color.from_string(settings.get_string('fire-color-' + i))[1];
gradient.push(`vec4(${color.red / 255}, ${color.green / 255}, ${
color.blue / 255}, ${color.alpha / 255})`);
}
this.set_shader_source(`
${shaderSnippets.standardUniforms()}
${shaderSnippets.noise()}
${shaderSnippets.effectMask()}
// These may be configurable in the future.
const float EDGE_FADE = 70;
const float FADE_WIDTH = 0.1;
const float HIDE_TIME = 0.2;
const vec2 FIRE_SCALE = vec2(400, 600) * ${settings.get_double('flame-scale')};
const float FIRE_SPEED = ${settings.get_double('flame-movement-speed')};
vec4 getFireColor(float v) {
const float steps[5] = float[](0.0, 0.2, 0.35, 0.5, 0.8);
const vec4 colors[5] = vec4[](
${gradient[0]},
${gradient[1]},
${gradient[2]},
${gradient[3]},
${gradient[4]}
);
if (v < steps[0]) {
return colors[0];
}
for (int i=0; i<4; ++i) {
if (v <= steps[i+1]) {
return mix(colors[i], colors[i+1], vec4(v -
steps[i])/(steps[i+1]-steps[i]));
}
}
return colors[4];
}
void main(void) {
// Get a noise value.
vec2 uv = cogl_tex_coord_in[0].st * vec2(uSizeX, uSizeY) / FIRE_SCALE;
uv.y += uTime * FIRE_SPEED;
float noise = perlinNoise(uv, 10.0, 5, 0.5);
// Modulate noise by mask.
vec2 effectMask = effectMask(HIDE_TIME, FADE_WIDTH, EDGE_FADE);
noise *= effectMask.y;
// Map noise value to color.
vec4 fire = getFireColor(noise);
fire.rgb *= fire.a;
// Get window texture.
cogl_color_out = texture2D(uTexture, cogl_tex_coord_in[0].st) * effectMask.x;
// Add fire.
cogl_color_out += fire;
}
`);
};
});
+102
View File
@@ -0,0 +1,102 @@
//////////////////////////////////////////////////////////////////////////////////////////
// ) ( //
// ( /( ( ( ) ( ( ( ( )\ ) ( ( //
// )\()) ))\ )( ( ( )\ ) )\))( )\ ( (()/( ( )\))( ( //
// ((_)\ /((_|()\ )\ ) )\ '(()/( ((_)()((_) )\ ) ((_)))\((_)()\ )\ //
// | |(_|_))( ((_)_(_/( _((_)) )(_)) _(()((_|_)_(_/( _| |((_)(()((_|(_) //
// | '_ \ || | '_| ' \)) | ' \()| || | \ V V / | ' \)) _` / _ \ V V (_-< //
// |_.__/\_,_|_| |_||_| |_|_|_| \_, | \_/\_/|_|_||_|\__,_\___/\_/\_//__/ //
// |__/ //
// Copyright (c) 2021 Simon Schneegans //
// Released under the GPLv3 or later. See LICENSE file for details. //
//////////////////////////////////////////////////////////////////////////////////////////
'use strict';
function standardUniforms() {
return `
uniform sampler2D uTexture;
uniform float uProgress;
uniform float uTime;
uniform int uSizeX;
uniform int uSizeY;
`;
}
// The noise implementation is from https://www.shadertoy.com/view/3tcBzH
// (CC-BY-NC-SA).
function noise() {
return `
float rand(vec2 co) {
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
float hermite(float t) {
return t * t * (3.0 - 2.0 * t);
}
float noiseImpl(vec2 co, float frequency) {
vec2 v = vec2(co.x * frequency, co.y * frequency);
float ix1 = floor(v.x);
float iy1 = floor(v.y);
float ix2 = floor(v.x + 1.0);
float iy2 = floor(v.y + 1.0);
float fx = hermite(fract(v.x));
float fy = hermite(fract(v.y));
float fade1 = mix(rand(vec2(ix1, iy1)), rand(vec2(ix2, iy1)), fx);
float fade2 = mix(rand(vec2(ix1, iy2)), rand(vec2(ix2, iy2)), fx);
return mix(fade1, fade2, fy);
}
float perlinNoise(vec2 co, float freq, int steps, float persistence) {
float value = 0.0;
float ampl = 1.0;
float sum = 0.0;
for(int i=0 ; i<steps ; i++) {
sum += ampl;
value += noiseImpl(co, freq) * ampl;
freq *= 2.0;
ampl *= persistence;
}
return value / sum;
}
`;
}
function effectMask() {
return `
vec2 effectMask(float hideTime, float fadeWidth, float edgeFadeWidth) {
float burnProgress = clamp(uProgress/hideTime, 0, 1);
float afterBurnProgress = clamp((uProgress-hideTime)/(1-hideTime), 0, 1);
// Gradient from top to bottom.
float t = cogl_tex_coord_in[0].t * (1 - fadeWidth);
// Visible part of the window. Gradually dissolves towards the bottom.
float windowMask = 1 - clamp((burnProgress - t) / fadeWidth, 0, 1);
// Gradient from top burning window.
float effectMask = clamp(t*(1-windowMask)/burnProgress, 0, 1);
// Fade-out when the window burned down.
if (uProgress > hideTime) {
effectMask *= mix(1, 1-t, afterBurnProgress) * pow(1-afterBurnProgress, 2);
}
// Fade at window borders.
vec2 pos = cogl_tex_coord_in[0].st * vec2(uSizeX, uSizeY);
effectMask *= clamp(pos.x / edgeFadeWidth, 0, 1);
effectMask *= clamp(pos.y / edgeFadeWidth, 0, 1);
effectMask *= clamp((uSizeX - pos.x) / edgeFadeWidth, 0, 1);
effectMask *= clamp((uSizeY - pos.y) / edgeFadeWidth, 0, 1);
return vec2(windowMask, effectMask);
}
`;
}