scene.org File Archive

File download

<root>­/­parties­/­2024­/­sessions24­/­code_graphics/stairs-house-by-ukeyshima.txt

File size:
24 451 bytes (23.88K)
File date:
2024-11-19 12:14:03
Download count:
all-time: 12

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;
}