gtk-demo: Add GskGLShaderNode demo
Add adds a demo showing off GskGLShaderNode in various ways. It has a transistion widget, using some examples from gl-transitions.com, with child widgets being both images, a GL area and real widgets (that let you edit the transition shaders themselves. It also has a fancy fire effect on hove on the buttons.
This commit is contained in:
@@ -133,6 +133,17 @@
|
||||
<file>cogs.glsl</file>
|
||||
<file>glowingstars.glsl</file>
|
||||
</gresource>
|
||||
<gresource prefix="/gltransition">
|
||||
<file>gtkshaderstack.c</file>
|
||||
<file>gtkshaderstack.h</file>
|
||||
<file>gtkshaderbin.h</file>
|
||||
<file>gtkshaderbin.c</file>
|
||||
<file>fire.glsl</file>
|
||||
<file>transition1.glsl</file>
|
||||
<file>transition2.glsl</file>
|
||||
<file>transition3.glsl</file>
|
||||
<file>transition4.glsl</file>
|
||||
</gresource>
|
||||
<gresource prefix="/iconscroll">
|
||||
<file>iconscroll.ui</file>
|
||||
</gresource>
|
||||
@@ -247,6 +258,7 @@
|
||||
<file>gears.c</file>
|
||||
<file>gestures.c</file>
|
||||
<file>glarea.c</file>
|
||||
<file>gltransition.c</file>
|
||||
<file>headerbar.c</file>
|
||||
<file>hypertext.c</file>
|
||||
<file>iconscroll.c</file>
|
||||
|
||||
72
demos/gtk-demo/fire.glsl
Normal file
72
demos/gtk-demo/fire.glsl
Normal file
@@ -0,0 +1,72 @@
|
||||
uniform float u_time;
|
||||
uniform sampler2D u_texture1;
|
||||
|
||||
/* 2D -> [0..1] random number generator */
|
||||
float random(vec2 st) {
|
||||
return fract(sin(dot(st.xy,
|
||||
vec2(12.9898,78.233))) *
|
||||
43758.5453123);
|
||||
}
|
||||
|
||||
/* Generate a smoothed 2d noise based on random() */
|
||||
float noise(vec2 v) {
|
||||
/* Round point v to integer grid grid */
|
||||
vec2 grid_point = floor(v);
|
||||
/* Randomize in grid corners */
|
||||
float corner1 = random(grid_point);
|
||||
float corner2 = random(grid_point + vec2(1, 0));
|
||||
float corner3 = random(grid_point + vec2(0, 1));
|
||||
float corner4 = random(grid_point + vec2(1, 1));
|
||||
/* Interpolate smoothly between grid points */
|
||||
vec2 fraction = smoothstep(vec2(0.0), vec2(1.0), fract(v));
|
||||
return mix(mix(corner1, corner2, fraction.x),
|
||||
mix(corner3, corner4, fraction.x),
|
||||
fraction.y);
|
||||
}
|
||||
|
||||
/* fractal brownian motion noice, see https://www.iquilezles.org/www/articles/fbm/fbm.htm */
|
||||
float fbm(in vec2 x)
|
||||
{
|
||||
const float octaveScale = 1.9;
|
||||
const float G = 0.5;
|
||||
float f = 1.0;
|
||||
float a = 1.0;
|
||||
float t = 0.0;
|
||||
int numOctaves = 5;
|
||||
for (int i = 0; i < numOctaves; i++) {
|
||||
t += a*noise(f*x);
|
||||
f *= octaveScale;
|
||||
a *= G;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv)
|
||||
{
|
||||
vec2 xy = fragCoord / resolution;
|
||||
|
||||
float zoom = 3.0 - sin(u_time*0.5)*0.3;
|
||||
|
||||
// Normalize coord to height of widget
|
||||
vec2 p = (vec2 (-resolution.x/2.0 + fragCoord.x, resolution.y - fragCoord.y) / resolution.yy)* zoom;
|
||||
|
||||
// Use recursive incantations of fbm
|
||||
float q1 = fbm(p - vec2(0.8, 0.3) * u_time);
|
||||
float q2 = fbm(p - vec2(0.5, 1.3) * u_time);
|
||||
float r = fbm(2.0*p + vec2(q1,q2) - vec2(0.0, 1.0)*u_time*10.0 *0.4);
|
||||
|
||||
// Compute intensity, mostly on the bottom
|
||||
float w = 2.0 * r * p.y;
|
||||
|
||||
// Smooth out left/right side and fade in at start
|
||||
w /= smoothstep(0.0,0.1, xy.x)* smoothstep(0.0,0.1, 1.0-xy.x) * smoothstep(0.0,0.4, u_time);
|
||||
|
||||
// Compute colors
|
||||
vec3 c = vec3(1.0,.2,.05);
|
||||
vec3 color = 1.0 / (w*w/c + 1.0);
|
||||
|
||||
// Mix in widget
|
||||
vec4 widget = GskTexture(u_texture1,uv);
|
||||
fragColor = mix(vec4(color,1), widget, 1.0-color.x);
|
||||
}
|
||||
300
demos/gtk-demo/gltransition.c
Normal file
300
demos/gtk-demo/gltransition.c
Normal file
@@ -0,0 +1,300 @@
|
||||
/* OpenGL/Transitions
|
||||
* #Keywords: OpenGL, shader
|
||||
*
|
||||
* Create transitions between pages using a custom fragment shader.
|
||||
* The examples here are taken from gl-transitions.com, and you
|
||||
* can edit the transision code itself on the last page of the stack.
|
||||
*
|
||||
* It also shows some sample fire effects on the buttons.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include "gtkshaderstack.h"
|
||||
#include "gtkshaderbin.h"
|
||||
#include "gtkshadertoy.h"
|
||||
|
||||
static GtkWidget *demo_window = NULL;
|
||||
|
||||
static void
|
||||
close_window (GtkWidget *widget)
|
||||
{
|
||||
/* Reset the state */
|
||||
demo_window = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
text_changed (GtkTextBuffer *buffer,
|
||||
GtkWidget *button)
|
||||
{
|
||||
gtk_widget_show (button);
|
||||
}
|
||||
|
||||
static void
|
||||
apply_text (GtkWidget *button,
|
||||
GtkTextBuffer *buffer)
|
||||
{
|
||||
GtkWidget *stack;
|
||||
GskGLShader *shader;
|
||||
GtkTextIter start, end;
|
||||
char *text;
|
||||
|
||||
stack = g_object_get_data (G_OBJECT (button), "the-stack");
|
||||
|
||||
gtk_text_buffer_get_bounds (buffer, &start, &end);
|
||||
text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
|
||||
|
||||
GBytes *bytes = g_bytes_new_take (text, strlen (text));
|
||||
shader = gsk_gl_shader_new_from_bytes (bytes);
|
||||
|
||||
gtk_shader_stack_set_shader (GTK_SHADER_STACK (stack), shader);
|
||||
|
||||
g_object_unref (shader);
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
gtk_widget_hide (button);
|
||||
}
|
||||
|
||||
static void
|
||||
go_back (GtkWidget *button,
|
||||
GtkWidget *stack)
|
||||
{
|
||||
gtk_shader_stack_transition (GTK_SHADER_STACK (stack), FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
go_forward (GtkWidget *button,
|
||||
GtkWidget *stack)
|
||||
{
|
||||
gtk_shader_stack_transition (GTK_SHADER_STACK (stack), TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
clicked_cb (GtkGestureClick *gesture,
|
||||
guint n_pressed,
|
||||
double x,
|
||||
double y,
|
||||
gpointer data)
|
||||
{
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
fire_bin_new (void)
|
||||
{
|
||||
GtkWidget *bin = gtk_shader_bin_new ();
|
||||
static GskGLShader *shader = NULL;
|
||||
|
||||
if (shader == NULL)
|
||||
shader = gsk_gl_shader_new_from_resource ("/gltransition/fire.glsl");
|
||||
|
||||
gtk_shader_bin_add_shader (GTK_SHADER_BIN (bin), shader, GTK_STATE_FLAG_PRELIGHT, GTK_STATE_FLAG_PRELIGHT);
|
||||
|
||||
return bin;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
new_shadertoy (const char *path)
|
||||
{
|
||||
GBytes *shader;
|
||||
GtkWidget *toy;
|
||||
|
||||
toy = gtk_shadertoy_new ();
|
||||
shader = g_resources_lookup_data (path, 0, NULL);
|
||||
gtk_shadertoy_set_image_shader (GTK_SHADERTOY (toy),
|
||||
g_bytes_get_data (shader, NULL));
|
||||
g_bytes_unref (shader);
|
||||
|
||||
return toy;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
make_shader_stack (const char *name,
|
||||
const char *resource_path,
|
||||
GtkWidget *scale)
|
||||
{
|
||||
GtkWidget *stack, *child, *widget, *vbox, *hbox, *bin;
|
||||
GtkWidget *label, *button, *tv;
|
||||
GskGLShader *shader;
|
||||
GObjectClass *class;
|
||||
GParamSpecFloat *pspec;
|
||||
GtkAdjustment *adjustment;
|
||||
GtkTextBuffer *buffer;
|
||||
GBytes *bytes;
|
||||
GtkEventController *controller;
|
||||
GtkCssProvider *provider;
|
||||
|
||||
stack = gtk_shader_stack_new ();
|
||||
shader = gsk_gl_shader_new_from_resource (resource_path);
|
||||
gtk_shader_stack_set_shader (GTK_SHADER_STACK (stack), shader);
|
||||
g_object_unref (shader);
|
||||
|
||||
child = gtk_picture_new_for_resource ("/css_pixbufs/background.jpg");
|
||||
gtk_picture_set_can_shrink (GTK_PICTURE (child), TRUE);
|
||||
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
|
||||
|
||||
child = gtk_picture_new_for_resource ("/transparent/portland-rose.jpg");
|
||||
gtk_picture_set_can_shrink (GTK_PICTURE (child), TRUE);
|
||||
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
|
||||
|
||||
child = new_shadertoy ("/shadertoy/neon.glsl");
|
||||
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
|
||||
|
||||
child = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
||||
|
||||
class = g_type_class_ref (GTK_TYPE_SHADER_STACK);
|
||||
pspec = G_PARAM_SPEC_FLOAT (g_object_class_find_property (class, "duration"));
|
||||
|
||||
adjustment = gtk_range_get_adjustment (GTK_RANGE (scale));
|
||||
if (gtk_adjustment_get_lower (adjustment) == 0.0 &&
|
||||
gtk_adjustment_get_upper (adjustment) == 0.0)
|
||||
{
|
||||
gtk_adjustment_configure (adjustment,
|
||||
pspec->default_value,
|
||||
pspec->minimum,
|
||||
pspec->maximum,
|
||||
0.1, 0.5, 0);
|
||||
}
|
||||
|
||||
g_type_class_unref (class);
|
||||
|
||||
g_object_bind_property (adjustment, "value",
|
||||
stack, "duration",
|
||||
G_BINDING_DEFAULT);
|
||||
|
||||
widget = gtk_scrolled_window_new ();
|
||||
gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (widget), TRUE);
|
||||
gtk_widget_set_hexpand (widget, TRUE);
|
||||
gtk_widget_set_vexpand (widget, TRUE);
|
||||
|
||||
controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
|
||||
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (controller), 0);
|
||||
g_signal_connect (controller, "released", G_CALLBACK (clicked_cb), NULL);
|
||||
gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_BUBBLE);
|
||||
gtk_widget_add_controller (GTK_WIDGET (widget), controller);
|
||||
|
||||
tv = gtk_text_view_new ();
|
||||
gtk_text_view_set_left_margin (GTK_TEXT_VIEW (tv), 4);
|
||||
gtk_text_view_set_right_margin (GTK_TEXT_VIEW (tv), 4);
|
||||
gtk_text_view_set_top_margin (GTK_TEXT_VIEW (tv), 4);
|
||||
gtk_text_view_set_bottom_margin (GTK_TEXT_VIEW (tv), 4);
|
||||
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
|
||||
bytes = g_resources_lookup_data (resource_path, 0, NULL);
|
||||
gtk_text_buffer_set_text (buffer,
|
||||
(const char *)g_bytes_get_data (bytes, NULL),
|
||||
g_bytes_get_size (bytes));
|
||||
g_bytes_unref (bytes);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (widget), tv);
|
||||
|
||||
gtk_box_append (GTK_BOX (child), widget);
|
||||
|
||||
gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
||||
|
||||
widget = gtk_center_box_new ();
|
||||
label = gtk_label_new (name);
|
||||
gtk_widget_add_css_class (label, "title-4");
|
||||
gtk_widget_set_size_request (label, -1, 26);
|
||||
gtk_center_box_set_center_widget (GTK_CENTER_BOX (widget), label);
|
||||
|
||||
button = gtk_button_new_from_icon_name ("view-refresh-symbolic");
|
||||
g_signal_connect (buffer, "changed", G_CALLBACK (text_changed), button);
|
||||
g_object_set_data (G_OBJECT (button), "the-stack", stack);
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (apply_text), buffer);
|
||||
provider = gtk_css_provider_new ();
|
||||
gtk_css_provider_load_from_data (provider, "button.small { padding: 0; }", -1);
|
||||
gtk_style_context_add_provider (gtk_widget_get_style_context (button),
|
||||
GTK_STYLE_PROVIDER (provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
g_object_unref (provider);
|
||||
gtk_widget_set_halign (button, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_valign (button, GTK_ALIGN_CENTER);
|
||||
gtk_widget_add_css_class (button, "small");
|
||||
gtk_widget_hide (button);
|
||||
gtk_center_box_set_end_widget (GTK_CENTER_BOX (widget), button);
|
||||
|
||||
gtk_box_append (GTK_BOX (vbox), widget);
|
||||
|
||||
gtk_box_append (GTK_BOX (vbox), stack);
|
||||
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
gtk_widget_set_halign (hbox, GTK_ALIGN_CENTER);
|
||||
|
||||
gtk_box_append (GTK_BOX (vbox), hbox);
|
||||
|
||||
button = gtk_button_new_from_icon_name ("go-previous");
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (go_back), stack);
|
||||
bin = fire_bin_new ();
|
||||
gtk_shader_bin_set_child (GTK_SHADER_BIN (bin), button);
|
||||
gtk_box_append (GTK_BOX (hbox), bin);
|
||||
|
||||
button = gtk_button_new_from_icon_name ("go-next");
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (go_forward), stack);
|
||||
bin = fire_bin_new ();
|
||||
gtk_shader_bin_set_child (GTK_SHADER_BIN (bin), button);
|
||||
gtk_box_append (GTK_BOX (hbox), bin);
|
||||
|
||||
return vbox;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
create_gltransition_window (GtkWidget *do_widget)
|
||||
{
|
||||
GtkWidget *window, *headerbar, *scale, *grid;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Transitions");
|
||||
headerbar = gtk_header_bar_new ();
|
||||
scale = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, NULL);
|
||||
gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
|
||||
gtk_widget_set_size_request (scale, 100, -1);
|
||||
gtk_header_bar_pack_end (GTK_HEADER_BAR (headerbar), scale);
|
||||
gtk_window_set_titlebar (GTK_WINDOW (window), headerbar);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
|
||||
g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
|
||||
|
||||
grid = gtk_grid_new ();
|
||||
gtk_window_set_child (GTK_WINDOW (window), grid);
|
||||
|
||||
gtk_widget_set_halign (grid, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_valign (grid, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_margin_start (grid, 12);
|
||||
gtk_widget_set_margin_end (grid, 12);
|
||||
gtk_widget_set_margin_top (grid, 12);
|
||||
gtk_widget_set_margin_bottom (grid, 12);
|
||||
gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
|
||||
gtk_grid_set_column_spacing (GTK_GRID (grid), 6);
|
||||
gtk_grid_set_row_homogeneous (GTK_GRID (grid), TRUE);
|
||||
gtk_grid_set_column_homogeneous (GTK_GRID (grid), TRUE);
|
||||
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
make_shader_stack ("Wind", "/gltransition/transition1.glsl", scale),
|
||||
0, 0, 1, 1);
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
make_shader_stack ("Radial", "/gltransition/transition2.glsl", scale),
|
||||
1, 0, 1, 1);
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
make_shader_stack ("Crosswarp", "/gltransition/transition3.glsl", scale),
|
||||
0, 1, 1, 1);
|
||||
gtk_grid_attach (GTK_GRID (grid),
|
||||
make_shader_stack ("Kaleidoscope", "/gltransition/transition4.glsl", scale),
|
||||
1, 1, 1, 1);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_gltransition (GtkWidget *do_widget)
|
||||
{
|
||||
if (!demo_window)
|
||||
demo_window = create_gltransition_window (do_widget);
|
||||
|
||||
if (!gtk_widget_get_visible (demo_window))
|
||||
gtk_widget_show (demo_window);
|
||||
else
|
||||
gtk_window_destroy (GTK_WINDOW (demo_window));
|
||||
|
||||
return demo_window;
|
||||
}
|
||||
220
demos/gtk-demo/gtkshaderbin.c
Normal file
220
demos/gtk-demo/gtkshaderbin.c
Normal file
@@ -0,0 +1,220 @@
|
||||
#include "gtkshaderbin.h"
|
||||
|
||||
typedef struct {
|
||||
GskGLShader *shader;
|
||||
GtkStateFlags state;
|
||||
GtkStateFlags state_mask;
|
||||
} ShaderInfo;
|
||||
|
||||
struct _GtkShaderBin
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
GtkWidget *child;
|
||||
GskGLShader *active_shader;
|
||||
GPtrArray *shaders;
|
||||
guint tick_id;
|
||||
float time;
|
||||
gint64 first_frame_time;
|
||||
};
|
||||
|
||||
struct _GtkShaderBinClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkShaderBin, gtk_shader_bin, GTK_TYPE_WIDGET)
|
||||
|
||||
static void
|
||||
shader_info_free (ShaderInfo *info)
|
||||
{
|
||||
g_object_unref (info->shader);
|
||||
g_free (info);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_bin_finalize (GObject *object)
|
||||
{
|
||||
GtkShaderBin *self = GTK_SHADER_BIN (object);
|
||||
|
||||
g_ptr_array_free (self->shaders, TRUE);
|
||||
|
||||
G_OBJECT_CLASS (gtk_shader_bin_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_bin_dispose (GObject *object)
|
||||
{
|
||||
GtkShaderBin *self = GTK_SHADER_BIN (object);
|
||||
|
||||
g_clear_pointer (&self->child, gtk_widget_unparent);
|
||||
|
||||
G_OBJECT_CLASS (gtk_shader_bin_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_shader_bin_tick (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkShaderBin *self = GTK_SHADER_BIN (widget);
|
||||
gint64 frame_time;
|
||||
|
||||
frame_time = gdk_frame_clock_get_frame_time (frame_clock);
|
||||
if (self->first_frame_time == 0)
|
||||
self->first_frame_time = frame_time;
|
||||
self->time = (frame_time - self->first_frame_time) / (float)G_USEC_PER_SEC;
|
||||
|
||||
gtk_widget_queue_draw (widget);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_bin_init (GtkShaderBin *self)
|
||||
{
|
||||
self->shaders = g_ptr_array_new_with_free_func ((GDestroyNotify)shader_info_free);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_shader_bin_update_active_shader (GtkShaderBin *self)
|
||||
{
|
||||
GtkStateFlags new_state = gtk_widget_get_state_flags (GTK_WIDGET (self));
|
||||
GskGLShader *new_shader = NULL;
|
||||
|
||||
for (int i = 0; i < self->shaders->len; i++)
|
||||
{
|
||||
ShaderInfo *info = g_ptr_array_index (self->shaders, i);
|
||||
|
||||
if ((info->state_mask & new_state) == info->state)
|
||||
{
|
||||
new_shader = info->shader;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (self->active_shader == new_shader)
|
||||
return;
|
||||
|
||||
self->active_shader = new_shader;
|
||||
self->first_frame_time = 0;
|
||||
|
||||
if (self->active_shader)
|
||||
{
|
||||
if (self->tick_id == 0)
|
||||
self->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (self),
|
||||
gtk_shader_bin_tick,
|
||||
NULL, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self->tick_id != 0)
|
||||
{
|
||||
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->tick_id);
|
||||
self->tick_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_bin_state_flags_changed (GtkWidget *widget,
|
||||
GtkStateFlags previous_state_flags)
|
||||
{
|
||||
GtkShaderBin *self = GTK_SHADER_BIN (widget);
|
||||
|
||||
gtk_shader_bin_update_active_shader (self);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_shader_bin_add_shader (GtkShaderBin *self,
|
||||
GskGLShader *shader,
|
||||
GtkStateFlags state,
|
||||
GtkStateFlags state_mask)
|
||||
{
|
||||
ShaderInfo *info = g_new0 (ShaderInfo, 1);
|
||||
info->shader = g_object_ref (shader);
|
||||
info->state = state;
|
||||
info->state_mask = state_mask;
|
||||
|
||||
g_ptr_array_add (self->shaders, info);
|
||||
|
||||
gtk_shader_bin_update_active_shader (self);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_shader_bin_set_child (GtkShaderBin *self,
|
||||
GtkWidget *child)
|
||||
{
|
||||
|
||||
if (self->child == child)
|
||||
return;
|
||||
|
||||
g_clear_pointer (&self->child, gtk_widget_unparent);
|
||||
|
||||
if (child)
|
||||
{
|
||||
self->child = child;
|
||||
gtk_widget_set_parent (child, GTK_WIDGET (self));
|
||||
}
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_shader_bin_get_child (GtkShaderBin *self)
|
||||
{
|
||||
return self->child;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_bin_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
GtkShaderBin *self = GTK_SHADER_BIN (widget);
|
||||
int width, height;
|
||||
|
||||
width = gtk_widget_get_width (widget);
|
||||
height = gtk_widget_get_height (widget);
|
||||
|
||||
if (self->active_shader)
|
||||
{
|
||||
gtk_snapshot_push_glshader_v (snapshot, self->active_shader,
|
||||
&GRAPHENE_RECT_INIT(0, 0, width, height),
|
||||
1,
|
||||
"u_time", &self->time,
|
||||
NULL
|
||||
);
|
||||
gtk_widget_snapshot_child (widget, self->child, snapshot);
|
||||
gtk_snapshot_pop (snapshot); /* Fallback */
|
||||
gtk_widget_snapshot_child (widget, self->child, snapshot);
|
||||
gtk_snapshot_pop (snapshot); /* Shader node child 1 */
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_snapshot_child (widget, self->child, snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_bin_class_init (GtkShaderBinClass *class)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = gtk_shader_bin_finalize;
|
||||
object_class->dispose = gtk_shader_bin_dispose;
|
||||
|
||||
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
|
||||
|
||||
widget_class->snapshot = gtk_shader_bin_snapshot;
|
||||
widget_class->state_flags_changed = gtk_shader_bin_state_flags_changed;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_shader_bin_new (void)
|
||||
{
|
||||
GtkShaderBin *self;
|
||||
|
||||
self = g_object_new (GTK_TYPE_SHADER_BIN, NULL);
|
||||
|
||||
return GTK_WIDGET (self);
|
||||
}
|
||||
22
demos/gtk-demo/gtkshaderbin.h
Normal file
22
demos/gtk-demo/gtkshaderbin.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef __GTK_SHADER_BIN_H__
|
||||
#define __GTK_SHADER_BIN_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_SHADER_BIN (gtk_shader_bin_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkShaderBin, gtk_shader_bin, GTK, SHADER_BIN, GtkWidget)
|
||||
|
||||
GtkWidget *gtk_shader_bin_new (void);
|
||||
void gtk_shader_bin_add_shader (GtkShaderBin *self,
|
||||
GskGLShader *shader,
|
||||
GtkStateFlags state,
|
||||
GtkStateFlags state_mask);
|
||||
void gtk_shader_bin_set_child (GtkShaderBin *self,
|
||||
GtkWidget *child);
|
||||
GtkWidget *gtk_shader_bin_get_child (GtkShaderBin *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_SHADER_BIN_H__ */
|
||||
342
demos/gtk-demo/gtkshaderstack.c
Normal file
342
demos/gtk-demo/gtkshaderstack.c
Normal file
@@ -0,0 +1,342 @@
|
||||
#include "gtkshaderstack.h"
|
||||
|
||||
struct _GtkShaderStack
|
||||
{
|
||||
GtkWidget parent_instance;
|
||||
|
||||
GskGLShader *shader;
|
||||
GPtrArray *children;
|
||||
int current;
|
||||
int next;
|
||||
gboolean backwards;
|
||||
|
||||
guint tick_id;
|
||||
float time;
|
||||
float duration;
|
||||
gint64 start_time;
|
||||
};
|
||||
|
||||
struct _GtkShaderStackClass
|
||||
{
|
||||
GtkWidgetClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
enum {
|
||||
PROP_DURATION = 1,
|
||||
NUM_PROPERTIES
|
||||
};
|
||||
|
||||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL };
|
||||
|
||||
G_DEFINE_TYPE (GtkShaderStack, gtk_shader_stack, GTK_TYPE_WIDGET)
|
||||
|
||||
static void
|
||||
gtk_shader_stack_finalize (GObject *object)
|
||||
{
|
||||
GtkShaderStack *self = GTK_SHADER_STACK (object);
|
||||
|
||||
g_object_unref (self->shader);
|
||||
|
||||
G_OBJECT_CLASS (gtk_shader_stack_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
update_child_visible (GtkShaderStack *self)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < self->children->len; i++)
|
||||
{
|
||||
GtkWidget *child = g_ptr_array_index (self->children, i);
|
||||
|
||||
gtk_widget_set_child_visible (child,
|
||||
i == self->current || i == self->next);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
transition_cb (GtkWidget *widget,
|
||||
GdkFrameClock *clock,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkShaderStack *self = GTK_SHADER_STACK (widget);
|
||||
gint64 frame_time;
|
||||
|
||||
frame_time = gdk_frame_clock_get_frame_time (clock);
|
||||
|
||||
if (self->start_time == 0)
|
||||
self->start_time = frame_time;
|
||||
|
||||
self->time = (frame_time - self->start_time) / (float)G_USEC_PER_SEC;
|
||||
|
||||
gtk_widget_queue_draw (widget);
|
||||
|
||||
if (self->time >= self->duration)
|
||||
{
|
||||
self->current = self->next;
|
||||
self->next = -1;
|
||||
|
||||
update_child_visible (self);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
else
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
start_transition (GtkShaderStack *self)
|
||||
{
|
||||
self->start_time = 0;
|
||||
self->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (self),
|
||||
transition_cb,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
stop_transition (GtkShaderStack *self)
|
||||
{
|
||||
if (self->tick_id != 0)
|
||||
{
|
||||
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->tick_id);
|
||||
self->tick_id = 0;
|
||||
}
|
||||
|
||||
if (self->next != -1)
|
||||
self->current = self->next;
|
||||
self->next = -1;
|
||||
|
||||
update_child_visible (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_stack_dispose (GObject *object)
|
||||
{
|
||||
GtkShaderStack *self = GTK_SHADER_STACK (object);
|
||||
|
||||
stop_transition (self);
|
||||
|
||||
g_clear_pointer (&self->children, g_ptr_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_shader_stack_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_shader_stack_transition (GtkShaderStack *self,
|
||||
gboolean forward)
|
||||
{
|
||||
stop_transition (self);
|
||||
|
||||
self->backwards = !forward;
|
||||
if (self->backwards)
|
||||
self->next = (self->current + self->children->len - 1) % self->children->len;
|
||||
else
|
||||
self->next = (self->current + 1) % self->children->len;
|
||||
|
||||
update_child_visible (self);
|
||||
|
||||
start_transition (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_stack_init (GtkShaderStack *self)
|
||||
{
|
||||
self->children = g_ptr_array_new_with_free_func ((GDestroyNotify)gtk_widget_unparent);
|
||||
self->current = -1;
|
||||
self->next = -1;
|
||||
self->backwards = FALSE;
|
||||
self->duration = 1.0;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_stack_measure (GtkWidget *widget,
|
||||
GtkOrientation orientation,
|
||||
int for_size,
|
||||
int *minimum,
|
||||
int *natural,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkShaderStack *self = GTK_SHADER_STACK (widget);
|
||||
int i;
|
||||
|
||||
*minimum = 0;
|
||||
*natural = 0;
|
||||
|
||||
for (i = 0; i < self->children->len; i++)
|
||||
{
|
||||
GtkWidget *child = g_ptr_array_index (self->children, i);
|
||||
int child_min, child_nat;
|
||||
|
||||
if (gtk_widget_get_visible (child))
|
||||
{
|
||||
gtk_widget_measure (child, orientation, for_size, &child_min, &child_nat, NULL, NULL);
|
||||
|
||||
*minimum = MAX (*minimum, child_min);
|
||||
*natural = MAX (*natural, child_nat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_stack_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkShaderStack *self = GTK_SHADER_STACK (widget);
|
||||
GtkAllocation child_allocation;
|
||||
GtkWidget *child;
|
||||
int i;
|
||||
|
||||
child_allocation.x = 0;
|
||||
child_allocation.y = 0;
|
||||
child_allocation.width = width;
|
||||
child_allocation.height = height;
|
||||
|
||||
for (i = 0; i < self->children->len; i++)
|
||||
{
|
||||
child = g_ptr_array_index (self->children, i);
|
||||
if (gtk_widget_get_visible (child))
|
||||
gtk_widget_size_allocate (child, &child_allocation, -1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_stack_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
GtkShaderStack *self = GTK_SHADER_STACK (widget);
|
||||
int width, height;
|
||||
GtkWidget *current, *next;
|
||||
|
||||
width = gtk_widget_get_width (widget);
|
||||
height = gtk_widget_get_height (widget);
|
||||
|
||||
current = g_ptr_array_index (self->children, self->current);
|
||||
|
||||
if (self->next == -1)
|
||||
{
|
||||
gtk_widget_snapshot_child (widget, current, snapshot);
|
||||
}
|
||||
else
|
||||
{
|
||||
float progress;
|
||||
|
||||
next = g_ptr_array_index (self->children, self->next);
|
||||
|
||||
progress = self->time / self->duration;
|
||||
|
||||
if (self->backwards)
|
||||
{
|
||||
GtkWidget *tmp = next;
|
||||
next = current;
|
||||
current = tmp;
|
||||
progress = 1. - progress;
|
||||
}
|
||||
|
||||
gtk_snapshot_push_glshader_v (snapshot,
|
||||
self->shader,
|
||||
&GRAPHENE_RECT_INIT(0, 0, width, height),
|
||||
2,
|
||||
"progress", &progress,
|
||||
NULL);
|
||||
gtk_widget_snapshot_child (widget, next, snapshot);
|
||||
gtk_snapshot_pop (snapshot); /* Fallback */
|
||||
gtk_widget_snapshot_child (widget, current, snapshot);
|
||||
gtk_snapshot_pop (snapshot); /* current child */
|
||||
gtk_widget_snapshot_child (widget, next, snapshot);
|
||||
gtk_snapshot_pop (snapshot); /* next child */
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_stack_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkShaderStack *self = GTK_SHADER_STACK (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DURATION:
|
||||
g_value_set_float (value, self->duration);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_stack_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkShaderStack *self = GTK_SHADER_STACK (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DURATION:
|
||||
self->duration = g_value_get_float (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_shader_stack_class_init (GtkShaderStackClass *class)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = gtk_shader_stack_finalize;
|
||||
object_class->dispose = gtk_shader_stack_dispose;
|
||||
object_class->get_property = gtk_shader_stack_get_property;
|
||||
object_class->set_property = gtk_shader_stack_set_property;
|
||||
|
||||
widget_class->snapshot = gtk_shader_stack_snapshot;
|
||||
widget_class->measure = gtk_shader_stack_measure;
|
||||
widget_class->size_allocate = gtk_shader_stack_size_allocate;
|
||||
|
||||
properties[PROP_DURATION] =
|
||||
g_param_spec_float ("duration", "Duration", "Duration",
|
||||
0.1, 3.0, 1.0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_shader_stack_new (void)
|
||||
{
|
||||
return g_object_new (GTK_TYPE_SHADER_STACK, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_shader_stack_set_shader (GtkShaderStack *self,
|
||||
GskGLShader *shader)
|
||||
{
|
||||
g_set_object (&self->shader, shader);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_shader_stack_add_child (GtkShaderStack *self,
|
||||
GtkWidget *child)
|
||||
{
|
||||
g_ptr_array_add (self->children, child);
|
||||
gtk_widget_set_parent (child, GTK_WIDGET (self));
|
||||
gtk_widget_queue_resize (GTK_WIDGET (self));
|
||||
|
||||
if (self->current == -1)
|
||||
self->current = 0;
|
||||
else
|
||||
gtk_widget_set_child_visible (child, FALSE);
|
||||
}
|
||||
21
demos/gtk-demo/gtkshaderstack.h
Normal file
21
demos/gtk-demo/gtkshaderstack.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef __GTK_SHADER_STACK_H__
|
||||
#define __GTK_SHADER_STACK_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_SHADER_STACK (gtk_shader_stack_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (GtkShaderStack, gtk_shader_stack, GTK, SHADER_STACK, GtkWidget)
|
||||
|
||||
GtkWidget * gtk_shader_stack_new (void);
|
||||
void gtk_shader_stack_set_shader (GtkShaderStack *self,
|
||||
GskGLShader *shader);
|
||||
void gtk_shader_stack_add_child (GtkShaderStack *self,
|
||||
GtkWidget *child);
|
||||
void gtk_shader_stack_transition (GtkShaderStack *self,
|
||||
gboolean forward);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_SHADER_STACK_H__ */
|
||||
@@ -32,6 +32,7 @@ demos = files([
|
||||
'gears.c',
|
||||
'gestures.c',
|
||||
'glarea.c',
|
||||
'gltransition.c',
|
||||
'headerbar.c',
|
||||
'hypertext.c',
|
||||
'iconscroll.c',
|
||||
@@ -102,7 +103,9 @@ extra_demo_sources = files(['main.c',
|
||||
'gtkfishbowl.c',
|
||||
'fontplane.c',
|
||||
'gtkgears.c',
|
||||
'gtkshaderbin.c',
|
||||
'gtkshadertoy.c',
|
||||
'gtkshaderstack.c',
|
||||
'puzzlepiece.c',
|
||||
'bluroverlay.c',
|
||||
'demoimage.c',
|
||||
|
||||
33
demos/gtk-demo/transition1.glsl
Normal file
33
demos/gtk-demo/transition1.glsl
Normal file
@@ -0,0 +1,33 @@
|
||||
uniform float progress;
|
||||
uniform sampler2D u_texture1;
|
||||
uniform sampler2D u_texture2;
|
||||
|
||||
vec4 getFromColor (vec2 uv) {
|
||||
return GskTexture(u_texture1, uv);
|
||||
}
|
||||
|
||||
vec4 getToColor (vec2 uv) {
|
||||
return GskTexture(u_texture2, uv);
|
||||
}
|
||||
|
||||
// Source: https://gl-transitions.com/editor/wind
|
||||
// Author: gre
|
||||
// License: MIT
|
||||
|
||||
const float size = 0.2;
|
||||
|
||||
float rand(vec2 co) {
|
||||
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
vec4 transition(vec2 p) {
|
||||
float r = rand(vec2(0, p.y));
|
||||
float m = smoothstep(0.0, -size, p.x*(1.0-size) + size*r - (progress * (1.0 + size)));
|
||||
return mix(getFromColor(p), getToColor(p), m);
|
||||
}
|
||||
|
||||
|
||||
void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv)
|
||||
{
|
||||
fragColor = transition(uv);
|
||||
}
|
||||
34
demos/gtk-demo/transition2.glsl
Normal file
34
demos/gtk-demo/transition2.glsl
Normal file
@@ -0,0 +1,34 @@
|
||||
uniform float progress;
|
||||
uniform sampler2D u_texture1;
|
||||
uniform sampler2D u_texture2;
|
||||
|
||||
vec4 getFromColor (vec2 uv) {
|
||||
return GskTexture(u_texture1, uv);
|
||||
}
|
||||
|
||||
vec4 getToColor (vec2 uv) {
|
||||
return GskTexture(u_texture2, uv);
|
||||
}
|
||||
|
||||
// Source: https://gl-transitions.com/editor/Radial
|
||||
// License: MIT
|
||||
// Author: Xaychru
|
||||
|
||||
const float smoothness = 1.0;
|
||||
|
||||
const float PI = 3.141592653589;
|
||||
|
||||
vec4 transition(vec2 p) {
|
||||
vec2 rp = p*2.-1.;
|
||||
return mix(
|
||||
getToColor(p),
|
||||
getFromColor(p),
|
||||
smoothstep(0., smoothness, atan(rp.y,rp.x) - (progress-.5) * PI * 2.5)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv)
|
||||
{
|
||||
fragColor = transition(uv);
|
||||
}
|
||||
27
demos/gtk-demo/transition3.glsl
Normal file
27
demos/gtk-demo/transition3.glsl
Normal file
@@ -0,0 +1,27 @@
|
||||
uniform float progress;
|
||||
uniform sampler2D u_texture1;
|
||||
uniform sampler2D u_texture2;
|
||||
|
||||
vec4 getFromColor (vec2 uv) {
|
||||
return GskTexture(u_texture1, uv);
|
||||
}
|
||||
|
||||
vec4 getToColor (vec2 uv) {
|
||||
return GskTexture(u_texture2, uv);
|
||||
}
|
||||
|
||||
// Source: https://gl-transitions.com/editor/crosswarp
|
||||
// Author: Eke Péter <peterekepeter@gmail.com>
|
||||
// License: MIT
|
||||
|
||||
vec4 transition(vec2 p) {
|
||||
float x = progress;
|
||||
x=smoothstep(.0,1.0,(x*2.0+p.x-1.0));
|
||||
return mix(getFromColor((p-.5)*(1.-x)+.5), getToColor((p-.5)*x+.5), x);
|
||||
}
|
||||
|
||||
|
||||
void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv)
|
||||
{
|
||||
fragColor = transition(uv);
|
||||
}
|
||||
41
demos/gtk-demo/transition4.glsl
Normal file
41
demos/gtk-demo/transition4.glsl
Normal file
@@ -0,0 +1,41 @@
|
||||
uniform float progress;
|
||||
uniform sampler2D u_texture1;
|
||||
uniform sampler2D u_texture2;
|
||||
|
||||
vec4 getFromColor (vec2 uv) {
|
||||
return GskTexture(u_texture1, uv);
|
||||
}
|
||||
|
||||
vec4 getToColor (vec2 uv) {
|
||||
return GskTexture(u_texture2, uv);
|
||||
}
|
||||
|
||||
// Source: https://gl-transitions.com/editor/kaleidoscope
|
||||
// Author: nwoeanhinnogaehr
|
||||
// License: MIT
|
||||
|
||||
const float speed = 1.0;
|
||||
const float angle = 1.0;
|
||||
const float power = 1.5;
|
||||
|
||||
vec4 transition(vec2 uv) {
|
||||
vec2 p = uv.xy / vec2(1.0).xy;
|
||||
vec2 q = p;
|
||||
float t = pow(progress, power)*speed;
|
||||
p = p -0.5;
|
||||
for (int i = 0; i < 7; i++) {
|
||||
p = vec2(sin(t)*p.x + cos(t)*p.y, sin(t)*p.y - cos(t)*p.x);
|
||||
t += angle;
|
||||
p = abs(mod(p, 2.0) - 1.0);
|
||||
}
|
||||
abs(mod(p, 1.0));
|
||||
return mix(
|
||||
mix(getFromColor(q), getToColor(q), progress),
|
||||
mix(getFromColor(p), getToColor(p), progress), 1.0 - 2.0*abs(progress - 0.5));
|
||||
}
|
||||
|
||||
|
||||
void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv)
|
||||
{
|
||||
fragColor = transition(uv);
|
||||
}
|
||||
Reference in New Issue
Block a user