Glitch shader?

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
MordaBest
Prole
Posts: 4
Joined: Sun Feb 03, 2019 3:54 pm

Glitch shader?

Post by MordaBest » Sat Nov 09, 2019 4:26 pm

I've found this really neat glitch effect written with GLSL and was banging my head trying to apply it to LOVE since, but to no avail. Here's a link.
https://www.shadertoy.com/view/XtyXzW

The main problem is that it's written for 3D environment and all of it's rendering logic is tied into that. Any hints?

User avatar
raidho36
Party member
Posts: 1952
Joined: Mon Jun 17, 2013 12:00 pm

Re: Glitch shader?

Post by raidho36 » Sat Nov 09, 2019 8:11 pm

Shadertoy is not 3d nor uses non-standard OpenGL so you shouldn't have any problems. I indeed did not.

Code: Select all

local shader = [[
uniform vec3      iResolution;           // viewport resolution (in pixels)
uniform float     iTime;                 // shader playback time (in seconds)
uniform vec4      iMouse;                // mouse pixel coords. xy: current (if MLB down), zw: click


	#define MODEL_ROTATION vec2(.5, .5)
#define LIGHT_ROTATION vec2(.3, .8)
#define CAMERA_ROTATION vec2(.5, .67)

// Mouse control
// 0: Defaults
// 1: Model
// 2: Lighting
// 3: Camera
#define MOUSE_CONTROL 1

// Debugging
//#define NORMALS
//#define NO_GLITCH
//#define GLITCH_MASK


float time;

float round(float n) {
    return floor(n + .5);
}

vec2 round(vec2 n) {
    return floor(n + .5);
}

// --------------------------------------------------------
// HG_SDF
// https://www.shadertoy.com/view/Xs3GRB
// --------------------------------------------------------

#define PI 3.14159265359
#define PHI (1.618033988749895)
#define TAU 6.283185307179586

float vmax(vec3 v) {
    return max(max(v.x, v.y), v.z);
}

// Rotate around a coordinate axis (i.e. in a plane perpendicular to that axis) by angle <a>.
// Read like this: R(p.xz, a) rotates "x towards z".
// This is fast if <a> is a compile-time constant and slower (but still practical) if not.
void pR(inout vec2 p, float a) {
    p = cos(a)*p + sin(a)*vec2(p.y, -p.x);
}

// Plane with normal n (n is normalized) at some distance from the origin
float fPlane(vec3 p, vec3 n, float distanceFromOrigin) {
    return dot(p, n) + distanceFromOrigin;
}

// Box: correct distance to corners
float fBox(vec3 p, vec3 b) {
    vec3 d = abs(p) - b;
    return length(max(d, vec3(0))) + vmax(min(d, vec3(0)));
}


#define GDFVector3 normalize(vec3(1, 1, 1 ))
#define GDFVector4 normalize(vec3(-1, 1, 1))
#define GDFVector5 normalize(vec3(1, -1, 1))
#define GDFVector6 normalize(vec3(1, 1, -1))

#define GDFVector7 normalize(vec3(0, 1, PHI+1.))
#define GDFVector8 normalize(vec3(0, -1, PHI+1.))
#define GDFVector9 normalize(vec3(PHI+1., 0, 1))
#define GDFVector10 normalize(vec3(-PHI-1., 0, 1))
#define GDFVector11 normalize(vec3(1, PHI+1., 0))
#define GDFVector12 normalize(vec3(-1, PHI+1., 0))

#define GDFVector13 normalize(vec3(0, PHI, 1))
#define GDFVector14 normalize(vec3(0, -PHI, 1))
#define GDFVector15 normalize(vec3(1, 0, PHI))
#define GDFVector16 normalize(vec3(-1, 0, PHI))
#define GDFVector17 normalize(vec3(PHI, 1, 0))
#define GDFVector18 normalize(vec3(-PHI, 1, 0))

#define fGDFBegin float d = 0.;
#define fGDF(v) d = max(d, abs(dot(p, v)));
#define fGDFEnd return d - r;

float fDodecahedron(vec3 p, float r) {
    fGDFBegin
    fGDF(GDFVector13) fGDF(GDFVector14) fGDF(GDFVector15) fGDF(GDFVector16)
    fGDF(GDFVector17) fGDF(GDFVector18)
    fGDFEnd
}

float fIcosahedron(vec3 p, float r) {
    fGDFBegin
    fGDF(GDFVector3) fGDF(GDFVector4) fGDF(GDFVector5) fGDF(GDFVector6)
    fGDF(GDFVector7) fGDF(GDFVector8) fGDF(GDFVector9) fGDF(GDFVector10)
    fGDF(GDFVector11) fGDF(GDFVector12)
    fGDFEnd
}


// --------------------------------------------------------
// Rotation
// --------------------------------------------------------

mat3 sphericalMatrix(float theta, float phi) {
    float cx = cos(theta);
    float cy = cos(phi);
    float sx = sin(theta);
    float sy = sin(phi);
    return mat3(
        cy, -sy * -sx, -sy * cx,
        0, cx, sx,
        sy, cy * -sx, cy * cx
    );
}

mat3 mouseRotation(bool enable, vec2 xy) {
    if (enable) {
        vec2 mouse = iMouse.xy / iResolution.xy;

        if (mouse.x != 0. && mouse.y != 0.) {
            xy.x = mouse.x;
            xy.y = mouse.y;
        }
    }
    float rx, ry;

    rx = (xy.y + .5) * PI;
    ry = (-xy.x) * 2. * PI;

    return sphericalMatrix(rx, ry);
}

mat3 modelRotation() {
    mat3 m = mouseRotation(MOUSE_CONTROL==1, MODEL_ROTATION);
    return m;
}

mat3 lightRotation() {
    mat3 m = mouseRotation(MOUSE_CONTROL==2, LIGHT_ROTATION);
    return m;
}

mat3 cameraRotation() {
    mat3 m = mouseRotation(MOUSE_CONTROL==3, CAMERA_ROTATION);
    return m;
}


// --------------------------------------------------------
// Modelling
// --------------------------------------------------------

struct Material {
    vec3 albedo;
};

struct Model {
    float dist;
    Material material;
};

Material defaultMaterial = Material(
    vec3(.5)
);

Model newModel() {
    return Model(
        10000.,
        defaultMaterial
    );
}

const float modelSize = 1.2;

float blend(float y, float blend, float progress) {
    float a = (y / modelSize) + .5;
    a -= progress * (1. + blend) - blend * .5;
    a += blend / 2.;
    a /= blend;
    a = clamp(a, 0., 1.);
    a = smoothstep(0., 1., a);
    a = smoothstep(0., 1., a);
    return a;
}

float ShapeBlend(float y, float progress) {
    float shapeProgress = clamp(progress * 2. - .5, 0., 1.);
    float shapeBlend = blend(y, .8, shapeProgress);
    return shapeBlend;
}

float SpinBlend(float y, float progress) {
    return blend(y, 1.5, progress);
}

float Flip() {
	return round(mod(time, 1.));
}

float Progress() {
    float progress = mod(time*2., 1.);
    //progress = smoothstep(0., 1., progress);
    //progress = sin(progress * PI - PI/2.) * .5 + .5;
    return progress;
}

Model mainModel(vec3 p) {
    Model model = newModel();

	float progress = Progress();
    float flip = Flip();

    float spinBlend = SpinBlend(p.y, progress);
    pR(p.xz, spinBlend * PI / 2.);
    pR(p.xz, PI * -.5 * flip);

    float part1 = fDodecahedron(p, modelSize * .5);
    pR(p.xz, PI/2.);
    float part2 = fIcosahedron(p, modelSize * .5);

	float shapeBlend = ShapeBlend(p.y, progress);
    shapeBlend = mix(shapeBlend, 1. - shapeBlend, flip);
    float d = mix(part1, part2, shapeBlend);

    model.dist = d;
    model.material.albedo = mix(vec3(.03), vec3(.8), 1. - shapeBlend);

    return model;
}

Model glitchModel(vec3 p) {
    Model model = newModel();
    float progress = Progress();
	float band = ShapeBlend(p.y, progress);
    band = sin(band * PI);

   	float fadeBottom = clamp(1. - dot(p, vec3(0,1,0)), 0., 1.);
    band *= fadeBottom;

    float radius = modelSize / 2. + band * .2;
    model.dist = length(p) - radius;
    model.material.albedo = vec3(band);

    return model;
}

Model map( vec3 p , bool glitchMask){
    mat3 m = modelRotation();
    p *= m;
    pR(p.xz, -time*PI);
    if (glitchMask) {
    	return glitchModel(p);
    }
    Model model = mainModel(p);
    return model;
}


// --------------------------------------------------------
// LIGHTING
// https://www.shadertoy.com/view/Xds3zN
// --------------------------------------------------------

float softshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax )
{
    float res = 1.0;
    float t = mint;
    for( int i=0; i<16; i++ )
    {
        float h = map( ro + rd*t, false ).dist;
        res = min( res, 8.0*h/t );
        t += clamp( h, 0.02, 0.10 );
        if( h<0.00001 || t>tmax ) break;
    }
    return clamp( res, 0.0, 1.0 );
}

float calcAO( in vec3 pos, in vec3 nor )
{
    float occ = 0.0;
    float sca = 1.0;
    for( int i=0; i<5; i++ )
    {
        float hr = 0.01 + 0.12*float(i)/4.0;
        vec3 aopos =  nor * hr + pos;
        float dd = map( aopos, false ).dist;
        occ += -(dd-hr)*sca;
        sca *= 0.95;
    }
    return clamp( 1.0 - 3.0*occ, 0.0, 1.0 );
}

vec3 doLighting(Material material, vec3 pos, vec3 nor, vec3 ref, vec3 rd) {
    vec3 lightPos = vec3(0,0,-1);
    vec3 backLightPos = normalize(vec3(0,-.3,1));
    vec3 ambientPos = vec3(0,1,0);

    mat3 m = lightRotation();
    lightPos *= m;
    backLightPos *= m;

    float occ = calcAO( pos, nor );
    vec3  lig = lightPos;
    float amb = clamp((dot(nor, ambientPos) + 1.) / 2., 0., 1.);
    float dif = clamp((dot(nor, lig) + 1.) / 3., 0.0, 1.0 );
    float bac = pow(clamp(dot(nor, backLightPos), 0., 1.), 1.5);
    float fre = pow( clamp(1.0+dot(nor,rd),0.0,1.0), 2.0 );

    dif *= softshadow( pos, lig, 0.01, 2.5 ) * .5 + .5;

    vec3 lin = vec3(0.0);
    lin += 1.20*dif*vec3(.95,0.80,0.60);
    lin += 0.80*amb*vec3(0.50,0.70,.80)*occ;
    lin += 0.30*bac*vec3(0.25,0.25,0.25)*occ;
    lin += 0.20*fre*vec3(1.00,1.00,1.00)*occ;
    vec3 col = material.albedo*lin;

	float spe = clamp(dot(ref, lightPos), 0., 1.);
    spe = pow(spe, 2.) * .1;
    col += spe;

    return col;
}


// --------------------------------------------------------
// Ray Marching
// Adapted from: https://www.shadertoy.com/view/Xl2XWt
// --------------------------------------------------------

const float MAX_TRACE_DISTANCE = 30.; // max trace distance
const float INTERSECTION_PRECISION = .001; // precision of the intersection
const int NUM_OF_TRACE_STEPS = 100;
const float FUDGE_FACTOR = .9; // Default is 1, reduce to fix overshoots

struct CastRay {
    vec3 origin;
    vec3 direction;
    bool glitchMask;
};

struct Ray {
    vec3 origin;
    vec3 direction;
    float len;
};

struct Hit {
    Ray ray;
    Model model;
    vec3 pos;
    bool isBackground;
    vec3 normal;
    vec3 color;
};

vec3 calcNormal( in vec3 pos ){
    vec3 eps = vec3( 0.001, 0.0, 0.0 );
    vec3 nor = vec3(
        map(pos+eps.xyy, false).dist - map(pos-eps.xyy, false).dist,
        map(pos+eps.yxy, false).dist - map(pos-eps.yxy, false).dist,
        map(pos+eps.yyx, false).dist - map(pos-eps.yyx, false).dist );
    return normalize(nor);
}

Hit raymarch(CastRay castRay){

    float currentDist = INTERSECTION_PRECISION * 2.0;
    Model model;

    Ray ray = Ray(castRay.origin, castRay.direction, 0.);

    for( int i=0; i< NUM_OF_TRACE_STEPS ; i++ ){
        if (currentDist < INTERSECTION_PRECISION || ray.len > MAX_TRACE_DISTANCE) {
            break;
        }
        model = map(ray.origin + ray.direction * ray.len, castRay.glitchMask);
        currentDist = model.dist;
        ray.len += currentDist * FUDGE_FACTOR;
    }

    bool isBackground = false;
    vec3 pos = vec3(0);
    vec3 normal = vec3(0);
    vec3 color = vec3(0);

    if (ray.len > MAX_TRACE_DISTANCE) {
        isBackground = true;
    } else {
        pos = ray.origin + ray.direction * ray.len;
        normal = calcNormal(pos);
    }

    return Hit(ray, model, pos, isBackground, normal, color);
}


// --------------------------------------------------------
// Rendering
// Refraction from https://www.shadertoy.com/view/lsXGzH
// --------------------------------------------------------

void shadeSurface(inout Hit hit){

    vec3 color = vec3(.25);

    if (hit.isBackground) {
        hit.color = color;
        return;
    }

    #ifdef NORMALS
        color = hit.normal * 0.5 + 0.5;
    #else
    	vec3 ref = reflect(hit.ray.direction, hit.normal);
        color = doLighting(
            hit.model.material,
            hit.pos,
            hit.normal,
            ref,
            hit.ray.direction
        );
    #endif

    hit.color = color;
}


vec3 render(Hit hit){

    shadeSurface(hit);

    if (hit.isBackground) {
        return hit.color;
    }

    return hit.color;
}


// --------------------------------------------------------
// Camera
// https://www.shadertoy.com/view/Xl2XWt
// --------------------------------------------------------

mat3 calcLookAtMatrix( in vec3 ro, in vec3 ta, in float roll )
{
    vec3 ww = normalize( ta - ro );
    vec3 uu = normalize( cross(ww,vec3(sin(roll),cos(roll),0.0) ) );
    vec3 vv = normalize( cross(uu,ww));
    return mat3( uu, vv, ww );
}

void doCamera(out vec3 camPos, out vec3 camTar, out float camRoll, in float time, in vec2 mouse) {
    float dist = 3.;
    camRoll = 0.;
    camTar = vec3(0,0,0);
    camPos = vec3(0,0,-dist);
    camPos *= cameraRotation();
    camPos += camTar;
}

Hit raymarchPixel(vec2 p, bool glitchPass) {
    vec2 m = iMouse.xy / iResolution.xy;

    vec3 camPos = vec3( 0., 0., 2.);
    vec3 camTar = vec3( 0. , 0. , 0. );
    float camRoll = 0.;

    // camera movement
    doCamera(camPos, camTar, camRoll, iTime, m);

    // camera matrix
    mat3 camMat = calcLookAtMatrix( camPos, camTar, camRoll );  // 0.0 is the camera roll

    // create view ray
    float focalLength = 3.;
    vec3 rd = normalize( camMat * vec3(p.xy, focalLength) );

    Hit hit = raymarch(CastRay(camPos, rd, glitchPass));

    return hit;
}


// --------------------------------------------------------
// Gamma
// https://www.shadertoy.com/view/Xds3zN
// --------------------------------------------------------

const float GAMMA = 2.2;

vec3 gamma(vec3 color, float g) {
    return pow(color, vec3(g));
}

vec3 linearToScreen(vec3 linearRGB) {
    return gamma(linearRGB, 1.0 / GAMMA);
}


// --------------------------------------------------------
// Glitch core
// --------------------------------------------------------


float rand(vec2 co){
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

const float glitchScale = .5;

vec2 glitchCoord(vec2 p, vec2 gridSize) {
	vec2 coord = floor(p / gridSize) * gridSize;;
    coord += (gridSize / 2.);
    return coord;
}


struct GlitchSeed {
    vec2 seed;
    float prob;
};

float fBox2d(vec2 p, vec2 b) {
  vec2 d = abs(p) - b;
  return min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
}

GlitchSeed glitchSeed(vec2 p, float speed) {
    float seedTime = floor(time * speed);
    vec2 seed = vec2(
        1. + mod(seedTime / 100., 100.),
        1. + mod(seedTime, 100.)
    ) / 100.;
    seed += p;

    float prob = 0.;
    Hit hit = raymarchPixel(p, true);
    if ( ! hit.isBackground) {
    	prob = hit.model.material.albedo.x;
    }

    return GlitchSeed(seed, prob);
}

float shouldApply(GlitchSeed seed) {
    return round(
        mix(
            mix(rand(seed.seed), 1., seed.prob - .5),
            0.,
            (1. - seed.prob) * .5
        )
    );
}


// --------------------------------------------------------
// Glitch effects
// --------------------------------------------------------

// Swap

vec4 swapCoords(vec2 seed, vec2 groupSize, vec2 subGrid, vec2 blockSize) {
    vec2 rand2 = vec2(rand(seed), rand(seed+.1));
    vec2 range = subGrid - (blockSize - 1.);
    vec2 coord = floor(rand2 * range) / subGrid;
    vec2 bottomLeft = coord * groupSize;
    vec2 realBlockSize = (groupSize / subGrid) * blockSize;
    vec2 topRight = bottomLeft + realBlockSize;
    topRight -= groupSize / 2.;
    bottomLeft -= groupSize / 2.;
    return vec4(bottomLeft, topRight);
}

float isInBlock(vec2 pos, vec4 block) {
    vec2 a = sign(pos - block.xy);
    vec2 b = sign(block.zw - pos);
    return min(sign(a.x + a.y + b.x + b.y - 3.), 0.);
}

vec2 moveDiff(vec2 pos, vec4 swapA, vec4 swapB) {
    vec2 diff = swapB.xy - swapA.xy;
    return diff * isInBlock(pos, swapA);
}

void swapBlocks(inout vec2 xy, vec2 groupSize, vec2 subGrid, vec2 blockSize, vec2 seed, float apply) {

    vec2 groupOffset = glitchCoord(xy, groupSize);
    vec2 pos = xy - groupOffset;

    vec2 seedA = seed * groupOffset;
    vec2 seedB = seed * (groupOffset + .1);

    vec4 swapA = swapCoords(seedA, groupSize, subGrid, blockSize);
    vec4 swapB = swapCoords(seedB, groupSize, subGrid, blockSize);

    vec2 newPos = pos;
    newPos += moveDiff(pos, swapA, swapB) * apply;
    newPos += moveDiff(pos, swapB, swapA) * apply;
    pos = newPos;

    xy = pos + groupOffset;
}


// Static

void staticNoise(inout vec2 p, vec2 groupSize, float grainSize, float contrast) {
    GlitchSeed seedA = glitchSeed(glitchCoord(p, groupSize), 5.);
    seedA.prob *= .5;
    if (shouldApply(seedA) == 1.) {
        GlitchSeed seedB = glitchSeed(glitchCoord(p, vec2(grainSize)), 5.);
        vec2 offset = vec2(rand(seedB.seed), rand(seedB.seed + .1));
        offset = round(offset * 2. - 1.);
        offset *= contrast;
        p += offset;
    }
}


// Freeze time

void freezeTime(vec2 p, inout float time, vec2 groupSize, float speed) {
    GlitchSeed seed = glitchSeed(glitchCoord(p, groupSize), speed);
    //seed.prob *= .5;
    if (shouldApply(seed) == 1.) {
        float frozenTime = floor(time * speed) / speed;
        time = frozenTime;
    }
}


// --------------------------------------------------------
// Glitch compositions
// --------------------------------------------------------

void glitchSwap(inout vec2 p) {

    vec2 pp = p;

    float scale = glitchScale;
    float speed = 5.;

    vec2 groupSize;
    vec2 subGrid;
    vec2 blockSize;
    GlitchSeed seed;
    float apply;

    groupSize = vec2(.6) * scale;
    subGrid = vec2(2);
    blockSize = vec2(1);

    seed = glitchSeed(glitchCoord(p, groupSize), speed);
    apply = shouldApply(seed);
    swapBlocks(p, groupSize, subGrid, blockSize, seed.seed, apply);

    groupSize = vec2(.8) * scale;
    subGrid = vec2(3);
    blockSize = vec2(1);

    seed = glitchSeed(glitchCoord(p, groupSize), speed);
    apply = shouldApply(seed);
    swapBlocks(p, groupSize, subGrid, blockSize, seed.seed, apply);

    groupSize = vec2(.2) * scale;
    subGrid = vec2(6);
    blockSize = vec2(1);

    seed = glitchSeed(glitchCoord(p, groupSize), speed);
    float apply2 = shouldApply(seed);
    swapBlocks(p, groupSize, subGrid, blockSize, (seed.seed + 1.), apply * apply2);
    swapBlocks(p, groupSize, subGrid, blockSize, (seed.seed + 2.), apply * apply2);
    swapBlocks(p, groupSize, subGrid, blockSize, (seed.seed + 3.), apply * apply2);
    swapBlocks(p, groupSize, subGrid, blockSize, (seed.seed + 4.), apply * apply2);
    swapBlocks(p, groupSize, subGrid, blockSize, (seed.seed + 5.), apply * apply2);

    groupSize = vec2(1.2, .2) * scale;
    subGrid = vec2(9,2);
    blockSize = vec2(3,1);

    seed = glitchSeed(glitchCoord(p, groupSize), speed);
    apply = shouldApply(seed);
    swapBlocks(p, groupSize, subGrid, blockSize, seed.seed, apply);
}



void glitchStatic(inout vec2 p) {

    // Static
    //staticNoise(p, vec2(.25, .25/2.) * glitchScale, .005, 5.);

    // 8-bit
    staticNoise(p, vec2(.5, .25/2.) * glitchScale, .2 * glitchScale, 2.);
}

void glitchTime(vec2 p, inout float time) {
   freezeTime(p, time, vec2(.5) * glitchScale, 2.);
}

void glitchColor(vec2 p, inout vec3 color) {
    vec2 groupSize = vec2(.75,.125) * glitchScale;
    vec2 subGrid = vec2(0,6);
    float speed = 5.;
    GlitchSeed seed = glitchSeed(glitchCoord(p, groupSize), speed);
    seed.prob *= .3;
    if (shouldApply(seed) == 1.) {
        vec2 co = mod(p, groupSize) / groupSize;
        co *= subGrid;
        float a = max(co.x, co.y);
        //color.rgb *= vec3(
        //  min(floor(mod(a - 0., 3.)), 1.),
        //    min(floor(mod(a - 1., 3.)), 1.),
        //    min(floor(mod(a - 2., 3.)), 1.)
        //);

        color *= min(floor(mod(a, 2.)), 1.) * 10.;
    }
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    time = iTime;
    time /= 3.;
    time = mod(time, 1.);

    vec2 p = (-iResolution.xy + 2.0*fragCoord.xy)/iResolution.y;

    vec3 color;

    #ifdef GLITCH_MASK
    	float prob = glitchSeed(p, 10.).prob;
    	color = vec3(prob);
   	#else

        #ifndef NO_GLITCH
            glitchSwap(p);
            glitchTime(p, time);
            glitchStatic(p);
        #endif

        Hit hit = raymarchPixel(p, false);
        color = render(hit);

        #ifndef NO_GLITCH
            glitchColor(p, color);
        #endif

        #ifndef NORMALS
           color = linearToScreen(color);
        #endif

    #endif

    fragColor = vec4(color,1.0);
}

vec4 effect(vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords)
{
	vec4 c;
	mainImage ( c, screen_coords );
	return c;
}
]]

local t = 0

function love.load ( )
	shader = love.graphics.newShader ( shader )
end

function love.update ( dt )
	t = t + dt
end

function love.draw ( )
	love.graphics.setShader ( shader )
	shader:send ( "iTime", t )
	shader:send ( "iMouse", { love.mouse.getX ( ), love.mouse.getY ( ), love.mouse.isDown ( 1 ) and 1 or 0, love.mouse.isDown ( 2 ) and 1 or 0 } )
	shader:send ( "iResolution", { 800, 600, 0 } )
	love.graphics.rectangle ( "fill", 0, 0, 800, 600 )
	love.graphics.setShader ( )
	love.graphics.print ( love.timer.getFPS ( ), 10, 10 )
end

MordaBest
Prole
Posts: 4
Joined: Sun Feb 03, 2019 3:54 pm

Re: Glitch shader?

Post by MordaBest » Sun Nov 10, 2019 12:18 am

With original's author's help I was able to make this shader, that can be applied do any graphic element. You have to feed it time every frame for it to change, I used this:

Code: Select all

shader:send("iTime",os.clock())
And here's the shader itself.

Code: Select all

uniform float iTime;
	
float time;
	
float round(float n) {
	return floor(n + .5);
}

vec2 round(vec2 n) {
	return floor(n + .5);
}

float rand(vec2 co){
	return fract(dot(co.xy,vec2(12.9898,78.233)) * 43758.5453);
}

const float glitchScale = .2;

vec2 glitchCoord(vec2 p, vec2 gridSize) {
	vec2 coord = floor(p / gridSize) * gridSize;
	coord += (gridSize / 2.);
	return coord;
}

struct GlitchSeed {
	vec2 seed;
	float prob;
};

GlitchSeed glitchSeed(vec2 p, float speed) {
	float seedTime = floor(time * speed);
	vec2 seed = vec2(
		1. + mod(seedTime / 100., 100.),
		1. + mod(seedTime, 100.)
	) / 100.;
	seed += p;

	float prob = 1;

	return GlitchSeed(seed, prob);
}

float shouldApply(GlitchSeed seed) {
	return round(
		mix(
			mix(rand(seed.seed), 1., seed.prob - .5),
			0.,
			(1. - seed.prob) * .5
		)
	);
}

vec4 swapCoords(vec2 seed, vec2 groupSize, vec2 subGrid, vec2 blockSize) {
	vec2 rand2 = vec2(rand(seed), rand(seed+.1));
	vec2 range = subGrid - (blockSize - 1.);
	vec2 coord = floor(rand2 * range) / subGrid;
	vec2 bottomLeft = coord * groupSize;
	vec2 realBlockSize = (groupSize / subGrid) * blockSize;
	vec2 topRight = bottomLeft + realBlockSize;
	topRight -= groupSize / 2.;
	bottomLeft -= groupSize / 2.;
	return vec4(bottomLeft, topRight);
}

float isInBlock(vec2 pos, vec4 block) {
	vec2 a = sign(pos - block.xy);
	vec2 b = sign(block.zw - pos);
	return min(sign(a.x + a.y + b.x + b.y - 3.), 0.);
}

vec2 moveDiff(vec2 pos, vec4 swapA, vec4 swapB) {
	vec2 diff = swapB.xy - swapA.xy;
	return diff * isInBlock(pos, swapA);
}

void swapBlocks(inout vec2 xy, vec2 groupSize, vec2 subGrid, vec2 blockSize, vec2 seed, float apply) {
	vec2 groupOffset = glitchCoord(xy, groupSize);
	vec2 pos = xy - groupOffset;
	
	vec2 seedA = seed * groupOffset;
	vec2 seedB = seed * (groupOffset + .1);
		
	vec4 swapA = swapCoords(seedA, groupSize, subGrid, blockSize);
	vec4 swapB = swapCoords(seedB, groupSize, subGrid, blockSize);
	
	vec2 newPos = pos;
	newPos += moveDiff(pos, swapA, swapB) * apply;
	newPos += moveDiff(pos, swapB, swapA) * apply;
	pos = newPos;
			
	xy = pos + groupOffset;
}

void staticNoise(inout vec2 p, vec2 groupSize, float grainSize, float contrast) {
	GlitchSeed seedA = glitchSeed(glitchCoord(p, groupSize), 5.);
	seedA.prob *= .5;
	if (shouldApply(seedA) == 1.) {
		GlitchSeed seedB = glitchSeed(glitchCoord(p, vec2(grainSize)), 5.);
		vec2 offset = vec2(rand(seedB.seed), rand(seedB.seed + .1));
		offset = round(offset * 2. - 1.);
		offset *= contrast;
		p += offset;
	}
}

void freezeTime(vec2 p, inout float time, vec2 groupSize, float speed) {
	GlitchSeed seed = glitchSeed(glitchCoord(p, groupSize), speed);
	if (shouldApply(seed) == 1.) {
		float frozenTime = floor(time * speed) / speed;
		time = frozenTime;
	}
}

void glitchSwap(inout vec2 p) {
	vec2 pp = p;
	
	float scale = glitchScale;
	float speed = 25.;
		
	vec2 groupSize;
	vec2 subGrid;
	vec2 blockSize;    
	GlitchSeed seed;
	float apply;
		
	groupSize = vec2(.6) * scale;
	subGrid = vec2(2);
	blockSize = vec2(1);

	seed = glitchSeed(glitchCoord(p, groupSize), speed);
	apply = shouldApply(seed);
	swapBlocks(p, groupSize, subGrid, blockSize, seed.seed, apply);
		
	groupSize = vec2(.8) * scale;
	subGrid = vec2(3);
	blockSize = vec2(1);
		
	seed = glitchSeed(glitchCoord(p, groupSize), speed);
	apply = shouldApply(seed);
	swapBlocks(p, groupSize, subGrid, blockSize, seed.seed, apply);

	groupSize = vec2(.2) * scale;
	subGrid = vec2(6);
	blockSize = vec2(1);
		
	seed = glitchSeed(glitchCoord(p, groupSize), speed);
	float apply2 = shouldApply(seed);
	swapBlocks(p, groupSize, subGrid, blockSize, (seed.seed + 1.), apply * apply2);
	swapBlocks(p, groupSize, subGrid, blockSize, (seed.seed + 2.), apply * apply2);
	swapBlocks(p, groupSize, subGrid, blockSize, (seed.seed + 3.), apply * apply2);
	swapBlocks(p, groupSize, subGrid, blockSize, (seed.seed + 4.), apply * apply2);
	swapBlocks(p, groupSize, subGrid, blockSize, (seed.seed + 5.), apply * apply2);
	
	groupSize = vec2(1.2, .2) * scale;
	subGrid = vec2(9,2);
	blockSize = vec2(3,1);
	
	seed = glitchSeed(glitchCoord(p, groupSize), speed);
	apply = shouldApply(seed);
	swapBlocks(p, groupSize, subGrid, blockSize, seed.seed, apply);
}

void glitchStatic(inout vec2 p) {
	staticNoise(p, vec2(.5, .25/2.) * glitchScale, .2 * glitchScale, 2.);
}

void glitchTime(vec2 p, inout float time) {
	freezeTime(p, time, vec2(.5) * glitchScale, 2.);
}

vec4 effect (vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords) {
	time = iTime;
	time /= 3.;
	time = mod(time, 1.);
	
	glitchSwap(texture_coords);
	glitchTime(texture_coords, time);
	glitchStatic(texture_coords);

	return Texel(tex,texture_coords);
}
The speed of glitching is determined in void glitchSwap, where you can tweak float speed.
The decay of glitching is determined in GlitchSeed glitchSeed, where you can change float prob. Default is 1, which should land it approximately in a middle of a processed texture. You can move intensity center elsewhere, for example 1-p.y moves it to top, p.x moves it to right.

Post Reply

Who is online

Users browsing this forum: No registered users and 9 guests