1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
#version 330
in vec2 texcoord; // texture coordinate of the fragment
uniform sampler2D tex; // texture of the window
ivec2 window_size = textureSize(tex, 0); // Size of the window
ivec2 window_center = ivec2(window_size.x/2, window_size.y/2);
/*
These shaders use a sorta hacky way to use the changing
window opacity you might set on picom.conf animation rules
to perform animations.
Basically, when a window get's mapped, we make it's alpha
go from 0 to 1, so, using the default_post_processing to get that alpha
we can get a variable going from 0 (start of mapping animation)
to 1 (end of mapping animation)
You can also set up your alpha value to go from 1 to 0 in picom when
a window is closed, effectively reversing the animations described here
*/
// Default window post-processing:
// 1) invert color
// 2) opacity / transparency
// 3) max-brightness clamping
// 4) rounded corners
vec4 default_post_processing(vec4 c);
// If you have semitransparent windows (like a terminal)
// You can use the below function to add an opacity threshold where the
// animation won't apply. For example, if you had your terminal
// configured to have 0.8 opacity, you'd set the below variable to 0.8
float max_opacity = 0.8;
float opacity_threshold(float opacity)
{
// if statement jic?
if (opacity >= max_opacity)
{
return 1.0;
}
else
{
return min(1, opacity/max_opacity);
}
}
// Pseudo-random function (from original shader)
float random(vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}
float PI = 3.1415926535;
float TWO_PI = 2.0 * PI;
// NEW anim function: Glass-Shard Shatter
vec4 anim(float animation_progress) {
vec4 out_color = vec4(0.0); // Default to transparent
// --- Shard Parameters ---
float num_shards = 20.0; // Number of angular shards
vec2 impact_point = window_center;
// --- Fragment's Relation to Impact Point & Shard ID ---
vec2 vec_frag_to_impact = texcoord - impact_point;
float dist_frag_to_impact = length(vec_frag_to_impact);
float angle_frag = atan(vec_frag_to_impact.y, vec_frag_to_impact.x); // Range: -PI to PI
if (angle_frag < 0.0) {
angle_frag += TWO_PI; // Normalize to 0 to 2*PI
}
float shard_id = floor(angle_frag / (TWO_PI / num_shards));
// --- Staggered Animation Timing for each Shard ---
// Use random for a less ordered shatter
float shard_delay_normalized = random(vec2(shard_id, shard_id * 0.31));
// float shard_delay_normalized = shard_id / num_shards; // For a sweep
float individual_shard_anim_duration = 0.7; // How long each shard takes to animate
float ripple_spread_factor = 1.0 - individual_shard_anim_duration;
float stagger_start_progress = shard_delay_normalized * ripple_spread_factor;
float stagger_end_progress = stagger_start_progress + individual_shard_anim_duration;
// shard_anim_progress: 0.0 (shard starts moving in) -> 1.0 (shard is in place)
float shard_anim_progress = smoothstep(stagger_start_progress, stagger_end_progress, animation_progress);
if (shard_anim_progress < 0.001) { // Shard is not yet visible or fully shattered away
return vec4(0.0); // Fully transparent
}
// --- Shard Transformation Parameters ---
// current_displacement_factor: 1.0 (max shatter) -> 0.0 (assembled)
float current_displacement_factor = 1.0 - shard_anim_progress;
// Max translation (e.g., 30% of half window width)
float max_translation_dist = length(vec2(window_size) * 0.5) * 0.3;
// Max rotation (e.g., 25 degrees)
float max_rotation_angle_rad = (PI / 180.0) * 25.0 * random(vec2(shard_id * 0.7, shard_id)); // Add some randomness to rotation
// Direction for this shard (center angle of the shard sector)
float shard_center_angle = (shard_id + 0.5) * (TWO_PI / num_shards);
vec2 shard_radial_dir = vec2(cos(shard_center_angle), sin(shard_center_angle));
vec2 translation_offset = shard_radial_dir * max_translation_dist * current_displacement_factor;
float current_rotation = max_rotation_angle_rad * current_displacement_factor;
// --- Inverse Transformation for Sampling ---
// We are at `texcoord` on screen. Find where this point came from on the original texture.
// 1. Undo translation
vec2 p1_translated_back = texcoord - translation_offset;
// 2. Undo rotation around impact_point
vec2 p1_rel_to_impact = p1_translated_back - impact_point;
float cos_rot = cos(current_rotation); // Rotate by +angle to undo shatter rotation by -angle
float sin_rot = sin(current_rotation); // (or vice-versa, depends on convention)
// Let's assume shatter rotates by -current_rotation
// So to undo, rotate by +current_rotation
mat2 rot_matrix = mat2(cos_rot, -sin_rot, sin_rot, cos_rot);
vec2 p2_rotated_back = rot_matrix * p1_rel_to_impact;
vec2 sample_coord = p2_rotated_back + impact_point;
// --- Boundary Check & Texture Fetch ---
if (sample_coord.x >= 0.0 && sample_coord.x < float(window_size.x) &&
sample_coord.y >= 0.0 && sample_coord.y < float(window_size.y)) {
// --- Chromatic Aberration ---
float ca_strength = 0.008 * current_displacement_factor; // Stronger when more shattered
vec2 ca_offset_dir = shard_radial_dir; // Radial aberration
// vec2 ca_offset_dir = vec2(-shard_radial_dir.y, shard_radial_dir.x); // Tangential
vec2 r_sample = sample_coord + ca_offset_dir * ca_strength * float(window_size.x);
vec2 b_sample = sample_coord - ca_offset_dir * ca_strength * float(window_size.x);
out_color.r = texelFetch(tex, ivec2(r_sample), 0).r;
out_color.g = texelFetch(tex, ivec2(sample_coord), 0).g; // Green channel from center
out_color.b = texelFetch(tex, ivec2(b_sample), 0).b;
out_color.a = texelFetch(tex, ivec2(sample_coord), 0).a; // Base alpha from original texture
} else {
out_color.a = 0.0; // Sampled point is outside original texture
}
// Modulate final alpha by shard's animation progress
out_color.a *= shard_anim_progress;
return out_color;
}
// Default window shader:
// 1) fetch the specified pixel
// 2) apply default post-processing
vec4 window_shader() {
vec4 c = texelFetch(tex, ivec2(texcoord), 0);
c = default_post_processing(c);
float opacity = opacity_threshold(c.w);
if (opacity == 0.0)
{
return c;
}
vec4 anim_c = anim(opacity);
return default_post_processing(anim_c);
}
|