Preview
precision mediump float;
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
const float MTL_PSEUDO_KLEINIAN = 1.;
const float MTL_KLEIN = 2.;
const vec3 ROTATION_AXIS = normalize(vec3(0.1, 1, 0.5));
vec3 rotate(vec3 p, float angle){
float s = sin(angle);
float c = cos(angle);
float r = 1.0 - c;
mat3 m = mat3(ROTATION_AXIS.x * ROTATION_AXIS.x * r + c,
ROTATION_AXIS.y * ROTATION_AXIS.x * r + ROTATION_AXIS.z * s,
ROTATION_AXIS.z * ROTATION_AXIS.x * r - ROTATION_AXIS.y * s,
ROTATION_AXIS.x * ROTATION_AXIS.y * r - ROTATION_AXIS.z * s,
ROTATION_AXIS.y * ROTATION_AXIS.y * r + c,
ROTATION_AXIS.z * ROTATION_AXIS.y * r + ROTATION_AXIS.x * s,
ROTATION_AXIS.x * ROTATION_AXIS.z * r + ROTATION_AXIS.y * s,
ROTATION_AXIS.y * ROTATION_AXIS.z * r - ROTATION_AXIS.x * s,
ROTATION_AXIS.z * ROTATION_AXIS.z * r + c);
return m * p;
}
vec2 opUnion(const vec2 d1, const vec2 d2){
return (d1.x < d2.x) ? d1 : d2;
}
const vec3 spherePos1 = vec3(5, 5, 0);
const vec3 spherePos2 = vec3(5, -5, 0);
const vec3 spherePos3 = vec3(-5, 5, 0);
const vec3 spherePos4 = vec3(-5, -5, 0);
const vec3 spherePos5 = vec3(0, 0, 7.071);
const vec3 spherePos6 = vec3(0, 0, -7.071);
const float SPHERE_R = 5.;
const float SPHERE_R2 = SPHERE_R * SPHERE_R;
int kleinIteration = 8;
float kleinSphereR = 5.;
float loopNum = 0.;
const int SPHERE_NUM = 6;
const vec3 KLEIN_POS = vec3(0, 0, -5);
const int MAX_KLEIN_ITARATION = 20;
const vec4 initialSp = vec4(-1.);
vec2 distKlein(vec3 pos){
pos = rotate(pos + KLEIN_POS, radians(time * 30.));
loopNum = 0.;
float dr = 1.;
vec4 sp;
for(int i = 0 ; i < MAX_KLEIN_ITARATION ; i++){
if(i > kleinIteration) break;
sp = initialSp;
float d = distance(pos, spherePos1);
sp = (d < SPHERE_R) ? vec4(spherePos1, d) : sp;
d = distance(pos, spherePos2);
sp = (d < SPHERE_R) ? vec4(spherePos2, d) : sp;
d = distance(pos, spherePos3);
sp = (d < SPHERE_R) ? vec4(spherePos3, d) : sp;
d = distance(pos, spherePos4);
sp = (d < SPHERE_R) ? vec4(spherePos4, d) : sp;
d = distance(pos, spherePos5);
sp = (d < SPHERE_R) ? vec4(spherePos5, d) : sp;
d = distance(pos, spherePos6);
sp = (d < SPHERE_R) ? vec4(spherePos6, d) : sp;
if(sp.x == -1.){
break;
}else{
vec3 diff = (pos - sp.xyz);
dr *= SPHERE_R2 / dot(diff, diff);
pos = (diff * SPHERE_R2)/(sp.w * sp.w) + sp.xyz;
loopNum++;
}
}
return vec2((length(pos) - kleinSphereR) / abs(dr) * 0.08, MTL_KLEIN);
}
vec3 orb;
const vec3 PSEUDO_KLEINIAN_POS = vec3(10, 6, 2.5);
const vec3 TRAP_POINT = vec3(1000.);
const vec3 PSEUDO_KLEINIAN_CUBE_SIZE = vec3(9.2436, 9.0756, 9.2436);
const float PSEUDO_KLEINIAN_SIZE = 110.;
vec2 distPseudoKleinian(vec3 p){
orb = TRAP_POINT;
p = p + PSEUDO_KLEINIAN_POS;
float DEfactor = 1.;
vec3 ap = p + 1.;
for(int i = 0; i < 7 ; i++){
ap = p;
p= -p + 2. * clamp(p, -PSEUDO_KLEINIAN_CUBE_SIZE, PSEUDO_KLEINIAN_CUBE_SIZE);
orb = min( orb, vec3(abs(p)));
float k = PSEUDO_KLEINIAN_SIZE / dot(p, p);
p *= k;
DEfactor *= k;
}
return vec2(abs(0.5*abs(p.z)/DEfactor), MTL_PSEUDO_KLEINIAN);
}
vec3 calcRay (const vec3 eye, const vec3 target, const vec3 up, const float fov,
const float width, const float height, const vec2 coord){
float imagePlane = (height * .5) / tan(fov * .5);
vec3 v = normalize(target - eye);
vec3 xaxis = normalize(cross(v, up));
vec3 yaxis = normalize(cross(v, xaxis));
vec3 center = v * imagePlane;
vec3 origin = center - (xaxis * (width *.5)) - (yaxis * (height * .5));
return normalize(origin + (xaxis * coord.x) + (yaxis * (height - coord.y)));
}
const vec4 K = vec4(1.0, .666666, .333333, 3.0);
vec3 hsv2rgb(const vec3 c){
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
vec3 eye = vec3(0, 0, 800);
vec3 target = vec3(0, 0, 7);
const vec3 up = vec3(0, 1, 0);
const float fov = radians(60.);
vec2 distFunc(vec3 p){
return opUnion(distPseudoKleinian(p), distKlein(p));;
}
const vec2 d = vec2(0.01, 0.);
vec3 getNormal(const vec3 p){
return normalize(vec3(distFunc(p + d.xyy).x - distFunc(p - d.xyy).x,
distFunc(p + d.yxy).x - distFunc(p - d.yxy).x,
distFunc(p + d.yyx).x - distFunc(p - d.yyx).x));
}
const float PI_4 = 12.566368;
const vec3 LIGHTING_FACT = vec3(0.1);
vec3 diffuseLighting(const vec3 p, const vec3 n, const vec3 diffuseColor,
const vec3 lightPos, const vec3 lightPower){
vec3 v = lightPos - p;
float dot = dot(n, normalize(v));
float r = length(v);
return (dot > 0.) ?
(lightPower * (dot / (PI_4 * r * r))) * diffuseColor
: LIGHTING_FACT * diffuseColor;
}
const vec3 lightPos = vec3(100., 100., 100.);
const vec3 lightPos2 = vec3(-100., -100., -100);
const vec3 lightPower = vec3(10.);
const vec3 lightPower2 = vec3(10.);
vec3 lighting(const float kd, const vec3 matColor, vec3 l,
const vec3 intersection, const vec3 normal){
return (kd > 0.) ?
l + (diffuseLighting(intersection, normal, matColor,
lightPos, lightPower) * kd) +
(diffuseLighting(intersection, normal, matColor,
lightPos2, lightPower2) * kd)
: l;
}
const int MAX_MARCHING_LOOP = 700;
int marchingLoop = 700;
vec3 march(const vec3 origin, const vec3 ray, const float threshold){
vec3 rayPos = origin;
vec2 dist = vec2(0., -1);
float rayLength = 0.;
for(int i = 0 ; i < MAX_MARCHING_LOOP ; i++){
dist = distFunc(rayPos);
rayLength += dist.x;
rayPos = origin + ray * rayLength ;
if(dist.x < threshold || i > marchingLoop) break;
}
return vec3(dist, rayLength);
}
const float fogStart = 10.;
const float fogEnd = 100.;
const float FOG_END_START_RECIPROCAL = 1. / (fogEnd - fogStart);
const vec3 fogf = vec3(1.);
const vec3 BLACK = vec3(0);
int reflectNum = 3;
vec3 trace(vec3 eye, vec3 ray){
vec3 l = BLACK;
float coeff = 1.;
for(int depth = 0 ; depth < 5 ; depth++){
if(depth >= reflectNum) break;
float threshold = 0.003 * pow(1.5 , float(depth));
vec3 result = march(eye, ray, threshold);
vec3 intersection = eye + ray * result.z;
vec3 matColor = vec3(0);
vec3 normal = getNormal(intersection);
if(result.x < threshold){
float ks = 0.;
if(result.y == MTL_KLEIN){
ks = (loopNum < 4.) ? 0.5 * coeff : 0.;
matColor = hsv2rgb(vec3(0.1 + loopNum * 0.1 , 1., 1.));
}else{
matColor = vec3(clamp(6.0*orb.y,0.0,1.0), clamp(1.0-2.0*orb.z,0.0,1.0), .5);
ks = (matColor.r > 0.8 &&
matColor.g > 0.8 ) ? 0.8 * coeff : 0.;
}
if(ks > 0.){
marchingLoop -= 200;
l = mix(fogf , lighting(1. - ks, matColor, l, intersection, normal),
clamp((fogEnd - result.z) * FOG_END_START_RECIPROCAL, 0.5, 1.0));
coeff = ks;
eye = eye + ray * result.z * 0.9;
ray = reflect(ray, normal);
}else{
l = mix(fogf , lighting(1. - ks, matColor, l, intersection, normal),
clamp((fogEnd - result.z) * FOG_END_START_RECIPROCAL, 0.5, 1.0));
break;
}
}else{
l = mix(fogf , l, clamp((fogEnd - result.z) * FOG_END_START_RECIPROCAL, 0.5, 1.0));
break;
}
}
return l;
}
void expandSphere(const float t,
const float minR, const float maxR, const int iteration){
kleinIteration = iteration;
kleinSphereR = mix(minR, maxR,
smoothstep(minR, maxR, t));
}
void shrinkSphere(const float t,
const float minR, const float maxR, const int iteration){
kleinIteration = iteration;
kleinSphereR = mix(maxR, minR,
smoothstep(minR, maxR, minR + t));
}
const float DISPLAY_GAMMA_COEFF = 1. / 2.2;
vec3 gammaCorrect(vec3 rgb) {
return vec3((min(pow(rgb.r, DISPLAY_GAMMA_COEFF), 1.)),
(min(pow(rgb.g, DISPLAY_GAMMA_COEFF), 1.)),
(min(pow(rgb.b, DISPLAY_GAMMA_COEFF), 1.)));
}
const vec2 constantList = vec2(1.0, 0.0);
const float eyeRad = 8.9;
void main(){
float t = mod(time, 85.);
const float minR = 0.;
const float maxR = 3.;
vec3 eye = vec3(eyeRad * sin(time), 0., 7. + eyeRad * cos(time));
target = -(eye - target ) + vec3(0. , (mouse.y - 0.5) * 10., 0);
if(t < 3.){
expandSphere(t, minR, maxR, 0);
}else if(t < 6.){
shrinkSphere(t - 3., minR, maxR, 0);
}else if(t < 9.){
expandSphere(t - 6., minR, maxR, 1);
}else if(t < 12.){
shrinkSphere(t - 9., minR, maxR, 1);
}else if(t < 15.){
expandSphere(t - 12., minR, maxR, 2);
}else if(t < 18.){
shrinkSphere(t - 15., minR, maxR, 2);
}else if(t < 21.){
expandSphere(t - 18., minR, maxR, 3);
}else if(t < 24.){
shrinkSphere(t - 21., minR, maxR, 3);
}else if(t < 27.){
expandSphere(t - 24., minR, maxR, 4);
}else if(t < 30.){
shrinkSphere(t - 27., minR, maxR, 4);
}else if(t < 40.){
expandSphere(t - 30., minR, 5., 12);
reflectNum = 4;
}else if(t < 55.){
expandSphere(t - 40., 5., 6.3, 12);
reflectNum = 4;
}else if(t < 65.){
shrinkSphere(t - 55., 2.0833, 6.5, 8);
}else if(t < 70.){
shrinkSphere(t - 65., minR, 2.0833, 8);
}else if(t < 71.){
kleinSphereR = 0.;
kleinIteration = 7;
}else if(t < 72.){
kleinSphereR = 0.;
kleinIteration = 6;
}else if(t < 73.){
kleinSphereR = 0.;
kleinIteration = 5;
}else if(t < 76.){
kleinSphereR = 0.;
kleinIteration = 4;
}else if(t < 78.){
kleinSphereR = 0.;
kleinIteration = 3;
}else if(t < 80.){
kleinSphereR = 0.;
kleinIteration = 2;
}else if(t < 82.){
kleinSphereR = 0.;
kleinIteration = 1;
}else{
kleinSphereR = 0.;
kleinIteration = 0;
}
const vec2 coordOffset = vec2(0.5);
vec3 ray = calcRay(eye, target, up, fov,
resolution.x, resolution.y,
gl_FragCoord.xy + coordOffset);
gl_FragColor = vec4(gammaCorrect(trace(eye, ray)), 1.);
}