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