Menger sponge
Jest przykładem fraktala IFS (Iterated Function System) opracowanego przez austriackiego matematyka Karla Mengera w 1927 roku. Więcej na Wikipedi.
W przypadku opracowania Menger’a przy pomocy shaderów posługujemy się następującym algorytmem:
- Generujemy sześcian
- Generujemy trójwymiarowy krzyż o o 6 ramionach który w kolejnych iteracjach jest przesuwany i zmieszany
- Odejmujemy od utworzonego sześciana
- Wykonujemy kolejną iterację
Poniżej kod shadera (sama procedura mapowania):
const float START_SCALE=3.0;
const float START_TIME_ADD=0.0;
const float START_TIME_SPEED=0.0;
const float ITER_SCALE=3.0;
const float ITER_TIME_ADD=0.0;
const float ITER_TIME_SPEED=1.0;
const float START_POS=3.0;
const float START_POS_TIME_ADD=0.0;
const float START_POS_TIME_SPEED=1.0;
const float START_DIV=3.0;
const float START_DIV_TIME_ADD=0.0;
const float START_DIV_TIME_SPEED=1.0;
const float START_MOD=2.0;
const float START_MOD_TIME_ADD=0.0;
const float START_MOD_TIME_SPEED=1.0;
const int ITERATIONS=1;
//-------------------------------------
// Map function
// Return 2 dimensional vector, where:
// x - distance
// y - material ID
//-------------------------------------
vec2 map(vec3 p) {
float scale=START_SCALE+(START_TIME_ADD*sin(time*START_TIME_SPEED));
float startPos=START_POS+(START_POS_TIME_ADD*sin(time*START_POS_TIME_SPEED));
float startDiv=START_DIV+(START_DIV_TIME_ADD*sin(time*START_DIV_TIME_SPEED));
float startMod=START_MOD+(START_MOD_TIME_ADD*sin(time*START_MOD_TIME_SPEED));
float startIter=ITER_SCALE + (ITER_TIME_ADD*sin(time*ITER_TIME_SPEED));
vec2 hit=box(p, vec3(0.0,0.0,0.0), vec3(1.0,1.0,1.0), 2.0);
//
// Fractal Menger sponge
//
for (int i=0;i<ITERATIONS;i++){
vec3 tempPos = startPos - abs(mod(p * scale, startMod) - 1.0)*startDiv;
scale=scale * startIter;
vec2 cross=cross3D(tempPos, vec3(0.0,0.0,0.0), 10.0, 1.0, 0.0)/scale;
hit.x=max(hit.x,-cross.x);
}
hit=join(hit, plane(p, vec3(0.0, -1.0, 0.0), vec3(0.1, 1.0, 0.1), 0.0));
return hit;
}
do funkcji generujących obiekty trzeba dodać kod funkcji generowania krzyża:
vec2 cross3D(vec3 p, vec3 pos, float size, float thick, float material) {
p=p - pos;
vec2 obj1=box(p.xyz, vec3(0.0,0.0,0.0), vec3(size,thick,thick), material);
vec2 obj2=box(p.yzx, vec3(0.0,0.0,0.0), vec3(thick,size,thick), material);
vec2 obj3=box(p.zxy, vec3(0.0,0.0,0.0), vec3(thick,thick,size), material);
return vec2(min(obj1.x,min(obj2.x, obj3.x)), material);
}
oraz efekt pracy:
Ps. Nie jest to do końca Menger sponge bo ma trzy duże dziury zamiast jednej :P
Stałe poniżej pozwalają na animację generowanego fraktala w czasie:
const float START_SCALE=3.0;
const float START_TIME_ADD=0.0;
const float START_TIME_SPEED=0.0;
const float ITER_SCALE=3.0;
const float ITER_TIME_ADD=0.0;
const float ITER_TIME_SPEED=1.0;
const float START_POS=3.0;
const float START_POS_TIME_ADD=0.0;
const float START_POS_TIME_SPEED=1.0;
const float START_DIV=3.0;
const float START_DIV_TIME_ADD=0.0;
const float START_DIV_TIME_SPEED=1.0;
const float START_MOD=2.0;
const float START_MOD_TIME_ADD=0.0;
const float START_MOD_TIME_SPEED=1.0;
const int ITERATIONS=1;
Optymalizacje:
- można inaczej generować krzyż w prostszy sposób
- generowanie funkcji sin zmieniających parametry startowe przenieś do kodu wyższego rzędu, i podawać je jako zmienne shadera
- oraz poczytać trochę dobrej lektury podanej na końcu RDF cz. III

Możliwość komentowania jest wyłączona.