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 😛
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.