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