From 1bc81fac2aaa856bfff6f621fb117e2c63d0db4a Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 24 Sep 2020 21:14:46 -0400 Subject: [PATCH] gtk-demo: Add a shader paintable demo Add a shader paintable to the OpenGL transitions demo. This is reusing one of the shadertoy examples, tweaked slightly to work as a standalone fragment shader. --- demos/gtk-demo/cogs2.glsl | 229 ++++++++++++++++++++++++++++++ demos/gtk-demo/demo.gresource.xml | 1 + demos/gtk-demo/gltransition.c | 43 +++++- 3 files changed, 266 insertions(+), 7 deletions(-) create mode 100644 demos/gtk-demo/cogs2.glsl diff --git a/demos/gtk-demo/cogs2.glsl b/demos/gtk-demo/cogs2.glsl new file mode 100644 index 0000000000..59fea55326 --- /dev/null +++ b/demos/gtk-demo/cogs2.glsl @@ -0,0 +1,229 @@ +uniform float iTime; + +in vec2 fragCoord; +out vec4 vFragColor; + +// Originally from: https://www.shadertoy.com/view/3ljyDD +// License CC0: Hexagonal tiling + cog wheels +// Nothing fancy, just hexagonal tiling + cog wheels + +#define PI 3.141592654 +#define TAU (2.0*PI) +#define MROT(a) mat2(cos(a), sin(a), -sin(a), cos(a)) + +float hash(in vec2 co) { + return fract(sin(dot(co.xy ,vec2(12.9898,58.233))) * 13758.5453); +} + +float pcos(float a) { + return 0.5 + 0.5*cos(a); +} + +void rot(inout vec2 p, float a) { + float c = cos(a); + float s = sin(a); + p = vec2(c*p.x + s*p.y, -s*p.x + c*p.y); +} + +float modPolar(inout vec2 p, float repetitions) { + float angle = 2.0*PI/repetitions; + float a = atan(p.y, p.x) + angle/2.; + float r = length(p); + float c = floor(a/angle); + a = mod(a,angle) - angle/2.; + p = vec2(cos(a), sin(a))*r; + // For an odd number of repetitions, fix cell index of the cell in -x direction + // (cell index would be e.g. -5 and 5 in the two halves of the cell): + if (abs(c) >= (repetitions/2.0)) c = abs(c); + return c; +} + +float pmin(float a, float b, float k) { + float h = clamp( 0.5+0.5*(b-a)/k, 0.0, 1.0 ); + return mix( b, a, h ) - k*h*(1.0-h); +} + +const vec2 sz = vec2(1.0, sqrt(3.0)); +const vec2 hsz = 0.5*sz; +const float smallCount = 16.0; + +vec2 hextile(inout vec2 p) { + // See Art of Code: Hexagonal Tiling Explained! + // https://www.youtube.com/watch?v=VmrIDyYiJBA + + vec2 p1 = mod(p, sz)-hsz; + vec2 p2 = mod(p - hsz*1.0, sz)-hsz; + vec2 p3 = mix(p2, p1, vec2(length(p1) < length(p2))); + vec2 n = p3 - p; + p = p3; + + return n; +} + +float circle(vec2 p, float r) { + return length(p) - r; +} + +float box(vec2 p, vec2 b) { + vec2 d = abs(p)-b; + return length(max(d,0.0)) + min(max(d.x,d.y),0.0); +} + +float unevenCapsule(vec2 p, float r1, float r2, float h) { + p.x = abs(p.x); + float b = (r1-r2)/h; + float a = sqrt(1.0-b*b); + float k = dot(p,vec2(-b,a)); + if( k < 0.0 ) return length(p) - r1; + if( k > a*h ) return length(p-vec2(0.0,h)) - r2; + return dot(p, vec2(a,b) ) - r1; +} + +float cogwheel(vec2 p, float innerRadius, float outerRadius, float cogs, float holes) { + float cogWidth = 0.25*innerRadius*TAU/cogs; + + float d0 = circle(p, innerRadius); + + vec2 icp = p; + modPolar(icp, holes); + icp -= vec2(innerRadius*0.55, 0.0); + float d1 = circle(icp, innerRadius*0.25); + + vec2 cp = p; + modPolar(cp, cogs); + cp -= vec2(innerRadius, 0.0); + float d2 = unevenCapsule(cp.yx, cogWidth, cogWidth*0.75, (outerRadius-innerRadius)); + + float d3 = circle(p, innerRadius*0.20); + + float d = 1E6; + d = min(d, d0); + d = pmin(d, d2, 0.5*cogWidth); + d = min(d, d2); + d = max(d, -d1); + d = max(d, -d3); + + return d; +} + +float ccell1(vec2 p, float r) { + float d = 1E6; + const float bigCount = 60.0; + + vec2 cp0 = p; + rot(cp0, -iTime*TAU/bigCount); + float d0 = cogwheel(cp0, 0.36, 0.38, bigCount, 5.0); + + vec2 cp1 = p; + float nm = modPolar(cp1, 6.0); + + cp1 -= vec2(0.5, 0.0); + rot(cp1, 0.2+TAU*nm/2.0 + iTime*TAU/smallCount); + float d1 = cogwheel(cp1, 0.11, 0.125, smallCount, 5.0); + + d = min(d, d0); + d = min(d, d1); + return d; +} + +float ccell2(vec2 p, float r) { + float d = 1E6; + vec2 cp0 = p; + float nm = modPolar(cp0, 6.0); + vec2 cp1 = cp0; + const float off = 0.275; + const float count = smallCount + 2.0; + cp0 -= vec2(off, 0.0); + rot(cp0, 0.+TAU*nm/2.0 - iTime*TAU/count); + float d0 = cogwheel(cp0, 0.09, 0.105, count, 5.0); + + + cp1 -= vec2(0.5, 0.0); + rot(cp1, 0.2+TAU*nm/2.0 + iTime*TAU/smallCount); + float d1 = cogwheel(cp1, 0.11, 0.125, smallCount, 5.0); + + float l = length(p); + float d2 = l - (off+0.055); + float d3 = d2 + 0.020;; + + vec2 tp0 = p; + modPolar(tp0, 60.0); + tp0.x -= off; + float d4 = box(tp0, vec2(0.0125, 0.005)); + + float ctime = -(iTime*0.05 + r)*TAU; + + vec2 tp1 = p; + rot(tp1, ctime*12.0); + tp1.x -= 0.13; + float d5 = box(tp1, vec2(0.125, 0.005)); + + vec2 tp2 = p; + rot(tp2, ctime); + tp2.x -= 0.13*0.5; + float d6 = box(tp2, vec2(0.125*0.5, 0.0075)); + + float d7 = l - 0.025; + float d8 = l - 0.0125; + + d = min(d, d0); + d = min(d, d1); + d = min(d, d2); + d = max(d, -d3); + d = min(d, d4); + d = min(d, d5); + d = min(d, d6); + d = min(d, d7); + d = max(d, -d8); + + return d; +} + +float df(vec2 p, float scale, inout vec2 nn) { + p /= scale; + nn = hextile(p); + nn = round(nn); + float r = hash(nn); + + float d;; + + if (r < 0.5) { + d = ccell1(p, r); + } else { + d = ccell2(p, r); + } + + return d*scale; +} + +vec3 postProcess(vec3 col, vec2 q) { + //col = saturate(col); + col=pow(clamp(col,0.0,1.0),vec3(0.75)); + col=col*0.6+0.4*col*col*(3.0-2.0*col); // contrast + col=mix(col, vec3(dot(col, vec3(0.33))), -0.4); // satuation + col*=0.5+0.5*pow(19.0*q.x*q.y*(1.0-q.x)*(1.0-q.y),0.7); // vigneting + return col; +} + +void mainImage(out vec4 fragColor, in vec2 fragCoord, in vec2 resolution, in vec2 uv) { + vec2 q = fragCoord/resolution.xy; + vec2 p = -1.0 + 2.0*q; + p.x *= resolution.x/resolution.y; + float tm = iTime*0.1; + p += vec2(cos(tm), sin(tm*sqrt(0.5))); + float z = mix(0.5, 1.0, pcos(tm*sqrt(0.3))); + float aa = 4.0 / resolution.y; + + vec2 nn = vec2(0.0); + float d = df(p, z, nn); + + vec3 col = vec3(160.0)/vec3(255.0); + vec3 baseCol = vec3(0.3); + vec4 logoCol = vec4(baseCol, 1.0)*smoothstep(-aa, 0.0, -d); + col = mix(col, logoCol.xyz, pow(logoCol.w, 8.0)); + col += 0.4*pow(abs(sin(20.0*d)), 0.6); + + col = postProcess(col, q); + + fragColor = vec4(col, 1.0); +} diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml index d77a8f3b6c..cc9727481c 100644 --- a/demos/gtk-demo/demo.gresource.xml +++ b/demos/gtk-demo/demo.gresource.xml @@ -143,6 +143,7 @@ transition2.glsl transition3.glsl transition4.glsl + cogs2.glsl iconscroll.ui diff --git a/demos/gtk-demo/gltransition.c b/demos/gtk-demo/gltransition.c index c0e49cc552..32cba33457 100644 --- a/demos/gtk-demo/gltransition.c +++ b/demos/gtk-demo/gltransition.c @@ -109,10 +109,26 @@ new_shadertoy (const char *path) return toy; } +static gboolean +update_paintable (GtkWidget *widget, + GdkFrameClock *frame_clock, + gpointer user_data) +{ + GskShaderPaintable *paintable; + gint64 frame_time; + + paintable = GSK_SHADER_PAINTABLE (gtk_picture_get_paintable (GTK_PICTURE (widget))); + frame_time = gdk_frame_clock_get_frame_time (frame_clock); + gsk_shader_paintable_update_time (paintable, 0, frame_time); + + return G_SOURCE_CONTINUE; +} + static GtkWidget * -make_shader_stack (const char *name, - const char *resource_path, - GtkWidget *scale) +make_shader_stack (const char *name, + const char *resource_path, + GtkWidget *scale, + GdkFrameClock *frame_clock) { GtkWidget *stack, *child, *widget, *vbox, *hbox, *bin; GtkWidget *label, *button, *tv; @@ -124,6 +140,7 @@ make_shader_stack (const char *name, GBytes *bytes; GtkEventController *controller; GtkCssProvider *provider; + GdkPaintable *paintable; stack = gtk_shader_stack_new (); shader = gsk_gl_shader_new_from_resource (resource_path); @@ -134,6 +151,14 @@ make_shader_stack (const char *name, gtk_picture_set_can_shrink (GTK_PICTURE (child), TRUE); gtk_shader_stack_add_child (GTK_SHADER_STACK (stack), child); + shader = gsk_gl_shader_new_from_resource ("/gltransition/cogs2.glsl"); + paintable = gsk_shader_paintable_new (shader, NULL); + + child = gtk_picture_new_for_paintable (paintable); + gtk_widget_add_tick_callback (child, update_paintable, NULL, NULL); + 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); @@ -243,6 +268,7 @@ static GtkWidget * create_gltransition_window (GtkWidget *do_widget) { GtkWidget *window, *headerbar, *scale, *grid; + GdkFrameClock *frame_clock; window = gtk_window_new (); gtk_window_set_display (GTK_WINDOW (window), gtk_widget_get_display (do_widget)); @@ -270,17 +296,20 @@ create_gltransition_window (GtkWidget *do_widget) gtk_grid_set_row_homogeneous (GTK_GRID (grid), TRUE); gtk_grid_set_column_homogeneous (GTK_GRID (grid), TRUE); + gtk_widget_realize (window); + frame_clock = gtk_widget_get_frame_clock (window); + gtk_grid_attach (GTK_GRID (grid), - make_shader_stack ("Wind", "/gltransition/transition1.glsl", scale), + make_shader_stack ("Wind", "/gltransition/transition1.glsl", scale, frame_clock), 0, 0, 1, 1); gtk_grid_attach (GTK_GRID (grid), - make_shader_stack ("Radial", "/gltransition/transition2.glsl", scale), + make_shader_stack ("Radial", "/gltransition/transition2.glsl", scale, frame_clock), 1, 0, 1, 1); gtk_grid_attach (GTK_GRID (grid), - make_shader_stack ("Crosswarp", "/gltransition/transition3.glsl", scale), + make_shader_stack ("Crosswarp", "/gltransition/transition3.glsl", scale, frame_clock), 0, 1, 1, 1); gtk_grid_attach (GTK_GRID (grid), - make_shader_stack ("Kaleidoscope", "/gltransition/transition4.glsl", scale), + make_shader_stack ("Kaleidoscope", "/gltransition/transition4.glsl", scale, frame_clock), 1, 1, 1, 1); return window;