♻️ Make it possible to reuse the hexagon shader

This commit is contained in:
Simon Schneegans
2022-05-04 21:32:21 +02:00
parent 27d2a43c99
commit 3b0be6236c
3 changed files with 124 additions and 73 deletions
+1 -2
View File
@@ -37,8 +37,7 @@ const ALL_EFFECTS = [
// Me.imports.src.BrokenGlass.BrokenGlass, // Me.imports.src.BrokenGlass.BrokenGlass,
// Me.imports.src.EnergizeA.EnergizeA, // Me.imports.src.EnergizeA.EnergizeA,
// Me.imports.src.EnergizeB.EnergizeB, // Me.imports.src.EnergizeB.EnergizeB,
Me.imports.src.Fire.Fire, Me.imports.src.Fire.Fire, Me.imports.src.Hexagon.Hexagon,
// Me.imports.src.Hexagon.Hexagon,
// Me.imports.src.Matrix.Matrix, // Me.imports.src.Matrix.Matrix,
// Me.imports.src.SnapOfDisintegration.SnapOfDisintegration, // Me.imports.src.SnapOfDisintegration.SnapOfDisintegration,
// Me.imports.src.TRexAttack.TRexAttack, // Me.imports.src.TRexAttack.TRexAttack,
+1 -2
View File
@@ -36,8 +36,7 @@ const ALL_EFFECTS = [
// Me.imports.src.BrokenGlass.BrokenGlass, // Me.imports.src.BrokenGlass.BrokenGlass,
// Me.imports.src.EnergizeA.EnergizeA, // Me.imports.src.EnergizeA.EnergizeA,
// Me.imports.src.EnergizeB.EnergizeB, // Me.imports.src.EnergizeB.EnergizeB,
Me.imports.src.Fire.Fire, Me.imports.src.Fire.Fire, Me.imports.src.Hexagon.Hexagon,
// Me.imports.src.Hexagon.Hexagon,
// Me.imports.src.Matrix.Matrix, // Me.imports.src.Matrix.Matrix,
// Me.imports.src.SnapOfDisintegration.SnapOfDisintegration, // Me.imports.src.SnapOfDisintegration.SnapOfDisintegration,
// Me.imports.src.TRexAttack.TRexAttack, // Me.imports.src.TRexAttack.TRexAttack,
+83 -30
View File
@@ -26,8 +26,11 @@ const utils = Me.imports.src.utils;
// gradually shrink until the window is fully dissolved. // // gradually shrink until the window is fully dissolved. //
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
// The shader class for this effect is registered further down in this file. // The shader class for this effect is registered further down in this file. When this
let Shader = null; // effect is used for the first time, shaderInstance will store one instance of
// ShaderClass which will be used whenever this effect is used.
let ShaderClass = null;
let shaderInstance = null;
// The effect class is completely static. It can be used to get some metadata (like the // The effect class is completely static. It can be used to get some metadata (like the
// effect's name or supported GNOME Shell versions), to initialize the respective page of // effect's name or supported GNOME Shell versions), to initialize the respective page of
@@ -79,9 +82,16 @@ var Hexagon = class Hexagon {
// ---------------------------------------------------------------- API for extension.js // ---------------------------------------------------------------- API for extension.js
// This is called from extension.js whenever a window is closed with this effect. // This is called from extension.js whenever a window is opened or closed with this
static createShader(actor, settings, forOpening) { // effect.
return new Shader(settings, forOpening); static getShader(actor, settings, forOpening) {
if (shaderInstance == null) {
shaderInstance = new ShaderClass();
}
shaderInstance.setUniforms(settings, forOpening);
return shaderInstance;
} }
// The tweakTransition() is called from extension.js to tweak a window's open / close // The tweakTransition() is called from extension.js to tweak a window's open / close
@@ -101,6 +111,12 @@ var Hexagon = class Hexagon {
'scale-y': {from: 1.0, to: 1.0, mode: 1} 'scale-y': {from: 1.0, to: 1.0, mode: 1}
}; };
} }
// This is called from extension.js if the extension is disabled. This should free all
// static resources.
static cleanUp() {
shaderInstance = null;
}
} }
@@ -112,34 +128,72 @@ var Hexagon = class Hexagon {
if (utils.isInShellProcess()) { if (utils.isInShellProcess()) {
const Clutter = imports.gi.Clutter; const {Clutter, Shell} = imports.gi;
const shaderSnippets = Me.imports.src.shaderSnippets; const shaderSnippets = Me.imports.src.shaderSnippets;
Shader = GObject.registerClass({}, class Shader extends Clutter.ShaderEffect { ShaderClass = GObject.registerClass({}, class ShaderClass extends Shell.GLSLEffect {
_init(settings, forOpening) { // This is called when the effect is used for the first time. This can be used to
super._init({shader_type: Clutter.ShaderType.FRAGMENT_SHADER}); // store all required uniform locations.
_init() {
super._init();
this._uForOpening = this.get_uniform_location('uForOpening');
this._uAdditiveBlending = this.get_uniform_location('uAdditiveBlending');
this._uSeed = this.get_uniform_location('uSeed');
this._uScale = this.get_uniform_location('uScale');
this._uLineWidth = this.get_uniform_location('uLineWidth');
this._uGlowColor = this.get_uniform_location('uGlowColor');
this._uLineColor = this.get_uniform_location('uLineColor');
}
// This is called each time the effect is used. This can be used to retrieve the
// configuration from the settings and update all uniforms accordingly.
setUniforms(settings, forOpening) {
// Get the two configurable colors. They are directly injected into the shader code // Get the two configurable colors. They are directly injected into the shader code
// below. // below.
const glowColor = const glow =
Clutter.Color.from_string(settings.get_string('hexagon-glow-color'))[1]; Clutter.Color.from_string(settings.get_string('hexagon-glow-color'))[1];
const lineColor = const line =
Clutter.Color.from_string(settings.get_string('hexagon-line-color'))[1]; Clutter.Color.from_string(settings.get_string('hexagon-line-color'))[1];
// If we are currently performing integration test, the animation uses a fixed seed. // If we are currently performing integration test, the animation uses a fixed seed.
const testMode = settings.get_boolean('test-mode'); const testMode = settings.get_boolean('test-mode');
this.set_uniform_float(this._uForOpening, 1, [forOpening]);
this.set_uniform_float(this._uAdditiveBlending, 1,
[settings.get_boolean('hexagon-additive-blending')]);
this.set_uniform_float(
this._uSeed, 2, [testMode ? 0 : Math.random(), testMode ? 0 : Math.random()]);
this.set_uniform_float(this._uScale, 1, [settings.get_double('hexagon-scale')]);
this.set_uniform_float(this._uLineWidth, 1,
[settings.get_double('hexagon-line-width')]);
this.set_uniform_float(
this._uGlowColor, 4,
[glow.red / 255, glow.green / 255, glow.blue / 255, glow.alpha / 255]);
this.set_uniform_float(
this._uLineColor, 4,
[line.red / 255, line.green / 255, line.blue / 255, line.alpha / 255]);
}
// This is called by the constructor. This is means it's only called when the effect
// is used for the first time.
vfunc_build_pipeline() {
// Feel free to read the inline comments below to learn how this shader works. // Feel free to read the inline comments below to learn how this shader works.
this.set_shader_source(` const declarations = `
// Inject some common shader snippets. // Inject some common shader snippets.
${shaderSnippets.standardUniforms()} ${shaderSnippets.standardUniforms()}
${shaderSnippets.noise()} ${shaderSnippets.noise()}
const vec2 SEED = vec2(${testMode ? 0 : Math.random()}, uniform bool uForOpening;
${testMode ? 0 : Math.random()}); uniform bool uAdditiveBlending;
const float SCALE = 10.0 * ${settings.get_double('hexagon-scale')}; uniform vec2 uSeed;
const float LINE_WIDTH = 0.02 * ${settings.get_double('hexagon-line-width')}; uniform float uScale;
uniform float uLineWidth;
uniform vec4 uGlowColor;
uniform vec4 uLineColor;
// This methods generates a procedural hexagonal pattern. It returns four values: // This methods generates a procedural hexagonal pattern. It returns four values:
// result.xy: This contains cell-relative coordinates for the given point. // result.xy: This contains cell-relative coordinates for the given point.
@@ -182,15 +236,15 @@ if (utils.isInShellProcess()) {
return vec4(cellCoords, dist, glow); return vec4(cellCoords, dist, glow);
} }
`;
void main() { const code = `
// We simply inverse the progress for opening windows. // We simply inverse the progress for opening windows.
float progress = ${forOpening ? '1.0-uProgress' : 'uProgress'}; float progress = uForOpening ? 1.0-uProgress : uProgress;
// Add some smooth noise to the progress so that not every tile behaves the // Add some smooth noise to the progress so that not every tile behaves the
// same. // same.
float noise = simplex2D(cogl_tex_coord_in[0].st + SEED); float noise = simplex2D(cogl_tex_coord_in[0].st + uSeed);
progress = clamp(mix(noise - 1.0, noise + 1.0, progress), 0.0, 1.0); progress = clamp(mix(noise - 1.0, noise + 1.0, progress), 0.0, 1.0);
// glowProgress fades in in the first half of the animation, tileProgress fades // glowProgress fades in in the first half of the animation, tileProgress fades
@@ -198,7 +252,7 @@ if (utils.isInShellProcess()) {
float glowProgress = smoothstep(0, 1, clamp(progress / 0.5, 0, 1)); float glowProgress = smoothstep(0, 1, clamp(progress / 0.5, 0, 1));
float tileProgress = smoothstep(0, 1, clamp((progress - 0.5) / 0.5, 0, 1)); float tileProgress = smoothstep(0, 1, clamp((progress - 0.5) / 0.5, 0, 1));
vec2 texScale = vec2(uSizeX, uSizeY) / SCALE; vec2 texScale = 0.1 * vec2(uSizeX, uSizeY) / uScale;
vec4 hex = getHexagons(cogl_tex_coord_in[0].st * texScale); vec4 hex = getHexagons(cogl_tex_coord_in[0].st * texScale);
if (tileProgress > hex.z) { if (tileProgress > hex.z) {
@@ -213,19 +267,17 @@ if (utils.isInShellProcess()) {
vec2 lookupOffset = tileProgress * hex.xy / texScale / (1.0 - tileProgress); vec2 lookupOffset = tileProgress * hex.xy / texScale / (1.0 - tileProgress);
cogl_color_out = texture2D(uTexture, cogl_tex_coord_in[0].st + lookupOffset); cogl_color_out = texture2D(uTexture, cogl_tex_coord_in[0].st + lookupOffset);
vec4 glow = vec4(${glowColor.red / 255}, ${glowColor.green / 255}, vec4 glow = uGlowColor;
${glowColor.blue / 255}, ${glowColor.alpha / 255}); vec4 line = uLineColor;
vec4 line = vec4(${lineColor.red / 255}, ${lineColor.green / 255},
${lineColor.blue / 255}, ${lineColor.alpha / 255});
// For the glow, we accumulate a few exponentially scaled versions of hex.w. // For the glow, we accumulate a few exponentially scaled versions of hex.w.
glow.a *= pow(hex.w, 20.0) * 10.0 + glow.a *= pow(hex.w, 20.0) * 10.0 +
pow(hex.w, 10.0) * 5.0 + pow(hex.w, 10.0) * 5.0 +
pow(hex.w, 2.0) * 0.5; pow(hex.w, 2.0) * 0.5;
// Using step(LINE_WIDTH, hex.z) would be simpler, but the below creates some // Using step(uLineWidth, hex.z) would be simpler, but the below creates some
// fake antialiasing. // fake antialiasing.
line.a *= 1.0 - smoothstep(LINE_WIDTH*0.5, LINE_WIDTH, hex.z); line.a *= 1.0 - smoothstep(uLineWidth*0.02*0.5, uLineWidth*0.02, hex.z);
// Fade in the glowing lines. // Fade in the glowing lines.
glow.a *= glowProgress; glow.a *= glowProgress;
@@ -235,7 +287,7 @@ if (utils.isInShellProcess()) {
glow *= cogl_color_out.a; glow *= cogl_color_out.a;
line *= cogl_color_out.a; line *= cogl_color_out.a;
if (${settings.get_boolean('hexagon-additive-blending')}) { if (uAdditiveBlending) {
cogl_color_out.rgb += glow.rgb * glow.a; cogl_color_out.rgb += glow.rgb * glow.a;
cogl_color_out.rgb += line.rgb * line.a; cogl_color_out.rgb += line.rgb * line.a;
} else { } else {
@@ -243,8 +295,9 @@ if (utils.isInShellProcess()) {
cogl_color_out.rgb = mix(cogl_color_out.rgb, line.rgb, line.a); cogl_color_out.rgb = mix(cogl_color_out.rgb, line.rgb, line.a);
} }
} }
} `;
`);
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, declarations, code, true);
}; };
}); });
} }