mirror of
https://github.com/libretro/slang-shaders.git
synced 2024-11-27 02:20:41 +00:00
646 lines
13 KiB
Plaintext
646 lines
13 KiB
Plaintext
#version 450
|
|
// Ray * Bert - 2013-03-08
|
|
// https://www.shadertoy.com/view/4sl3RH
|
|
|
|
// --------
|
|
// Ray*Bert!!! ( @!#?@! )
|
|
// --| |---
|
|
// |/
|
|
// By Dave Hoskins
|
|
// Finished for now... may come back later to put the other characters in.
|
|
// V. 0.8 - Added colour changes and a fixed path for Q*Bert.
|
|
// V. 0.7 - Better fur and ambient lighting.
|
|
// V. 0.64 - Moving Q*bert.Behaves differently on different machines! :(
|
|
// V. 0.63 - Pupil watching.
|
|
// V. 0.62 - Asynchronous bounces.
|
|
// V. 0.61 - Fur.
|
|
// V. 0.60 - In the middle of getting old Q to move about.
|
|
|
|
|
|
layout(std140, set = 0, binding = 0) uniform UBO
|
|
{
|
|
mat4 MVP;
|
|
vec4 OutputSize;
|
|
vec4 OriginalSize;
|
|
vec4 SourceSize;
|
|
uint FrameCount;
|
|
} global;
|
|
|
|
#pragma stage vertex
|
|
layout(location = 0) in vec4 Position;
|
|
layout(location = 1) in vec2 TexCoord;
|
|
layout(location = 0) out vec2 vTexCoord;
|
|
const vec2 madd = vec2(0.5, 0.5);
|
|
void main()
|
|
{
|
|
gl_Position = global.MVP * Position;
|
|
vTexCoord = gl_Position.xy;
|
|
}
|
|
|
|
#pragma stage fragment
|
|
layout(location = 0) in vec2 vTexCoord;
|
|
layout(location = 0) out vec4 FragColor;
|
|
float iGlobalTime = float(global.FrameCount)*0.025;
|
|
vec2 iResolution = global.OutputSize.xy;
|
|
|
|
#define REFLECTIONS_ON
|
|
|
|
#define PI 3.1415926535
|
|
|
|
vec3 areaPlane = normalize(vec3(-1.0, 1.0, 1.0));
|
|
vec3 lightDir = normalize(vec3(-1437.0, 1743.0, 430.0));
|
|
|
|
vec3 QbertPos;
|
|
float QbertRotation;
|
|
vec3 balls[3];
|
|
float squish[4];
|
|
float radius[3];
|
|
|
|
const vec3 ballStart1 = vec3(-.0, 2.6, -1.);
|
|
const vec3 ballStart2 = vec3( 1.0, 2.6, 0.);
|
|
|
|
|
|
const vec3 addLeft = vec3(-1.0, -1.0, 0.0);
|
|
const vec3 addRight = vec3(.0, -1.0, 1.0);
|
|
|
|
const vec3 QbertStart = vec3(-3.0, -1.3, .00);
|
|
|
|
|
|
float time;
|
|
|
|
float rand( float n )
|
|
{
|
|
return fract(sin(n*1233.23)*43758.5453);
|
|
}
|
|
|
|
float hash( float n )
|
|
{
|
|
return fract(sin(n)*43758.5453123);
|
|
}
|
|
|
|
float noise( in vec3 x )
|
|
{
|
|
vec3 p = floor(x);
|
|
vec3 f = fract(x);
|
|
|
|
f = f*f*(3.0-2.0*f);
|
|
|
|
float n = p.x + p.y*57.0 + 113.0*p.z;
|
|
|
|
float res = mix(mix(mix( hash(n+ 0.0), hash(n+ 1.0),f.x),
|
|
mix( hash(n+ 57.0), hash(n+ 58.0),f.x),f.y),
|
|
mix(mix( hash(n+113.0), hash(n+114.0),f.x),
|
|
mix( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z);
|
|
return res;
|
|
}
|
|
|
|
|
|
vec2 rotate2D(vec2 p, float a)
|
|
{
|
|
float sa = sin(a);
|
|
float ca = cos(a);
|
|
vec2 r;
|
|
r.x = ca*p.x + sa*p.y;
|
|
r.y = -sa*p.x + ca*p.y;
|
|
return r;
|
|
}
|
|
|
|
// Finding the movement location at a set time. Loopy IF-tastic!!...
|
|
void GetLocation(int i, float s, out vec3 outPos1, out vec3 outPos2)
|
|
{
|
|
int end = int(mod(s, 8.0))-1;
|
|
float r = floor(s/8.0) + float(i*547);
|
|
vec3 pos;
|
|
if (rand(float(r)) > .5)
|
|
{
|
|
pos = ballStart1;
|
|
}else
|
|
{
|
|
pos = ballStart2;
|
|
}
|
|
|
|
for (int t = 0; t < 9; t++)
|
|
{
|
|
if (t == 0)
|
|
{
|
|
outPos1 = pos + vec3(0.0, 3.5, 0.0);
|
|
}
|
|
if (t == end)
|
|
{
|
|
outPos1 = pos;
|
|
}
|
|
if (t == end+1)
|
|
{
|
|
outPos2 = pos;
|
|
if (t == 7)
|
|
{
|
|
outPos2 = outPos1 + vec3(0.0, -8.5, 0.0);
|
|
}
|
|
}
|
|
|
|
if (rand(float(t)+r) > .5)
|
|
{
|
|
pos += addLeft;
|
|
}else
|
|
{
|
|
pos += addRight;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
vec3 MoveQbert(int n, in vec3 pos, out float dir)
|
|
{
|
|
if (n == 0)
|
|
{
|
|
pos -= addLeft;
|
|
dir = -90.0;
|
|
}else
|
|
if (n == 1)
|
|
{
|
|
pos -= addRight;
|
|
dir = 180.0;
|
|
}else
|
|
if (n == 3)
|
|
{
|
|
pos += addLeft;
|
|
dir = 90.0;
|
|
}else
|
|
{
|
|
pos += addRight;
|
|
dir = 0.0;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
int DirTable[19];
|
|
float GetQbertLocation(float s, out vec3 out1, out vec3 out2)
|
|
{
|
|
int end = int(mod(s, 18.0));
|
|
float r = floor(s/18.0);
|
|
vec3 pos = QbertStart;
|
|
float dir = 0.0;
|
|
float outDir;
|
|
vec3 newPos;
|
|
|
|
for (int t = 0; t < 19; t++)
|
|
{
|
|
if (t == end)
|
|
{
|
|
out1 = pos;
|
|
}
|
|
if (t == end+1)
|
|
{
|
|
out2 = pos;
|
|
outDir = dir / 180.0 * PI;
|
|
}
|
|
int val = DirTable[t];
|
|
pos = MoveQbert(val, pos, dir);
|
|
}
|
|
return outDir;
|
|
|
|
}
|
|
|
|
//----------------------- Distance Estimation fields -------------------
|
|
float deTorus( vec3 p, vec2 t )
|
|
{
|
|
vec2 q = vec2(length(p.xy)-t.x,p.z);
|
|
return length(q)-t.y;
|
|
}
|
|
|
|
float deQBertEyes(vec3 p)
|
|
{
|
|
float t = squish[3];//clamp(fra*.25, 0.0, 0.2)-.2;
|
|
p.y -= t;
|
|
p.xz = rotate2D(p.xz, QbertRotation);
|
|
vec3 pos = p + vec3(-.08, -.17, -.18);
|
|
|
|
float d = length(pos)-.12;
|
|
pos = p + vec3(.08, -.17, -.18);
|
|
d = min(d, length(pos)-.12);
|
|
return d;
|
|
}
|
|
|
|
float deQBert(vec3 pos)
|
|
{
|
|
pos.xz = rotate2D(pos.xz, QbertRotation);
|
|
vec3 p = pos;
|
|
|
|
// Torso...
|
|
float t = squish[3];//clamp(fra*.25, 0.0, 0.2)-.2;
|
|
p.y -=t;
|
|
p.y *= .9;
|
|
float d = length(p)-.32;
|
|
p = pos * vec3(1.0, 1.0, .3);
|
|
p.z -= .1;
|
|
p.y -= t;
|
|
d = min(d, deTorus(p, vec2(.07, .06)));
|
|
|
|
// Vertical legs...
|
|
p = (pos * vec3(1.0, .4, 1.0))- vec3(-.13, -.2, -.1);
|
|
d = min(d, length(p)-.06);
|
|
p = (pos * vec3(1.0, .4, 1.0))- vec3(.13, -.2, -.1);
|
|
d = min(d, length(p)-.06);
|
|
// Feet...
|
|
p = (pos * vec3(.4, .8, .3))- vec3(-.05, -.5, .01);
|
|
d = min(d, length(p)-.03);
|
|
p = (pos * vec3(.4, .8, .3))- vec3(.05, -.5, .01);
|
|
d = min(d, length(p)-.03);
|
|
return d;
|
|
}
|
|
|
|
float deBall(vec3 p, float s)
|
|
{
|
|
return length(p)-s;
|
|
}
|
|
|
|
float dePlane(vec3 p, vec3 planeN)
|
|
{
|
|
return dot(p, planeN);
|
|
}
|
|
|
|
float PlayArea(vec3 p)
|
|
{
|
|
float d;
|
|
d = dePlane(p, areaPlane);
|
|
return d;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
float Scene(in vec3 p, out int which)
|
|
{
|
|
float d = 1000.0, f;
|
|
|
|
for (int i =0; i < 3; i++)
|
|
{
|
|
vec3 pos = p-balls[i];
|
|
|
|
// Squish it...
|
|
pos.xz /= squish[i];
|
|
pos.y *= squish[i];
|
|
f = deBall(pos, radius[i]);
|
|
if (f < d)
|
|
{
|
|
d = f;
|
|
which = i;
|
|
}
|
|
}
|
|
|
|
f = deQBert(p - QbertPos);
|
|
if (f < d)
|
|
{
|
|
d = f;
|
|
which = 4;
|
|
}
|
|
|
|
f = deQBertEyes(p - QbertPos);
|
|
if (f < d)
|
|
{
|
|
d = f;
|
|
which = 5;
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
//------------------------------ Lighting ------------------------------------
|
|
// Calculate scene normal
|
|
vec3 SceneNormal(vec3 pos )
|
|
{
|
|
float eps = 0.001;
|
|
vec3 n;
|
|
int m;
|
|
float d = Scene(pos, m);
|
|
n.x = Scene( vec3(pos.x+eps, pos.y, pos.z), m ) - d;
|
|
n.y = Scene( vec3(pos.x, pos.y+eps, pos.z),m ) - d;
|
|
n.z = Scene( vec3(pos.x, pos.y, pos.z+eps),m ) - d;
|
|
return normalize(n);
|
|
}
|
|
|
|
vec3 HueGradient(float t)
|
|
{
|
|
t += .4;
|
|
vec3 p = abs(fract(t + vec3(1.0, 2.0 / 3.0, 1.0 / 3.0)) * 6.0 - 3.0);
|
|
return (clamp(p - 1.0, 0.0, 1.0));
|
|
}
|
|
// Colouring in the fragments...
|
|
vec3 LightCubes(vec3 pos, vec3 n)
|
|
{
|
|
vec3 qpos = QbertStart;
|
|
float dir;
|
|
ivec3 ipos = ivec3(floor(pos+.5));
|
|
float foundHit = 0.0;
|
|
int end = int(mod(time*.8, 18.0));
|
|
|
|
vec3 areaColour = vec3(.53, .7, .85);
|
|
for (int t = 0; t < 18; t++)
|
|
{
|
|
qpos = MoveQbert(DirTable[t], qpos, dir);
|
|
ivec3 ip = ivec3(floor(qpos+.5));
|
|
if (ip == ipos && n.y >= .8)
|
|
{
|
|
if (t >= end)
|
|
foundHit = .5;
|
|
else
|
|
foundHit = 1.5;
|
|
}
|
|
}
|
|
if (foundHit > 0.0) areaColour = HueGradient((floor((time*.8/18.0))+foundHit)/4.23);
|
|
|
|
float diff = dot(n, lightDir);
|
|
diff = max(diff, 0.0);
|
|
return diff * areaColour;
|
|
}
|
|
|
|
vec3 LightCharacters(vec3 pos, vec3 norm, vec3 dir, int m)
|
|
{
|
|
float specular = 0.0;
|
|
vec3 ballColour;
|
|
float specSize = 8.0;
|
|
vec3 specColour = vec3(1.0, 1.0, 1.0);
|
|
if (m == 7)
|
|
{
|
|
ballColour = vec3(1.0, 1.0, 1.0);
|
|
specColour = vec3(0.0, 0.0, 0.0);
|
|
specSize = 2.0;
|
|
}
|
|
|
|
if (m == 6)
|
|
{
|
|
norm += (noise((pos-QbertPos)*42.0)*.5)-.5;
|
|
ballColour = vec3(1.2, 0.42, 0.);
|
|
}
|
|
else
|
|
{
|
|
vec3 reflect = ((-2.0*(dot(dir, norm))*norm)+dir);
|
|
specular = pow(max(dot(reflect, lightDir), 0.0), specSize);
|
|
|
|
if (m == 2)
|
|
{
|
|
ballColour = vec3(1.0, 0.0, 1.0);
|
|
}else
|
|
if (m == 3)
|
|
{
|
|
ballColour = vec3(1.0, 0.0, 0.0);
|
|
}else
|
|
if (m == 4)
|
|
{
|
|
ballColour = vec3(0.0, 1.0, 0.0);
|
|
}
|
|
}
|
|
|
|
float diff = dot(norm, lightDir);
|
|
diff = max(diff, 0.3);
|
|
|
|
return mix(diff * ballColour, specColour, specular);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
float DistanceFields(vec3 ro, vec3 rd, out int hit, out vec3 pos)
|
|
{
|
|
float len = .0;
|
|
float d;
|
|
hit = 0;
|
|
int m = 0;
|
|
for (int st = 0; st < 22; st++)
|
|
{
|
|
pos = ro + rd * len;
|
|
d = Scene(pos, m);
|
|
if (d < 0.01)
|
|
{
|
|
hit = m+1;
|
|
break;
|
|
}
|
|
len += d;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Voxel grid search that I found in 1994 in Graphics Gems IV - "Voxel Traversal along a 3D Line"!
|
|
// This (Amanatides & Woo) varient is from another shader on here, but with some calculations removed,
|
|
// and the distance value fixed.
|
|
// I had to use voxels as standard distance fields don't work for perfectly aligned cubes.
|
|
|
|
float VoxelTrace(vec3 ro, vec3 rd, out bool hit, out vec3 hitNormal, out vec3 pos)
|
|
{
|
|
const int maxSteps = 41;
|
|
vec3 voxel = floor(ro)+.5001;
|
|
vec3 step = sign(rd);
|
|
//voxel = voxel - vec3(rd.x > 0.0, rd.y > 0.0, rd.z > 0.0);
|
|
vec3 tMax = (voxel - ro) / rd;
|
|
vec3 tDelta = 1.0 / abs(rd);
|
|
vec3 hitVoxel = voxel;
|
|
|
|
|
|
hit = false;
|
|
|
|
float hitT = 0.0;
|
|
for(int i=0; i < maxSteps; i++)
|
|
{
|
|
if (!hit)
|
|
{
|
|
float d = PlayArea(voxel);
|
|
if (d <= 0.0 && !hit)
|
|
{
|
|
hit = true;
|
|
hitVoxel = voxel;
|
|
break;
|
|
}
|
|
bool c1 = tMax.x < tMax.y;
|
|
bool c2 = tMax.x < tMax.z;
|
|
bool c3 = tMax.y < tMax.z;
|
|
if (c1 && c2)
|
|
{
|
|
voxel.x += step.x;
|
|
tMax.x += tDelta.x;
|
|
if (!hit)
|
|
{
|
|
hitNormal = vec3(-step.x, 0.0, 0.0);
|
|
hitT = tMax.x-tDelta.x;
|
|
|
|
}
|
|
} else if (c3 && !c1)
|
|
{
|
|
voxel.y += step.y;
|
|
tMax.y += tDelta.y;
|
|
if (!hit)
|
|
{
|
|
hitNormal = vec3(0.0, -step.y, 0.0);
|
|
hitT = tMax.y-tDelta.y;
|
|
}
|
|
} else
|
|
{
|
|
voxel.z += step.z;
|
|
tMax.z += tDelta.z;
|
|
if (!hit)
|
|
{
|
|
hitNormal = vec3(0.0, 0.0, -step.z);
|
|
hitT = tMax.z-tDelta.z;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hit)
|
|
{
|
|
if (hitVoxel.x > 1.75 || hitVoxel.z < -1.75 || hitVoxel.y < -3.5)
|
|
{
|
|
hit = false;
|
|
hitT = 1000.0;
|
|
}
|
|
}
|
|
pos = ro + hitT * rd;
|
|
return hitT;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// Do all the ray casting for voxels and normal distance fields...
|
|
float TraceEverything(vec3 ro,vec3 rd, out int material, out vec3 hitNormal, out vec3 pos)
|
|
{
|
|
bool hit1;
|
|
int hit2;
|
|
vec3 pos2;
|
|
float dist = VoxelTrace(ro, rd, hit1, hitNormal, pos);
|
|
float dist2 = DistanceFields(ro, rd, hit2, pos2);
|
|
if (hit2 > 0 && dist2 < dist)
|
|
{
|
|
hitNormal = SceneNormal(pos2);
|
|
pos = pos2;
|
|
material = hit2+1;
|
|
}else
|
|
if (hit1)
|
|
{
|
|
material = 1;
|
|
}else
|
|
{
|
|
material = 0;
|
|
}
|
|
return dist;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
int TraceShadow(vec3 ro, vec3 rd)
|
|
{
|
|
int hit;
|
|
vec3 pos;
|
|
float dist2 = DistanceFields(ro, rd, hit, pos);
|
|
return hit;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
vec3 DoMaterialRGB(int m, vec3 pos, vec3 norm, vec3 rd)
|
|
{
|
|
vec3 rgb;
|
|
if (m == 1)
|
|
{
|
|
rgb = LightCubes(pos, norm);
|
|
}else
|
|
if (m >= 2)
|
|
{
|
|
rgb = LightCharacters(pos, norm, rd, m);
|
|
}else
|
|
{
|
|
rgb = mix(vec3(.0, .05, .1), vec3(0.4, 0.6, .8), abs(rd.y*1.));
|
|
}
|
|
return rgb;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
|
{
|
|
// For Q*Bert's movement to loop properly they needed a direction table...
|
|
// 1 0
|
|
// \ /
|
|
// @
|
|
// / \
|
|
// 3 2
|
|
|
|
DirTable[0] = 0; DirTable[1] = 2; DirTable[2] = 0; DirTable[3] = 2;
|
|
DirTable[4] = 0; DirTable[5] = 2; DirTable[6] = 0; DirTable[7] = 1; DirTable[8] = 1; DirTable[9] = 3; DirTable[10] = 1;
|
|
DirTable[11] = 1; DirTable[12] = 3; DirTable[13] = 3; DirTable[14] = 3;
|
|
DirTable[15] = 3; DirTable[16] = 2; DirTable[17] = 0; DirTable[18] = 0;
|
|
|
|
time = iGlobalTime*1.8878;;
|
|
|
|
vec2 pixel = (fragCoord.xy / iResolution.xy)*2.0-1.0;
|
|
float asp = iResolution.x / iResolution.y;
|
|
vec3 rd = normalize(vec3(asp*pixel.x, pixel.y-1.23, -3.5));
|
|
vec3 ro = vec3(-14.0, 6.3, 14.0);
|
|
// Rotate it to look at the plane
|
|
rd.xz = rotate2D(rd.xz, -(PI/4.0));
|
|
|
|
float sec, fra;
|
|
vec3 rgb;
|
|
vec3 norm, pos;
|
|
int material;
|
|
vec3 pos1, pos2;
|
|
radius[0] = .4;
|
|
radius[1] = .33;
|
|
radius[2] = .25;
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
sec = time * float(i+3) * .2;
|
|
fra = fract(sec);
|
|
sec = floor(sec);
|
|
|
|
GetLocation(i, sec, pos1, pos2);
|
|
balls[i] = mix(pos1, pos2, fra);
|
|
balls[i].y += sin(fra*PI);
|
|
if (balls[i].y > -2.5)
|
|
{
|
|
float t = clamp(-fra*.75+1.3, 1.0, 1.3);
|
|
squish[i] = t;
|
|
}else
|
|
{
|
|
squish[i] = 1.0;
|
|
}
|
|
}
|
|
|
|
|
|
fra = fract(time*.8);
|
|
sec = floor(time*.8);
|
|
QbertRotation = GetQbertLocation(sec, pos1, pos2);
|
|
|
|
float t = clamp(fra*.5, 0.0, 0.2)-.3;
|
|
squish[3] = t;
|
|
QbertPos = mix(pos1, pos2, fra);
|
|
QbertPos.y += sin(fra*PI)+.5;
|
|
|
|
|
|
TraceEverything(ro, rd, material, norm, pos);
|
|
rgb = DoMaterialRGB(material, pos, norm, rd);
|
|
|
|
// Do the shadow casting of the balls...
|
|
if (dot(norm, lightDir) > 0.1 && material > 0 && TraceShadow(pos+lightDir*.04, lightDir) != 0)
|
|
{
|
|
rgb *= .4;
|
|
}
|
|
|
|
|
|
#ifdef REFLECTIONS_ON
|
|
if (material > 0 && material != 6)
|
|
{
|
|
ro = pos;
|
|
rd = ((-2.0*(dot(rd, norm))*norm)+rd);
|
|
TraceEverything(ro+rd*0.04, rd, material, norm, pos);
|
|
rgb = mix(rgb, DoMaterialRGB(material, pos, norm, rd), .13);
|
|
}
|
|
#endif
|
|
|
|
// Curve the brightness a little...
|
|
rgb = pow(rgb, vec3(.8, .8, .8));
|
|
|
|
fragColor=vec4(rgb, 1.0);
|
|
}
|
|
|
|
void main(void)
|
|
{
|
|
//just some shit to wrap shadertoy's stuff
|
|
vec2 FragmentCoord = vTexCoord.xy*global.OutputSize.xy;
|
|
FragmentCoord.y = -FragmentCoord.y;
|
|
mainImage(FragColor,FragmentCoord);
|
|
}
|