Preview
precision highp float;
// COMMON DEFINITIONS
#define EPS 0.002
#define FLOAT_MAX float(0xffffffffu)
#define MOD(A, B) (A - B * floor(A / B))
#define MIN2(A) (min(A.x, A.y))
#define MIN3(A) (min(A.x, min(A.y, A.z)))
#define CROSS(X, Y) vec3(X.y*Y.z - X.z*Y.y, X.z*Y.x - X.x*Y.z, X.x*Y.y - X.y*Y.x)
#define SATURATE(A) clamp(A, 0.0, 1.0)
#define PI (3.14159265359)
#define TAU (6.28318530718)
#define OO vec2(0.0, 0.0)
#define IO vec2(1.0, 0.0)
#define OI vec2(0.0, 1.0)
#define II vec2(1.0, 1.0)
#define JO vec2(-1.0, 0.0)
#define OJ vec2(0.0, -1.0)
#define JJ vec2(-1.0, -1.0)
#define IJ vec2(1.0, -1.0)
#define JI vec2(-1.0, 1.0)
#define OOO vec3(0.0, 0.0, 0.0)
#define IOO vec3(1.0, 0.0, 0.0)
#define OIO vec3(0.0, 1.0, 0.0)
#define OOI vec3(0.0, 0.0, 1.0)
#define IOI vec3(1.0, 0.0, 1.0)
#define IIO vec3(1.0, 1.0, 0.0)
#define OII vec3(0.0, 1.0, 1.0)
#define III vec3(1.0, 1.0, 1.0)
#define JOO vec3(-1.0, 0.0, 0.0)
#define OJO vec3(0.0, -1.0, 0.0)
#define OOJ vec3(0.0, 0.0, -1.0)
#define JJO vec3(-1.0, -1.0, 0.0)
#define JOJ vec3(-1.0, 0.0, -1.0)
#define OJJ vec3(0.0, -1.0, -1.0)
#define JJJ vec3(-1.0, -1.0, -1.0)
#define IJJ vec3(1.0, -1.0, -1.0)
#define JIJ vec3(-1.0, 1.0, -1.0)
#define JJI vec3(-1.0, -1.0, 1.0)
#define IIJ vec3(1.0, 1.0, -1.0)
#define IJI vec3(1.0, -1.0, 1.0)
#define JII vec3(-1.0, 1.0, 1.0)
#define IOJ vec3(1.0, 0.0, -1.0)
#define JIO vec3(-1.0, 1.0, 0.0)
#define IJO vec3(1.0, -1.0, 0.0)
//TRANSFORMATIONS
mat3 TransformBasis(vec3 y, vec3 z)
{
vec3 x = normalize(CROSS(y, z));
y = CROSS(z, x);
return mat3(x, y, z);
}
mat2 rotate2d(float a) {
float s = sin(a);
float c = cos(a);
return mat2(c, -s, s, c);
}
//DISTANCE FUNCTIONS
float sdSphere(vec3 p, float r)
{
return length(p) - r;
}
float sdBox( vec3 p, vec3 b )
{
vec3 q = abs(p) - b;
return length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0);
}
float sdStairs(vec2 p)
{
vec2 pf = vec2(p.x + p.y, p.x + p.y) * 0.5;
vec2 d = p - vec2(floor(pf.x), ceil(pf.y));
vec2 d2 = p - vec2(floor(pf.x + 0.5), floor(pf.y + 0.5));
float d3 = length(vec2(min(d.x, 0.0), max(d.y, 0.0)));
float d4 = length(vec2(max(d2.x, 0.0), min(d2.y, 0.0)));
return d3 - d4;
}
float sdStairs(vec2 p, float h)
{
p.xy = p.y < p.x ? p.yx : p.xy;
return sdStairs(p - vec2(0.0, h));
}
float sdStairs(vec3 p, float h, float w)
{
float x = abs(p.x) - w;
float d = sdStairs(p.zy, h);
return max(x, d);
}
//INFO
struct Surface {
int surfaceId;
int objectId;
float distance;
};
struct Material {
vec3 baseColor;
float roughness;
vec3 emission;
};
Surface minSurface(Surface a, Surface b)
{
if (a.distance < b.distance)
{
return a;
}
return b;
}
//HASH FUNCTIONS
uint Pcg(uint v)
{
uint state = v * 747796405u + 2891336453u;
uint word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u;
return (word >> 22u) ^ word;
}
uvec2 Pcg2d(uvec2 v)
{
v = v * 1664525u + 1013904223u;
v.x += v.y * 1664525u;
v.y += v.x * 1664525u;
v = v ^ (v>>16u);
v.x += v.y * 1664525u;
v.y += v.x * 1664525u;
v = v ^ (v>>16u);
return v;
}
uvec3 Pcg3d(uvec3 v)
{
v = v * 1664525u + 1013904223u;
v.x += v.y*v.z;
v.y += v.z*v.x;
v.z += v.x*v.y;
v ^= v >> 16u;
v.x += v.y*v.z;
v.y += v.z*v.x;
v.z += v.x*v.y;
return v;
}
uvec4 Pcg4d(uvec4 v)
{
v = v * 1664525u + 1013904223u;
v.x += v.y*v.w;
v.y += v.z*v.x;
v.z += v.x*v.y;
v.w += v.y*v.z;
v ^= v >> 16u;
v.x += v.y*v.w;
v.y += v.z*v.x;
v.z += v.x*v.y;
v.w += v.y*v.z;
return v;
}
float Pcg01(uint v) { return float(Pcg(v)) / FLOAT_MAX; }
vec2 Pcg01(uvec2 v) { return vec2(Pcg2d(v)) / FLOAT_MAX; }
vec3 Pcg01(uvec3 v) { return vec3(Pcg3d(v)) / FLOAT_MAX; }
vec4 Pcg01(uvec4 v) { return vec4(Pcg4d(v)) / FLOAT_MAX; }
float Pcg01(int v) { return Pcg01(uint(v)); }
vec2 Pcg01(ivec2 v) { return Pcg01(uvec2(v)); }
vec3 Pcg01(ivec3 v) { return Pcg01(uvec3(v)); }
vec4 Pcg01(ivec4 v) { return Pcg01(uvec4(v)); }
float Pcg01(float v) { return Pcg01(floatBitsToUint(v)); }
vec2 Pcg01(vec2 v) { return Pcg01(floatBitsToUint(v)); }
vec3 Pcg01(vec3 v) { return Pcg01(floatBitsToUint(v)); }
vec4 Pcg01(vec4 v) { return Pcg01(floatBitsToUint(v)); }
//BRDF FUNCTIONS
vec3 Lambert(vec3 baseColor, vec3 N, vec3 L)
{
return baseColor / dot(N, L);
}
vec3 FresnelSchlick(float VdotH, vec3 F0) {
// return F0 + (1.0 - F0) * pow(1.0 - VdotH, 5.0);
return F0 + (1.0 - F0) * exp2((-5.55473 * VdotH - 6.98316) * VdotH);
}
vec3 GGX(vec3 N, vec3 V, vec3 L, float roughness, vec3 baseColor)
{
vec3 H = normalize(L + V);
float NdotH = max(dot(N, H), 0.0);
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float VdotH = max(dot(V, H), 0.0);
float r = (roughness + 1.0);
float k = (r * r) / 8.0;
vec3 F = FresnelSchlick(VdotH, baseColor);
return F * VdotH / (NdotH * (NdotV * (1.0 - k) + k) * (NdotL * (1.0 - k) + k));
}
//SAMPLING FUNCTIONS
vec3 SampleSphere(vec2 xi)
{
float a = xi.x * PI * 2.0;
float z = xi.y * 2.0 - 1.0;
float r = sqrt(1.0 - z * z);
return vec3(r * cos(a), r * sin(a), z);
}
vec3 SampleHemiSphere(vec2 xi, vec3 dir)
{
vec3 v = SampleSphere(xi);
return dot(dir, v) < 0.0 ? -v : v;
}
vec3 ImportanceSampleGGX(vec2 xi, float roughness, vec3 n, vec3 v)
{
float a = roughness * roughness;
float phi = 2.0 * PI * xi.x;
float cosTheta = sqrt((1.0 - xi.y) / (1.0 + (a * a - 1.0) * xi.y));
float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
vec3 h = vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
vec3 up = abs(n.z) < 0.999 ? vec3(0, 0, 1) : vec3(1, 0, 0);
vec3 tangentX = CROSS(up, n);
vec3 tangentY = CROSS(n, tangentX);
return reflect(v, tangentX * h.x + tangentY * h.y + n * h.z);
}
vec3 ImportanceSampleLambert(vec2 xi, vec3 n)
{
float phi = 2.0 * PI * xi.x;
float cosTheta = sqrt(1.0f - xi.x);
float sinTheta = sqrt(xi.y);
vec3 h = vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
vec3 up = abs(n.z) < 0.999 ? vec3(0, 0, 1) : vec3(1, 0, 0);
vec3 tangentX = CROSS(up, n);
vec3 tangentY = CROSS(n, tangentX);
return tangentX * h.x + tangentY * h.y + n * h.z;
}
uniform vec2 resolution;
uniform float time;
uniform sampler2D backbuffer;
const float dof = 1.5;
const int marchingStep = 70;
const float maxDistance = 400.0;
const float stairsHeight = 10.5;
const float stairsWidth = 11.0;
const float stairsTilingSize = 150.0;
const int bounceLimit = 2;
const int iterMax = 2;
const float wallWidth = 0.48;
const int ballNum = 15;
const float ballRadius = 5.0;
const float deltaTime = 0.05;
const float gravity = 1.0;
const float attraction = 10.0;
const float reflection = 10.0;
const float friction = -20.0;
const float randomize = 1.0;
const vec3 ballPosMax = vec3(200, 150, 400);
const vec3 ballPosMin = vec3(-200, -150, 75);
const vec3 ballVelMin = vec3(-100, -100, -100);
const vec3 ballVelMax = vec3(100, 100, 100);
vec3 ballPos[ballNum];
vec3 ballVel[ballNum];
vec3 cameraPos, cameraDir, cameraUp;
const float bpm = 84.0;
float elapsedTime;
float beatTime;
float sbeatTime;
const int phaseNum = 15;
const int phaseBPM[phaseNum + 1] = int[](0, 8, 16, 20, 24, 32, 36, 40, 48, 64, 72, 80, 96, 112, 128, 144);
float phasePeriod[phaseNum + 1];
float phaseFrag[phaseNum];
float phaseTime = 0.0;
void CalcBeatTime()
{
float scaledTime = elapsedTime * bpm / 60.0;
beatTime = floor(scaledTime);
sbeatTime = fract(scaledTime);
sbeatTime = beatTime + pow(sbeatTime, 20.0);
}
void CalcPhase()
{
phaseTime = 0.0;
for(int i = 0; i < phaseNum; i++){
phasePeriod[i + 1] = float(phaseBPM[i + 1]) / bpm * 60.0;
phaseFrag[i] = step(phasePeriod[i], elapsedTime) * step(elapsedTime, phasePeriod[i + 1]);
phaseTime += phaseFrag[i] * (elapsedTime - phasePeriod[i]);
}
}
void CalcCameraParams(){
if(phaseFrag[10] < 0.5) cameraUp = vec3(0.0, 1.0, 0.0);
if(phaseFrag[10] > 0.5) cameraUp = vec3(0.0, cos(-phaseTime * 0.1 + PI * 0.25), sin(-phaseTime * 0.1 + PI * 0.25));
if(phaseFrag[0] > 0.5) cameraDir = vec3(0.0, 0.0, 1.0);
if(phaseFrag[1] > 0.5) cameraDir = vec3(cos(phaseTime * 0.1), 0.0, sin(phaseTime * 0.1));
if(phaseFrag[2] > 0.5) cameraDir = vec3(cos(phaseTime * 0.1 + PI * 0.75), 0.0, sin(phaseTime * 0.1 + PI * 0.75));
if(phaseFrag[3] > 0.5) cameraDir = vec3(cos(-phaseTime * 0.07 + PI), sin(-phaseTime * 0.08 + PI), sin(-phaseTime * 0.07 + PI));
if(phaseFrag[4] > 0.5) cameraDir = vec3(-1.0, 0.2, 0.8);
if(phaseFrag[5] > 0.5) cameraDir = vec3(-1.0, 0.0, 1.0);
if(phaseFrag[6] > 0.5) cameraDir = vec3(-1.5, sin(-phaseTime * 0.08 + PI), 1.0);
if(phaseFrag[7] > 0.5) cameraDir = vec3(1.0, sin(-phaseTime * 0.08 + PI), 1.0);
if(phaseFrag[8] > 0.5) cameraDir = vec3(0.0, 0.0, 1.0);
if(phaseFrag[9] > 0.5) cameraDir = vec3(cos(phaseTime * 0.2 + PI * 0.25), 0.0, sin(phaseTime * 0.2 + PI * 0.25));
if(phaseFrag[10] > 0.5) cameraDir = vec3(0.0, cos(-phaseTime * 0.1 + PI * 0.6), sin(-phaseTime * 0.1 + PI * 0.6));
if(phaseFrag[11] > 0.5) cameraDir = vec3(0.0, 0.0, 1.0);
if(phaseFrag[12] > 0.5) cameraDir = vec3(cos(-phaseTime * 0.1 + PI * 0.25), 0.0, sin(-phaseTime * 0.1 + PI * 0.25));
if(phaseFrag[13] > 0.5) cameraDir = vec3(vec3(cos(phaseTime * 0.1 + PI * 0.5), -sin(phaseTime * 0.1 + PI * 0.5), sin(phaseTime * 0.1 + PI * 0.5)));
if(phaseFrag[14] > 0.5) cameraDir = vec3(cos(phaseTime * 0.1 + PI * 1.5), -0.5, sin(phaseTime * 0.1 + PI * 1.5));
if(phaseFrag[0] > 0.5) cameraPos = (vec3(0.0, -20.0, 15.0) + cameraDir * phaseTime * 0.8);
if(phaseFrag[1] > 0.5) cameraPos = (vec3(0.0, -20.0, 15.0));
if(phaseFrag[2] > 0.5) cameraPos = (vec3(0.0, -20.0, 15.0));
if(phaseFrag[3] > 0.5) cameraPos = (vec3(0.0, -20.0, 15.0));
if(phaseFrag[4] > 0.5) cameraPos = (vec3(4.0, -62.0, 1.0));
if(phaseFrag[5] > 0.5) cameraPos = (vec3(0.0, -50.0 + phaseTime * 1.0, -15.0 + phaseTime * 1.0));
if(phaseFrag[6] > 0.5) cameraPos = (vec3(4.0, -40.0, 1.0));
if(phaseFrag[7] > 0.5) cameraPos = (vec3(2.0, -30.0, 1.0));
if(phaseFrag[8] > 0.5) cameraPos = (vec3(0.0, -10.0 + phaseTime * 1.0, 25.0 + phaseTime * 1.0));
if(phaseFrag[9] > 0.5) cameraPos = (vec3(0.0, 20.0, 55.0));
if(phaseFrag[10] > 0.5) cameraPos = (vec3(0.0, 20.0, 55.0));
if(phaseFrag[11] > 0.5) cameraPos = (vec3(0.0, 20.0, 55.0 + phaseTime * 1.5));
if(phaseFrag[12] > 0.5) cameraPos = (vec3(0.0, 15.0, 225.0) - cameraDir * 50.0);
if(phaseFrag[13] > 0.5) cameraPos = (vec3(0.0, 15.0, 225.0) - cameraDir * 80.0);
if(phaseFrag[14] > 0.5) cameraPos = (vec3(0.0, 25.0, 75.0) - cameraDir * 130.0);
}
float sdTruchetStairs(vec3 p, float s, float t)
{
float hs = s * 0.5;
vec3 p1 = mix(p - hs * IIO, p + hs * JOJ, t);
vec3 p2 = mix(p.zyx * JII + hs * IOJ, p.yzx + hs * JIO, t);
vec3 p3 = mix(p.yxz * JII + hs * IIO, p.zyx * JII + hs * IJO, t);
float d1 = sdStairs(p1 - OII * sbeatTime * 3.0 * step(phasePeriod[8], elapsedTime), stairsHeight, stairsWidth);
float d2 = sdStairs(p2 - OII * sbeatTime * 3.0 * step(phasePeriod[8], elapsedTime), stairsHeight, stairsWidth);
float d3 = sdStairs(p3 - OII * sbeatTime * 3.0 * step(phasePeriod[8], elapsedTime), stairsHeight, stairsWidth);
return min(min(d1, d2), d3);
}
float sdTruchetTiledStairs(vec3 p, float s){
float hs = s * 0.5;
vec3 pf = MOD(p, s);
vec3 pi = p - pf;
float s1 = Pcg01(pi + vec3(0, 38, 0)).x;
float s2 = Pcg01(pi + vec3(hs, 0, 0)).x;
pf.xz = s1 < 0.25 ? (pf - hs).zx * JI + hs:
s1 < 0.5 ? (pf - hs).xz * JJ + hs:
s1 < 0.75 ? (pf - hs).zx * IJ + hs:
pf.xz;
return sdTruchetStairs(pf, s, step(s2, 0.5));
}
#define STAIRS_MAP(p) sdTruchetTiledStairs(p - vec3(stairsTilingSize * 0.5, stairsTilingSize * 0.5, stairsTilingSize * 0.5), stairsTilingSize)
vec3 GetStairsGrad(vec3 p)
{
const float e = EPS;
const vec2 k = vec2(1, -1);
return k.xyy * STAIRS_MAP(p + k.xyy * e) +
k.yyx * STAIRS_MAP(p + k.yyx * e) +
k.yxy * STAIRS_MAP(p + k.yxy * e) +
k.xxx * STAIRS_MAP(p + k.xxx * e);
}
Surface WallMap(vec3 p)
{
p.y += (phaseTime * 10.0 * Pcg01(int(p.z))) * phaseFrag[14];
ivec3 seed = ivec3(0.0, p.y, p.z);
vec3 hash = Pcg01(seed);
vec3 p1 = vec3(p.x, fract(p.y) - 0.5, fract(p.z) - 0.5);
float dx =
SATURATE(-(abs((floor(-p.y) + floor(p.z)) * 0.1 + hash.x * 0.4 - 6.2) - 1.0)) * pow(abs(sin(phaseTime / phasePeriod[1] * 1.0 * PI)), 4.0) * phaseFrag[0] +
SATURATE(-(abs((floor(-p.y) + floor(p.z)) * 0.1 + hash.x * 0.4 - 7.5 + phaseTime) - 1.0)) * phaseFrag[1] +
SATURATE(-(abs((floor(-p.y) + floor(p.z)) * 0.05 + hash.x * 0.4 - 10.0 + phaseTime * 1.6) - 1.0)) * phaseFrag[1] +
SATURATE(-(abs((floor(-p.y) + floor(p.z)) * 0.1 + hash.x * 1.5 - 7.5 + phaseTime) - 1.0)) * phaseFrag[2] +
SATURATE(-(abs((floor(-p.y) + floor(p.z)) * 0.1 + hash.x * 1.5 - 5.0 + phaseTime) - 1.0)) * phaseFrag[3] +
SATURATE(abs(sin(length(floor(vec3(0.0, p.y + 60.0, p.z))) * 0.2 + hash.x * 0.6 - phaseTime * 1.5) * 1.5) - 0.5) * phaseFrag[4] +
step(Pcg01(ivec4(vec4(p.x, p.y, floor(p.z) * 0.3 * Pcg01(int(sbeatTime)), sbeatTime * 10.0))).x, 0.3) * (phaseFrag[5] + phaseFrag[6] + phaseFrag[7]) +
mix(
step(Pcg01(ivec4(vec4(p.x, p.y, floor(p.z) * 0.3 * Pcg01(int(sbeatTime)), sbeatTime * 10.0))).x, 0.3),
SATURATE(sin(sbeatTime * 2.0 + (hash.x + hash.y) * PI) - 0.3),
SATURATE(phaseTime / phasePeriod[9] * 1.3)
) * phaseFrag[8] +
SATURATE(sin(sbeatTime * 2.0 + (hash.x + hash.y) * PI) - 0.3) * (phaseFrag[9] + phaseFrag[10] + phaseFrag[11]) +
3.0 * (phaseFrag[12] + phaseFrag[13] + phaseFrag[14])
;
float d = dx * wallWidth * 2.0;
vec3 delta = IOO * (d - (stairsWidth + wallWidth));
vec3 bSize = III * wallWidth * 0.8;
vec3 pSizeV = bSize * vec3(1.0, 0.15, 1.0);
vec3 pSizeH = bSize * vec3(1.0, 1.0, 0.15);
float sdWall1 = sdBox(p1 - delta, wallWidth * III);
sdWall1 = max(sdWall1,
-mix(
min(sdBox(p1 - wallWidth * IOO - delta, pSizeV), sdBox(p1 - wallWidth * IOO - delta, pSizeH)),
sdBox(p1 - wallWidth * IOO - delta, bSize), 1.0 - SATURATE(dx)));
sdWall1 = max(sdWall1, sdBox(p, stairsTilingSize * 0.5 * III));
float sdWall2 = sdBox(p1 + delta, wallWidth * III);
sdWall2 = max(sdWall2,
-mix(
min(sdBox(p1 + wallWidth * IOO + delta, pSizeV), sdBox(p1 + wallWidth * IOO + delta, pSizeH)),
sdBox(p1 + wallWidth * IOO + delta, bSize), 1.0 - SATURATE(dx)));
sdWall2 = max(sdWall2, sdBox(p, stairsTilingSize * 0.5 * III));
Surface wall1 = Surface(2, 0, sdWall1);
Surface wall2 = Surface(2, 1, sdWall2);
return minSurface(wall1, wall2);
}
void LoadBallParams(vec2 resolution)
{
for(int i = 0; i < ballNum; i++)
{
vec2 uv1 = (vec2(i, 0) + vec2(0.5, 0.5)) / resolution;
vec2 uv2 = (vec2(i, 1) + vec2(0.5, 0.5)) / resolution;
vec3 p = texture(backbuffer, uv1).xyz;
vec3 v = texture(backbuffer, uv2).xyz;
ballPos[i] = p * (ballPosMax - ballPosMin) + ballPosMin;
ballVel[i] = v * (ballVelMax - ballVelMin) + ballVelMin;
}
}
void SaveBallParams(ivec2 fragCoord, inout vec4 col)
{
for(int i = 0; i < ballNum; i++)
{
vec3 vel = ballVel[i];
vec3 pos = ballPos[i];
vec3 rand = Pcg01(vec4(pos.xz, i, elapsedTime)).xyz * 2.0 - 1.0;
float d = STAIRS_MAP(pos);
vec3 g = GetStairsGrad(pos);
vec3 norm = length(g) < 0.001 ? OIO : normalize(g);
vec3 up = abs(norm.z) < 0.999 ? OOI : IOO;
vec3 bitangent = CROSS(up, norm);
vec3 tangent = CROSS(norm, bitangent);
vel = d < ballRadius ?
reflection * norm - friction * tangent + randomize * rand :
vel + (-OIO * gravity - norm * attraction) * deltaTime;
pos += vel * deltaTime;
if(elapsedTime < phasePeriod[11]||
pos.x < ballPosMin.x || pos.x > ballPosMax.x ||
pos.y < ballPosMin.y || pos.y > ballPosMax.y ||
pos.z < ballPosMin.z || pos.z > ballPosMax.z){
pos = mix(ballPosMin, ballPosMax, rand * 0.2 + vec3(0.4, 0.6, 0.5));
vel = OOO;
}
vel = SATURATE((vel - ballVelMin) / (ballVelMax - ballVelMin));
pos = SATURATE((pos - ballPosMin) / (ballPosMax - ballPosMin));
if(fragCoord.x == i)
{
if(fragCoord.y == 0) { col = vec4(pos, 0.0); }
else if(fragCoord.y == 1) { col = vec4(vel, 0.0); }
}
}
}
Surface Map(vec3 p)
{
float sdStairs = STAIRS_MAP(p);
Surface s = Surface(0, 0, sdStairs);
vec3 ballPosCenter = (ballPosMax + ballPosMin) * 0.5;
vec3 ballArea = ballPosMax - ballPosMin;
s = minSurface(s, WallMap(p));
vec3 p1 = p - vec3(0.0, 19.0, 75.0);
p1.x -= 4.0;
p1.xz = rotate2d(mix(0.0, PI * 0.75, SATURATE((elapsedTime - phasePeriod[11] - 3.0) * 0.15))) * p1.xz;
p1.x += 4.0;
Surface door = Surface(3, 0, sdBox(p1, vec3(4.0, 9.0, wallWidth * 0.5)));
s = minSurface(s, door);
Surface doorWall = Surface(4, 0, max(
sdBox(p - vec3(0.0, 40.0, stairsTilingSize * 0.5), vec3(stairsWidth * 1.2, 30.0, wallWidth)),
-sdBox(p - vec3(0.0, 19.0, 75.0), vec3(4.0, 9.0, wallWidth * 1.1)))
);
s = minSurface(s, doorWall);
Surface monitor = Surface(1, 0, sdBox(p - vec3(0.0, 32.0, 73.0), vec3(5.333, 3.0, wallWidth * 0.5)));
s = minSurface(s, monitor);
Surface room = Surface(5, 0, max(
sdBox(p - ballPosCenter, ballArea * 0.5),
-sdBox(p - ballPosCenter + vec3(0.0, 0.0, 20.0), ballArea * 0.5 - vec3(5.0, 5.0, 5.0)))
);
s = minSurface(s, room);
for(int i = 0; i < ballNum; i++){
float sd = sdSphere(p - ballPos[i], ballRadius);
Surface ball = Surface(6, i, sd);
s = minSurface(s, ball);
}
return s;
}
#define LIMIT_MARCHING_DISTANCE(D,RD,RP) mix(D, min(MIN3(((1.0 * step(0.0, RD) - MOD(RP, 1.0)) / RD)) + EPS, D), \
step(RP.y, stairsTilingSize * 0.5) * step(-stairsTilingSize * 0.5, RP.y) * step(RP.z, stairsTilingSize * 0.5) * step(-stairsTilingSize * 0.5, RP.z) * \
(step(RP.x, -stairsWidth - wallWidth + wallWidth * 8.0) * step(-stairsWidth - wallWidth - wallWidth * 8.0, RP.x) + step(RP.x, stairsWidth + wallWidth + wallWidth * 8.0) * step(stairsWidth + wallWidth - wallWidth * 8.0, RP.x))\
)
#define MAP(P) Map(P)
bool RayMarching(vec3 ro, vec3 rd, int stepCount, float maxDistance, out vec3 rp, out Surface s)
{
rp = ro;
float rl = 0.0;
for (int i = 0; i < stepCount; i++)
{
s = MAP(rp);
float d = s.distance;
if (abs(d) < EPS){ return true; }
if (rl > maxDistance){ break; }
d = LIMIT_MARCHING_DISTANCE(d, rd, rp);
rl += d;
rp = ro + rd * rl;
if(elapsedTime < -1.0) break;
}
return false;
}
vec3 GetGrad(vec3 p)
{
const float e = EPS;
const vec2 k = vec2(1, -1);
return k.xyy * MAP(p + k.xyy * e).distance +
k.yyx * MAP(p + k.yyx * e).distance +
k.yxy * MAP(p + k.yxy * e).distance +
k.xxx * MAP(p + k.xxx * e).distance;
}
vec3 GetNormal(vec3 p)
{
return normalize(GetGrad(p));
}
Material GetMaterial(Surface s, vec3 p)
{
Material m = Material(III, 0.0, OOO);
vec3 defaultColor = mix(
mix(vec3(0.7, 0.8, 1.0), vec3(0.6, 0.05, 0.1), SATURATE(elapsedTime - phasePeriod[9] - 3.0)),
vec3(0.7, 0.8, 1.0), SATURATE(elapsedTime - phasePeriod[10] - 7.0));
if(s.surfaceId == 0) //Stairs
{
ivec3 seed = ivec3(p.x, p.y, p.z);
vec3 hash = Pcg01(seed);
m.baseColor = defaultColor;
m.roughness = mix(0.01, 0.25, hash.x);
m.emission = OOO;
return m;
}
else if(s.surfaceId == 1) //Monitor
{
float mask = step(sin(p.x * 2.0 + sbeatTime * 3.0 - p.y * 2.0), 0.0);
int seed = int((p.x * 2.0 + sbeatTime * 3.0 - p.y * 2.0) / PI);
float hash = Pcg01(seed);
vec3 color =
mix(vec3(1.0, 0.7, 0.8),
mix(vec3(0.7, 0.8, 1.0),
vec3(0.7, 1.0, 0.8),
step(hash, 0.33)), step(hash, 0.66));
color = mix(defaultColor, color, SATURATE(elapsedTime - phasePeriod[10] - 7.0));
m.baseColor = OOO;
m.roughness = 1.0;
m.emission = mask * color;
return m;
}
else if(s.surfaceId == 2) //Wall
{
float mask = s.objectId == 0 ? SATURATE(p.x + stairsWidth) : SATURATE(- p.x + stairsWidth);
mask *= 0.6;
ivec2 seed = ivec2(p.y, p.z);
vec2 hash = Pcg01(seed);
vec3 color =
mix(vec3(1.0, 0.7, 0.8),
mix(vec3(0.7, 0.8, 1.0),
vec3(0.7, 1.0, 0.8),
step(hash.x, 0.33)), step(hash.x, 0.66));
color = mix(defaultColor, color, SATURATE(elapsedTime - phasePeriod[10] - 7.0));
m.baseColor = color;
m.roughness = 0.4;
m.emission = mix(vec3(0.0, 0.0, 0.0), color, mask);
return m;
}
else if(s.surfaceId == 3) //door
{
m.baseColor = III;
m.roughness = 0.01;
m.emission = OOO;
return m;
}
else if(s.surfaceId == 4) //door wall
{
ivec2 seed = ivec2(p.x, p.y);
vec2 hash = Pcg01(seed);
m.baseColor = III;
m.roughness = mix(0.02, 0.98, hash.x * hash.y);
m.emission = OOO;
return m;
}
else if(s.surfaceId == 5) //room
{
ivec3 seed = ivec3(vec3(p.x, p.y, p.z) * 0.1);
seed.x += int(sbeatTime * 30.0);
seed.y += int(sbeatTime * 30.0);
seed.z += int(sbeatTime * 30.0);
vec3 hash = Pcg01(seed);
float mask = step(sin(p.x * 0.1 + elapsedTime * 3.0 - p.y * 0.1), 0.0);
m.baseColor = III;
m.roughness = mix(0.03, 0.98, hash.x);
m.emission = mix(0.1, 0.5, mask) * defaultColor;
return m;
}
else if(s.surfaceId == 6) //ball
{
vec3 hash = Pcg01(ivec3(s.objectId, 0, 0));
m.baseColor = hash;
m.roughness = 0.03;
m.emission = hash * 0.5;
return m;
}
return m;
}
vec3 SampleRadiance(vec3 ro0, vec3 rd0, vec3 color)
{
vec3 sum = OOO;
Surface s;
bool hit;
Material m;
vec3 n;
vec2 rand;
vec3 brdfOverPdf;
vec3 v;
vec3 hitPos;
for(int iter = 0; iter < iterMax; iter++)
{
vec3 ro = ro0;
vec3 rd = rd0;
vec3 acc = OOO;
vec3 weight = III;
for (int bounce = 0; bounce <= bounceLimit; bounce++)
{
hit = RayMarching(ro, rd, marchingStep, maxDistance, hitPos, s);
if(!hit) {
acc += color * weight;
break;
}
m = GetMaterial(s, hitPos);
n = GetNormal(hitPos);
rand = Pcg01(vec4(hitPos, float(iter * bounceLimit + bounce) + elapsedTime)).xy;
ro = hitPos + n * EPS * 2.0;
if(m.roughness > 0.99)
{
rd = ImportanceSampleLambert(rand, n);
brdfOverPdf = Lambert(m.baseColor, n, rd);
}
else
{
v = -rd;
rd = ImportanceSampleGGX(rand, m.roughness, n, rd);
if (dot(rd, n) < 0.0 ) { break; }
brdfOverPdf = GGX(n, v, rd, m.roughness, m.baseColor);
}
acc += m.emission * weight;
weight *= brdfOverPdf * max(dot(rd, n), 0.0);
if (dot(weight, weight) < EPS) { break; }
}
sum += acc;
}
return sum / float(iterMax);
}
out vec4 outColor;
void main()
{
vec2 r = resolution.xy;
elapsedTime = mod(time, float(phaseBPM[15]) / bpm * 60.0);
CalcBeatTime();
CalcPhase();
CalcCameraParams();
LoadBallParams(r);
vec4 col = vec4(0.0, 0.0, 0.0, 1.0);
vec3 p = vec3((gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y), 0.0);
cameraDir = normalize(cameraDir);
cameraUp = normalize(cameraUp);
vec3 cameraRight = CROSS(cameraUp, cameraDir);
cameraUp = CROSS(cameraDir, cameraRight);
vec3 ray = normalize(cameraRight * p.x + cameraUp * p.y + cameraDir * dof);
vec3 ro = cameraPos;
vec3 rd = ray;
col.xyz = SampleRadiance(ro, rd, col.xyz);
SaveBallParams(ivec2(gl_FragCoord.xy), col);
outColor = col;
}