Skip to content

Instantly share code, notes, and snippets.

@tschundler
Forked from glslioadmin/TEMPLATE.glsl
Last active August 30, 2016 07:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tschundler/597954724feda999b2cf2b5518d3edec to your computer and use it in GitHub Desktop.
Save tschundler/597954724feda999b2cf2b5518d3edec to your computer and use it in GitHub Desktop.
GLSL.io Transition (v1)
#ifdef GL_ES
precision highp float;
#endif
uniform sampler2D from, to;
uniform float progress;
uniform vec2 resolution;
// Tunable parameters
uniform float zoom; // How much to zoom (out) for the effect ~ 0.5 - 1.0
uniform float corner_radius; // Corner radius as a fraction of the image height
///////////////////////////////////////////////////////////////////////////////
// Stereo Viewer Toy Transition by Ted Schundler //
// BSD License: Free for use and modification by anyone with credit //
// //
// Inspired by ViewMaster / Image3D image viewer devices. //
// This effect is similar to what you see when you press the device's lever. //
// There is a quick zoom in / out to make the transition "valid" for GLSL.io //
///////////////////////////////////////////////////////////////////////////////
const vec4 black = vec4(0.0, 0.0, 0.0, 1.0);
const vec2 c00 = vec2(0.0, 0.0); // the four corner points
const vec2 c01 = vec2(0.0, 1.0);
const vec2 c11 = vec2(1.0, 1.0);
const vec2 c10 = vec2(1.0, 0.0);
// Check if a point is within a given corner
bool in_corner(vec2 p, vec2 corner, vec2 radius) {
// determine the direction we want to be filled
vec2 axis = (c11 - corner) - corner;
// warp the point so we are always testing the bottom left point with the
// circle centered on the origin
p = p - (corner + axis * radius);
p *= axis / radius;
return (p.x > 0.0 && p.y > -1.0) || (p.y > 0.0 && p.x > -1.0) || dot(p, p) < 1.0;
}
// Check all four corners
// return a float for v2 for anti-aliasing?
bool test_rounded_mask(vec2 p, vec2 corner_size) {
return
in_corner(p, c00, corner_size) &&
in_corner(p, c01, corner_size) &&
in_corner(p, c10, corner_size) &&
in_corner(p, c11, corner_size);
}
// Screen blend mode - https://en.wikipedia.org/wiki/Blend_modes
// This more closely approximates what you see than linear blending
vec4 screen(vec4 a, vec4 b) {
return 1.0 - (1.0 - a) * (1.0 -b);
}
// Given RGBA, find a value that when screened with itself
// will yield the original value.
vec4 unscreen(vec4 c) {
return 1.0 - sqrt(1.0 - c);
}
// Grab a pixel, only if it isn't masked out by the rounded corners
vec4 sample_with_corners(sampler2D tex, vec2 p, vec2 corner_size) {
p = (p - 0.5) / zoom + 0.5;
if (!test_rounded_mask(p, corner_size)) {
return black;
}
return unscreen(texture2D(tex, p));
}
// special sampling used when zooming - extra zoom parameter and don't unscreen
vec4 simple_sample_with_corners(sampler2D tex, vec2 p, vec2 corner_size, float zoom_amt) {
p = (p - 0.5) / (1.0 - zoom_amt + zoom * zoom_amt) + 0.5;
if (!test_rounded_mask(p, corner_size)) {
return black;
}
return texture2D(tex, p);
}
// Basic 2D affine transform matrix helpers
// These really shouldn't be used in a fragment shader - I should work out the
// the math for a translate & rotate function as a pair of dot products instead
mat3 rotate2d(float angle, float aspect) {
float s = sin(angle);
float c = cos(angle);
return mat3(
c, s ,0.0,
-s, c, 0.0,
0.0, 0.0, 1.0);
}
mat3 translate2d(float x, float y) {
return mat3(
1.0, 0.0, 0,
0.0, 1.0, 0,
-x, -y, 1.0);
}
mat3 scale2d(float x, float y) {
return mat3(
x, 0.0, 0,
0.0, y, 0,
0, 0, 1.0);
}
// Split an image and rotate one up and one down along off screen pivot points
vec4 get_cross_rotated(vec3 p3, float angle, vec2 corner_size, float aspect) {
angle = angle * angle; // easing
angle /= 2.4; // works out to be a good number of radians
mat3 center_and_scale = translate2d(-0.5, -0.5) * scale2d(1.0, aspect);
mat3 unscale_and_uncenter = scale2d(1.0, 1.0/aspect) * translate2d(0.5,0.5);
mat3 slide_left = translate2d(-2.0,0.0);
mat3 slide_right = translate2d(2.0,0.0);
mat3 rotate = rotate2d(angle, aspect);
mat3 op_a = center_and_scale * slide_right * rotate * slide_left * unscale_and_uncenter;
mat3 op_b = center_and_scale * slide_left * rotate * slide_right * unscale_and_uncenter;
vec4 a = sample_with_corners(from, (op_a * p3).xy, corner_size);
vec4 b = sample_with_corners(from, (op_b * p3).xy, corner_size);
return screen(a, b);
}
// Image stays put, but this time move two masks
vec4 get_cross_masked(vec3 p3, float angle, vec2 corner_size, float aspect) {
angle = 1.0 - angle;
angle = angle * angle; // easing
angle /= 2.4;
vec4 img;
mat3 center_and_scale = translate2d(-0.5, -0.5) * scale2d(1.0, aspect);
mat3 unscale_and_uncenter = scale2d(1.0 / zoom, 1.0 / (zoom * aspect)) * translate2d(0.5,0.5);
mat3 slide_left = translate2d(-2.0,0.0);
mat3 slide_right = translate2d(2.0,0.0);
mat3 rotate = rotate2d(angle, aspect);
mat3 op_a = center_and_scale * slide_right * rotate * slide_left * unscale_and_uncenter;
mat3 op_b = center_and_scale * slide_left * rotate * slide_right * unscale_and_uncenter;
bool mask_a = test_rounded_mask((op_a * p3).xy, corner_size);
bool mask_b = test_rounded_mask((op_b * p3).xy, corner_size);
if (mask_a || mask_b) {
img = sample_with_corners(to, p3.xy, corner_size);
return screen(mask_a ? img : black, mask_b ? img : black);
} else {
return black;
}
}
void main() {
float a;
vec2 p = gl_FragCoord.xy / resolution.xy;
vec3 p3 = vec3(p.xy, 1.0); // for 2D matrix transforms
float aspect = resolution.x / resolution.y;
// corner is warped to represent to size after mapping to 1.0, 1.0
vec2 corner_size = vec2(corner_radius / aspect, corner_radius);
if (progress <= 0.0) {
// 0.0: start with the base frame always
gl_FragColor = texture2D(from, p);
} else if (progress < 0.1) {
// 0.0-0.1: zoom out and add rounded corners
a = progress / 0.1;
gl_FragColor = simple_sample_with_corners(from, p, corner_size * a, a);
} else if (progress < 0.48) {
// 0.1-0.48: Split original image apart
a = (progress - 0.1)/0.38;
gl_FragColor = get_cross_rotated(p3, a, corner_size, aspect);
} else if (progress < 0.9) {
// 0.48-0.52: black
// 0.52 - 0.9: unmask new image
gl_FragColor = get_cross_masked(p3, (progress - 0.52)/0.38, corner_size, aspect);
} else if (progress < 1.0) {
// zoom out and add rounded corners
a = (1.0 - progress) / 0.1;
gl_FragColor = simple_sample_with_corners(to, p, corner_size * a, a);
} else {
// 1.0 end with base frame
gl_FragColor = texture2D(to, p);
}
}
{"zoom":0.88,"corner_radius":0.22}
Copyright (c) 2016, Theodore K Schundler
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment