precision highp float;
uniform vec2 resolution;
uniform vec2 mouse;
uniform float time;
uniform sampler2D backbuffer;
out vec4 outColor;
#define DEFAULT 0.0
#define BLOOM 1.0
#define MAT_BOX 1.0
#define MAT_BOX_FRAME 2.0
#define MAT_BOX_TORUS 3.0
float pi = acos(-1.0);
struct RayInfo{
vec3 camPos;
vec3 rayDir;
vec3 color;
bool isHit;
vec3 reflectionAttenuation;
};
mat2 rotate(float a){
float c = cos(a);
float s = sin(a);
return mat2(c, -s, s, c);
}
float repeat(float p, float repCoef){
return (fract(p/repCoef - 0.5) - 0.5) * repCoef;
}
float easeInOutExpo(float t)
{
if (t == 0.0 || t == 1.0) {
return t;
}
if ((t *= 2.0) < 1.0) {
return 0.5 * pow(2.0, 10.0 * (t - 1.0));
} else {
return 0.5 * (-pow(2.0, -10.0 * (t - 1.0)) + 2.0);
}
}
float linearStep(float start, float end, float t)
{
return clamp((t - start) / (end - start), 0.0, 1.0);
}
vec3 hsv2rgb(float h, float s, float v){
vec3 rgb = clamp(abs(mod(h * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
rgb = rgb * rgb * (3.0 - 2.0 * rgb);
return v * mix(vec3(1.0), rgb, s);
}
float random1d2d(vec2 p){
return fract(sin(dot(p.xy, vec2(12.575, 78.2356)))*43578.2356);
}
vec2 polarMod(vec2 p, float r){
float a = atan(p.y, p.x) + pi/r;
float n = 2.0 * pi / r;
a = floor(a/n)*n;
return p * rotate(-a);
}
float sdBoxFrame(vec3 p, vec3 b, vec3 e)
{
vec3 q1 = abs(p) - b;
vec3 q2 = abs(q1+e) - e;
return min(min(
length(max(vec3(q1.x, q2.y, q2.z), 0.0)) + min(max(q1.x, max(q2.y, q2.z)), 0.0),
length(max(vec3(q2.x, q1.y, q2.z), 0.0)) + min(max(q2.x, max(q1.y, q2.z)), 0.0)),
length(max(vec3(q2.x, q2.y, q1.z), 0.0)) + min(max(q2.x, max(q2.y, q1.z)), 0.0));
}
float sdBox(vec3 p, vec3 s){
vec3 q = abs(p) - s;
return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
}
vec3 optionMin(vec3 a, vec3 b)
{
return (a.x < b.x) ? a : b;
}
vec3 sdBoxWithFrame(vec3 p, vec3 s, float w){
vec3 d = vec3(10e8, 0.0, DEFAULT);
d = optionMin(d, vec3(sdBox(p, s - vec3(w)), MAT_BOX, DEFAULT));
d = optionMin(d, vec3(sdBoxFrame(p, s, vec3(w)), MAT_BOX_FRAME, BLOOM));
return d;
}
float sdBox2d(vec2 p, vec2 s){
p = abs(p) - s;
return length(max(p, 0.0))+min(max(p.x, p.y), 0.0);
}
float sdTorusKnots(vec3 p, float inRadius, float outRadius, float divide){
vec2 cp = vec2(length(p.xz) - outRadius, p.y);
float a = atan(p.x, p.z);
cp *= rotate(a*3.0);
cp.y = abs(cp.y)-0.01;
float d = sdBox2d(cp, vec2(inRadius, inRadius*2.0)*sin(divide*a+time*divide));
return d;
}
vec3 sdTorusWithFrame(vec3 p){
vec3 d = vec3(10e8, 0.0, DEFAULT);
d = optionMin(d, vec3(sdTorusKnots(p, 0.001, 0.021, 3.0), MAT_BOX_TORUS, DEFAULT));
d = optionMin(d, vec3(sdTorusKnots(p, 0.0012, 0.02, 3.0), MAT_BOX_FRAME, BLOOM));
return d;
}
vec3 distanceFunction(vec3 p){
vec3 d = vec3(10e8, 0.0, DEFAULT);
float it = time*2.0;
float fTime = mod(it, 16.0);
float t1 = linearStep(2.0, 2.5, fTime);
float t2 = linearStep(5.0, 5.5, fTime);
float t3 = linearStep(8.0, 8.5, fTime);
float t4 = linearStep(11.0, 11.5, fTime);
float t5 = linearStep(14.0, 14.5, fTime);
vec3 p1 = p;
p1.z -= time*0.3;
float rotateCoef = 0.0;
rotateCoef = mix(0.0, 0.8, easeInOutExpo(t1));
rotateCoef = mix(rotateCoef, 0.4, easeInOutExpo(t2));
rotateCoef = mix(rotateCoef, 0.9, easeInOutExpo(t3));
rotateCoef = mix(rotateCoef, 1.4, easeInOutExpo(t4));
rotateCoef = mix(rotateCoef, 0.0, easeInOutExpo(t5));
p1.xy *= rotate(rotateCoef*p1.z);
p1.z = repeat(p1.z, 0.5);
float offsetParamSub = 0.0;
offsetParamSub = mix(0.42, 0.34, easeInOutExpo(t1));
offsetParamSub = mix(offsetParamSub, 0.30, easeInOutExpo(t2));
offsetParamSub = mix(offsetParamSub, 0.43, easeInOutExpo(t3));
offsetParamSub = mix(offsetParamSub, 0.35, easeInOutExpo(t4));
offsetParamSub = mix(offsetParamSub, 0.42, easeInOutExpo(t5));
for(int i = 0; i < 4; i++){
p1.xy = polarMod(p1.xy, 4.0);
p1 = abs(p1) - offsetParamSub;
p1.xz *= rotate(0.38);
p1.yz *= rotate(0.26);
}
d = optionMin(d, sdBoxWithFrame(p1, vec3(0.03, 0.25, 0.25), 0.005));
vec3 p2 = p;
p2.z -= 4.93;
p2.xy *= rotate(pi/2.0);
p2.yz *= rotate(time);
d = optionMin(d, sdTorusWithFrame(p2));
vec3 p3 = p;
p3.z -= 4.93;
p3.xy *= rotate(time*0.2);
p3.yz *= rotate(time);
d = optionMin(d, sdTorusWithFrame(p3));
return d;
}
vec3 getNormal(vec3 p){
vec2 err = vec2(0.001, 0.0);
return normalize(vec3(
distanceFunction(p + err.xyy).x - distanceFunction(p - err.xyy).x,
distanceFunction(p + err.yxy).x - distanceFunction(p - err.yxy).x,
distanceFunction(p + err.yyx).x - distanceFunction(p - err.yyx).x
));
}
float getAO(vec3 p, vec3 n){
float occ = 0.0;
float sca = 1.0;
for(int i = 0; i < 5; i++){
float h = 0.01 + 0.12 * float(i) / 4.0;
float d = distanceFunction(p + h * n).x;
occ += (h - d) * sca;
if(occ > 0.35){
break;
}
}
return clamp(1.0 - 3.0 * occ, 0.0, 1.0) * (0.5 + 0.5 * n.y);
}
float getSoftShadow(vec3 camPos, vec3 rayDir, float tMin, float tMax){
float tp = (0.8 - camPos.y) / rayDir.y;
if(tp > 0.0){
tMax = min(tMax, tp);
}
float res = 1.0;
float t = tMin;
for(int i = 0; i < 24; i++){
float h = distanceFunction(camPos + rayDir * t).x;
float s = clamp(8.0 * h / t, 0.0, 1.0);
res = min(res, s * s * (3.0 - 2.0 * s));
t += clamp(h, 0.02, 0.2);
if(res < 0.004 || tMax < t){
break;
}
}
return clamp(res, 0.0, 1.0);
}
float fresnelSchlick(float f0, float c){
return f0 + (1.0 - f0) * pow((1.0 - c), 5.0);
}
vec3 acesFilm(vec3 col){
float a = 2.51;
float b = 0.03;
float c = 2.43;
float d = 0.59;
float e = 0.14;
return clamp((col * (a * col + b)) / (col * (c * col + d) + e), 0.0, 1.0);
}
vec3 getBloomAlbedo(vec3 p, float materialId)
{
if(materialId == MAT_BOX_FRAME){
return hsv2rgb(sin(p.z*6.2)+time*0.5, 0.7, 0.7);
}
return vec3(0.0);
}
RayInfo rayMarch(vec3 camPos, vec3 rayDir, vec3 reflectionAttenuation, float rand){
RayInfo info;
info.camPos = camPos;
info.rayDir = rayDir;
info.color = vec3(0.0);
info.isHit = false;
info.reflectionAttenuation = reflectionAttenuation;
vec3 p;
float d = 0.0;
vec3 df = vec3(0.0);
for(int i = 0; i < 160; i++){
p = camPos + rayDir * d;
df = distanceFunction(p);
float dist = df.x;
float gProperty = df.z;
if(gProperty == DEFAULT){
if(dist <= 0.001){
info.isHit = true;
break;
}
d += dist * 0.25;
}else{
info.color += 0.001/abs(dist) * getBloomAlbedo(p, df.y);
d += abs(dist) * 0.25;
}
}
if(info.isHit){
vec3 normal = getNormal(p);
float metalic = 0.0;
vec3 albedo = vec3(0.0);
vec3 ld = normalize(-p);
vec3 ref = reflect(rayDir, normal);
float f0 = 1.0;
if(df.y == MAT_BOX){
albedo = vec3(0.4588, 0.3843, 0.3843);
metalic = 1.0;
}
if(df.y == MAT_BOX_TORUS){
albedo = hsv2rgb(sin(atan(p.y, p.x)*0.2+time*0.2), 0.8, 0.7);
metalic = 1.0;
}
float diffuse = clamp(dot(normal, ld), 0.0, 1.0);
float specular = pow(clamp(dot(reflect(ld, normal), rayDir) ,0.0, 1.0), 10.0);
float ao = getAO(p, normal);
float shadow = getSoftShadow(p, ld, 0.25, 3.0);
info.color += albedo * diffuse * shadow * (1.0 - metalic);
info.color += albedo * specular * shadow * metalic;
info.color += albedo * ao * mix(vec3(0.0), vec3(1.0), 0.7);
info.reflectionAttenuation *= albedo * fresnelSchlick(f0, dot(ref, normal));
info.camPos = p + 0.01 * normal;
info.rayDir = ref;
}
info.color *= smoothstep(4.0, 0.0, d);
return info;
}
vec3 getCutInUv(vec2 uv){
float timer = time*2.0;
int index = int(floor(mod(timer, 30.0)));
float expCoef = -20.0;
if(index == 0){
float iUvY = floor((uv.y * 0.5 + 0.5) * 8.0);
uv.x += (step(mod(iUvY, 2.0), 0.0) - 0.5) * exp(expCoef * fract(timer)) * 5.0;
}else if(index == 14){
float iUvX = floor((uv.x * 0.5 + 0.5) * 10.0);
uv.y += (step(1.0 - mod(iUvX, 2.0), 0.0) - 0.5) * exp(expCoef * fract(timer)) * 5.0;
}
float reflectFlag = index >= 14 ? 1.0 : 0.0;
return vec3(uv, reflectFlag);
}
vec3 renderingFunc(vec2 uv){
vec3 uvElement = getCutInUv(uv);
uv = uvElement.xy;
float flag = uvElement.z;
vec3 color = vec3(0.0);
vec3 camPos = vec3(0.0, 0.0, 5.0);
vec3 lookPos = vec3(0.0, 0.0, 0.0);
vec3 forward = normalize(lookPos - camPos);
vec3 up = vec3(0.0, 1.0, 0.0);
vec3 right = normalize(cross(forward, up));
up = normalize(cross(right, forward));
float fov = 1.0;
vec3 rayDir = normalize(uv.x * right + uv.y * up + fov * forward);
float rand = random1d2d(uv);
vec3 ra = vec3(1.0);
float d = 0.0;
if(flag == 0.0){
for(int i = 0; i < 1; i++){
RayInfo info = rayMarch(camPos, rayDir, ra, rand);
color += info.reflectionAttenuation * info.color * ra;
if(!info.isHit){
break;
}
ra = info.reflectionAttenuation;
camPos = info.camPos;
rayDir = info.rayDir;
}
}
else{
for(int i = 0; i < 3; i++){
RayInfo info = rayMarch(camPos, rayDir, ra, rand);
color += info.reflectionAttenuation * info.color * ra;
if(!info.isHit){
break;
}
ra = info.reflectionAttenuation;
camPos = info.camPos;
rayDir = info.rayDir;
}
}
color = acesFilm(color*0.8);
color = pow(color, vec3(0.4545));
return color;
}
void main(){
vec2 uv = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
vec2 texUv = vec2(gl_FragCoord.xy/resolution);
vec3 color = vec3(0.0);
color += renderingFunc(uv);
outColor = vec4(color, 1.0);
}