Created: 6 Oct 2016, last rebuilt: 25 May 2017

# Menger sponge

I was inspired by FractalLab to try and make my own Menger Sponge animation.

My first attempt was in Three.js, by assembling the three-iteration sponge from smaller cubes. Pretty simple, but it got very slow very fast (at n=3 it’s basically unusable). See here for the result. It became obvious that this should be done with a shader. Many great tutorials exist to kick it off. I went through multiple iterations, starting with much worse performance than three.js:

• assembling the cube from “atoms”: slow (~1 fps for 3 iterations)
• knocking out empty spaces in all dimensions: slow (~1 fps for 3 iterations)
• using a repeating pattern (mod) to knock out empty spaces

The last one shows promise, but I’m sure it can be optimized much further. A project for another rainy day :)

Below’s the basic GLSL code for the last one

``````float basic_box(vec3 pos, vec3 b){
vec3 d = abs(pos) - b;
return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0));
}

float map_menger5(vec3 p){

float main_width_b = 4.0;
float inf = 50.0;

float hole_x, hole_y, hole_z;
float hole_width_b = main_width_b / 3.0;

float menger = basic_box(p, vec3(main_width_b));

for (int iter=0; iter<5; iter++){

float hole_distance = hole_width_b * 6.0;

vec3 c = vec3(hole_distance);
vec3 q = mod(p + vec3(hole_width_b), c) - vec3(hole_width_b);

hole_x = basic_box(q, vec3(inf, hole_width_b, hole_width_b));
hole_y = basic_box(q, vec3(hole_width_b, inf, hole_width_b));
hole_z = basic_box(q, vec3(hole_width_b, hole_width_b, inf));

hole_width_b = hole_width_b / 3.0;        // reduce hole size for next iter
menger = max(max(max(menger, -hole_x), -hole_y), -hole_z); // subtract

}
return menger;
}

float trace(vec3 origin, vec3 ray){

float t = 0.0;
for (int i=0; i<32; ++i){
vec3 p = origin + ray * t;
float d = map_menger5(p);
t += d;
}
return t;
}

mat2 rotate(float theta){
return mat2(cos(theta), -sin(theta), sin(theta), cos(theta));
}

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {

vec2 uv = fragCoord.xy / iResolution.xy;  // normalize coords
uv = uv * 2.0 - 1.0;                      // convert coords from 0,1 to -1,1
uv.x *= iResolution.x/iResolution.y;      // fix aspect

vec3 ray = normalize(vec3(uv.x, uv.y, 1.0));
vec3 origin = vec3(0.0,0.0,-4.0 -5.2*sin(iGlobalTime * 0.20));

float theta;
theta = iGlobalTime / 5.0;
ray.yz *= rotate(theta);
ray.xy *= rotate(theta);
origin.yz *= rotate(theta);
origin.xy *= rotate(theta);

float t = trace(origin, ray);
float fog = 1.0 / (1.0 + t * t * 0.05);
vec3 fc = vec3(fog) * vec3(0.8+0.2*sin(iGlobalTime * 0.1), 0.8+0.2*sin(iGlobalTime * 1.0),0.9+0.1*cos(iGlobalTime*1.0));
fragColor = vec4(fc,1.0);
}
``````