Hlavní navigace

Fraktály v počítačové grafice XXV

11. 4. 2006
Doba čtení: 12 minut

Sdílet

V dnešním pokračování seriálu o fraktálech používaných (nejenom) v počítačové grafice navážeme na díl předchozí, ve kterém jsme si popsali fraktální množiny Michaela Barnsleye počítané a následně zobrazované v komplexní rovině. Minule byly popsány "Mandelbrotovy varianty" těchto množin, dnešní část bude naopak věnována "variantám Juliovým", které jsou - alespoň z estetického hlediska - mnohem zajímavější.

Obsah

1. Juliovy verze fraktálních množin Michaela Barnsleyho
2. Fraktální množina pojmenovaná Barnsley J1
3. Demonstrační příklad vykreslující fraktální množinu Barnsley J1
4. Fraktální množina pojmenovaná Barnsley J2
5. Demonstrační příklad vykreslující fraktální množinu Barnsley J2
6. Fraktální množina pojmenovaná Barnsley J3
7. Demonstrační příklad vykreslující fraktální množinu Barnsley J3
8. Rozšiřující parametrizace Barnsleyho fraktálních množin
9. Obsah dalšího pokračování tohoto seriálu

1. Juliovy verze fraktálních množin Michaela Barnsleyho

V předchozím pokračování tohoto seriálu jsme si ukázali iterační vztahy, které jsou použity pro výpočet a následné vykreslení Mandelbrotových verzí Barnsleyho fraktálních množin nazývaných stručně M1, M2 a M3. Vzhledem k tomu, že u těchto množin byla pro každý počítaný bod (tento bod odpovídá pixelu zobrazenému na obrazovce) nastavena jiná hodnota komplexní konstanty c, je zřejmé, že fraktální množiny M1, M2 a M3 představují mapy odpovídající Juliovým verzím těchto množin, které budu v dalším textu označovat kódy J1, J2 a J3. Juliovy verze používají pro svůj výpočet naprosto stejný iterační vztah jako jejich protějšky; jediný (zato významný) rozdíl spočívá v počátečních podmínkách, které se pro každý bod/pixel nastavují před vstupem do iterační smyčky.

Zatímco u Mandelbrotových množin je komplexní proměnná z nastavena na hodnotu 0+0i a konstanta c odpovídá pozici počítaného bodu v komplexní rovině, je u Juliových verzí před vstupem do iterační smyčky nastavena proměnná z na souřadnice počítaného bodu, kdežto hodnota konstanty c zůstává pro celý počítaný obrázek zachována. V následujících kapitolách budou popsány všechny tři Juliovy varianty Barnsleyho fraktálů, samozřejmě spolu s demonstračními příklady (pro každou fraktální množinu je vytvořen samostatný příklad) a mnoha ilustračními obrázky.

2. Fraktální množina pojmenovaná Barnsley J1

Fraktální množina, která bývá v literatuře pojmenována jako Barnsley J1 nebo stručněji pouze J1, používá stejný iterační vztah, který byl aplikován i u fraktální množiny Barnsley M1. V tomto iteračním vztahu se vyskytuje podmínka (test) na aktuální hodnotu reálné složky komplexního čísla zn. Kromě této podmínky se při každé iteraci testuje, zda hodnota |zn| nepřekročila hodnotu 2. Pokud by k tomuto překročení došlo, řada hodnot zn by začala divergovat. Celý iterační vztah je možné v pseudokódu využívajícím komplexní proměnné zapsat následovně:

    if (real(z) >= 0)
        z(n+1)=(z-1)*c
    else
        z(n+1)=(z+1)*c
    if (|z|>2) break; 

Výše uvedený pseudokód přepíšeme tak, aby vznikla sekvence příkazů použitelná v programovacím jazyku C. V následující části programu je k iteračnímu vztahu přidán také i zmíněný test na ukončení iterační smyčky v případě divergence posloupnosti hodnot zn, který dobře známe už z funkce provádějící výpočet „klasické“ Mandelbrotovy množiny:

    zx2=zx*zx;                 // pomocné proměnné s druhými mocninami
    zy2=zy*zy;                 // složek real(z) a imag(z)
    if (zx2+zy2>4) break;      // test na ukončení iterační smyčky
    if (zx>=0) {               // test na znaménko reálné složky Z
        zxn=zx*cx0-zy*cy0-cx0;
        zyn=zx*cy0+zy*cx0-cy0;
    }
    else {
        zxn=zx*cx0-zy*cy0+cx0;
        zyn=zx*cy0+zy*cx0+cy0;
    }
    zx=zxn;                    // přepis nových hodnot do proměnné Z
    zy=zyn; 

Céčkovská funkce recalcJuliaJ1(), která s využitím výše uvedeného fragmentu kódu provádí přepočet a následné vykreslení Juliovy varianty Barnsleyovy fraktální množiny J1, vypadá následovně:

//-----------------------------------------------------------------------------
// Překreslení Juliovy verze Barnsleyovy fraktální množiny J1
//-----------------------------------------------------------------------------
void recalcJuliaJ1(pixmap *pix,                 // pixmapa pro vykreslování
                  int    maxiter,               // maximální počet iterací
                  double scale,                 // měřítko obrazce
                  double xpos,                  // posun obrazce
                  double ypos,
                  int    palette,               // barvová paleta
                  int    rf, int gf, int bf,    // příznaky barvových složek
                  double cx0,
                  double cy0)                   // aktuálně nastavená hodnota C
{
    double zx, zy, zx2, zy2, zxn, zyn;          // složky komplexní proměnné Z a Z^2
    double zx0, zy0;
    double cx, cy;
    double xmin, ymin, xmax, ymax;              // rohy vykreslovaného obrazce v komplexní
                                                // rovině
    int    x, y;                                // počitadla sloupců a řádků v pixmapě
    int    iter;                                // počitadlo iterací
    unsigned char r, g, b;

    calcCorner(xpos, ypos, scale, &xmin, &ymin, &xmax, &ymax);
    zy0=ymin;
    for (y=0; y<pix->height; y++) {             // pro všechny řádky v pixmapě
        zx0=xmin;
        for (x=0; x<pix->width; x++) {          // pro všechny pixely na řádku
            zx=zx0;
            zy=zy0;
            for (iter=0; iter<maxiter; iter++) {// iterační smyčka
                zx2=zx*zx;                      // zkrácený výpočet druhé mocniny složek Z
                zy2=zy*zy;
                if (zx2+zy2>4) break;
                if (zx>=0) {
                    zxn=zx*cx0-zy*cy0-cx0;
                    zyn=zx*cy0+zy*cx0-cy0;
                }
                else {
                    zxn=zx*cx0-zy*cy0+cx0;
                    zyn=zx*cy0+zy*cx0+cy0;
                }
                zx=zxn;
                zy=zyn;
            }
            mapPalette(palette, iter, &r, &g, &b);
            r=r*rf;                             // uživatelem řízené vynulování
            g=g*gf;                             // vybraných barvových složek
            b=b*bf;
            putpixel(pix, x, y, r, g, b);
            zx0+=(xmax-xmin)/pix->width;        // posun na další bod na řádku
        }
        zy0+=(ymax-ymin)/pix->height;           // posun na další řádek
    }
} 

Na rozdíl od fraktálu M1 je fraktální množina J1 po vizuální stránce mnohem zajímavější, zejména díky tomu, že se původní „organické“ tvary (typické pro Juliovy množiny) díky přídavné podmínce v iterační smyčce rozpadají na ostře oddělené fragmenty, takže výsledkem je spíše „krystalická“ struktura celého útvaru – to je ostatně patrné na prvním, druhém i třetím ilustračním obrázku. Druhý obrázek je poněkud netypický v tom, že se výrazně zvětšila hodnota použitá v podmínce pro ukončení iterační smyčky (tato hodnota je známá pod označením bailout) a i vztah mezi počtem iterací a barvou výsledného pixelu byl modifikován.

fractals25_1.png

Obrázek 1: Barnsleyova fraktální množina J1

fractals25_2.png

Obrázek 2: Barnsleyova fraktální množina J1 s odlišnou konstantou C

fractals25_3.png

Obrázek 3: Další pohled na Barnsleyovu fraktální množinu J1

3. Demonstrační příklad vykreslující fraktální množinu Barnsley J1

Výše uvedená céčkovská funkce recalcJuliaJ1() je použita v dnešním prvním demonstračním příkladu. Po překladu a spuštění tohoto příkladu se zobrazí okno o rozměrech 640×460 pixelů, v jehož levé části se nalézá pohled na Barnsleyho fraktální množinu M1 a v pravé části pohled na Barnsleyho fraktální množinu J1. Ve spodní části okna se nachází základní informace o nastavených parametrech použitých pro generování a zobrazení obou fraktálních množin. Tyto množiny, z nichž každá má rozlišení 320×320 pixelů, jsou podobně jako původní Mandelbrotova množina a Juliovy množiny mezi sebou svázány stejným iteračním vztahem a hodnotou komplexní konstanty c. Pohybem kurzoru myši po fraktální množině M1, tj. po levé polovině okna, se mění komplexní konstanta c, která je ihned použita pro dynamický přepočet Barnsleyovy fraktální množiny J1. Screenshot z prvního demonstračního příkladu je zobrazen na čtvrtém obrázku, stručný popis ovládání je uveden v následující tabulce.

Ovládání příkladu
Klávesa Význam
, snížení počtu iterací o jednotku
. zvýšení počtu iterací o jednotku
< snížení počtu iterací o deset jednotek
> zvýšení počtu iterací o deset jednotek
S uložení rastrového obrázku s fraktálem do externího souboru typu TGA
Q ukončení tohoto demonstračního příkladu
Esc má stejný význam jako klávesa Q – ukončení aplikace
šipky posun obrazce ve směru šipek (o cca pět procent velikosti obrázku)
Page Up změna měřítka (zvětšení výřezu o deset procent)
Page Down změna měřítka (zmenšení výřezu o deset procent)
0 nastavení barvové palety „Blues“ (implicitní nastavení po spuštění programu)
1 nastavení barvové palety „Gold“
2 nastavení barvové palety „Greens“
3 nastavení barvové palety „Ice“
4 nastavení barvové palety „Juteblue“
5 nastavení barvové palety „Jutemap“
6 nastavení barvové palety „Jutes“
7 nastavení barvové palety „Mandmap“
8 nastavení barvové palety „Phong“
9 nastavení barvové palety „Rose“

fractals25_4.png

Obrázek 4: Screenshot prvního demonstračního příkladu s Barnsleyovým fraktálem J1

4. Fraktální množina pojmenovaná Barnsley J2

Fraktální množina Barnsley J2, která používá stejný iterační vztah, jako minule popsaná množina Barnsley M2, po svém zobrazení velmi silně připomíná různé krystalické struktury, což je ostatně patrné z pátého a šestého obrázku. Iterační vztah použitý při výpočtu této množiny je možné zapsat pomocí následujícího pseudokódu:

    if (real(z)*imag(c) + real(c)*imag(z) >= 0)
        z(n+1) = (z-1)*c
    else
        z(n+1) = (z+1)*c
    if (|z|>2) break; 

Po přidání podmínky na ukončení iterační smyčky a rozpisu komplexních proměnných z a c na jejich reálné a imaginární složky získáme programový kód, který je již možné prakticky využít v céčkovských programech:

    zx2=zx*zx;                 // pomocné proměnné s druhými mocninami
    zy2=zy*zy;                 // složek real(z) a imag(z)
    if (zx2+zy2>4) break;      // test na ukončení iterační smyčky
    if (zx*cy0+zy*cx0>=0) {
        zxn=zx*cx0-zy*cy0-cx0;
        zyn=zx*cy0+zy*cx0-cy0;
    }
    else {
        zxn=zx*cx0-zy*cy0+cx0;
        zyn=zx*cy0+zy*cx0+cy0;
    }
    zx=zxn;                    // přepis nových hodnot do proměnné Z
    zy=zyn; 

Tento kód je použitý i v níže uvedené céčkovské funkci recalcJuliaJ2()

//-----------------------------------------------------------------------------
// Překreslení Juliovy verze Barnsleyovy fraktální množiny J2
//-----------------------------------------------------------------------------
void recalcJuliaJ2(pixmap *pix,                 // pixmapa pro vykreslování
                  int    maxiter,               // maximální počet iterací
                  double scale,                 // měřítko obrazce
                  double xpos,                  // posun obrazce
                  double ypos,
                  int    palette,               // barvová paleta
                  int    rf, int gf, int bf,    // příznaky barvových složek
                  double cx0,
                  double cy0)                   // aktuálně nastavená hodnota C
{
    double zx, zy, zx2, zy2, zxn, zyn;          // složky komplexní proměnné Z a Z^2
    double zx0, zy0;
    double cx, cy;
    double xmin, ymin, xmax, ymax;              // rohy vykreslovaného obrazce v komplexní
                                                // rovině
    int    x, y;                                // počitadla sloupců a řádků v pixmapě
    int    iter;                                // počitadlo iterací
    unsigned char r, g, b;

    calcCorner(xpos, ypos, scale, &xmin, &ymin, &xmax, &ymax);
    zy0=ymin;
    for (y=0; y<pix->height; y++) {             // pro všechny řádky v pixmapě
        zx0=xmin;
        for (x=0; x<pix->width; x++) {          // pro všechny pixely na řádku
            zx=zx0;
            zy=zy0;
            for (iter=0; iter<maxiter; iter++) {// iterační smyčka
                zx2=zx*zx;                      // zkrácený výpočet druhé mocniny složek Z
                zy2=zy*zy;
                if (zx2+zy2>4) break;
                if (zx*cy0+zy*cx0>=0) {
                    zxn=zx*cx0-zy*cy0-cx0;
                    zyn=zx*cy0+zy*cx0-cy0;
                }
                else {
                    zxn=zx*cx0-zy*cy0+cx0;
                    zyn=zx*cy0+zy*cx0+cy0;
                }
                zx=zxn;
                zy=zyn;
            }
            if (iter==0) iter++;
            mapPalette(palette, iter, &r, &g, &b);
            r=r*rf;                             // uživatelem řízené vynulování
            g=g*gf;                             // vybraných barvových složek
            b=b*bf;
            putpixel(pix, x, y, r, g, b);
            zx0+=(xmax-xmin)/pix->width;        // posun na další bod na řádku
        }
        zy0+=(ymax-ymin)/pix->height;           // posun na další řádek
    }
} 

fractals25_5.png

Obrázek 5: Barnsleyova fraktální množina J2

fractals25_6.png

Obrázek 6: Barnsleyova fraktální množina J2 s odlišnou konstantou C

5. Demonstrační příklad vykreslující fraktální množinu Barnsley J2

Céčkovská funkce recalcJuliaJ2(), jejíž výpis byl uveden v předchozí kapitole, je použita v dnešní druhé demonstrační aplikaci. Po překladu a spuštění této aplikace se objeví okno, v jehož levé části se nachází obraz Barnsleyho množiny M2 a v pravé části je obraz odpovídající množiny J2. Spodní část okna je vyhrazena pro výpis informací o podmínkách zobrazení, spolu se seznamem klávesových zkratek, které je možné použít pro změnu těchto podmínek. Pohybem kurzoru myši spolu se stlačeným tlačítkem, po levé polovině okna (tj. po ploše obrázku množiny M2) se mění hodnota komplexní konstanty c. Tato hodnota je použita při výpočtu fraktální množiny J2. Význam konstanty c je naprosto stejný jako u „klasické“ Mandelbrotovy a Juliovy množiny: pokud se bod odpovídající hodnotě c nachází uvnitř Mandelbrotovy množiny (v tomto případě množiny M2), je příslušná Juliova množina (J2) spojitá a naopak; pokud se bod nachází vně Mandelbrotovy množiny, je k ní příslušná Juliova množina tvořena Cantorovým prachem (Cantor Dust). Screenshot ze druhého demonstračního příkladu je zobrazen na sedmém obrázku.

fractals25_7.png

Obrázek 7: Screenshot druhého demonstračního příkladu s Barnsleyovým fraktálem J2

6. Fraktální množina pojmenovaná Barnsley J3

Posledním fraktálem pocházejícím od Michaela Barnsleye, který si budeme v této části seriálu popisovat, je fraktální množina nazývaná Barnsley J3. Při pohledu na obrázky 8, 9 a 10 zjistíme, že tento fraktál má – samozřejmě v závislosti na nastavené hodnotě proměnné c – velmi zajímavou a dosti chaotickou strukturu, kterou je možné dále zvýraznit vhodně zvolenou barvovou paletou. Mezi další vlastnosti tohoto fraktálu patří jeho nesymetričnost vůči souřadnicovým osám i vůči počátku – to je pro Juliovy množiny poměrně nezvyklé. V této množině je, stejně jako u minule popisované fraktální množiny Barnsley M3, použit iterační vztah, který je možné zapsat pomocí poměrně složitého pseudokódu:

    if (real(z(n) > 0)
        z(n+1) = (real(z(n))^2 - imag(z(n))^2 - 1)
               + i * (2*real(z((n)) * imag(z((n)))
    else
        z(n+1) = (real(z(n))^2 - imag(z(n))^2 - 1 + real(c) * real(z(n))
               + i * (2*real(z((n)) * imag(z((n)) + imag(c) * real(z(n))
    if (|z|>2) break; 

Po přepisu tohoto pseudokódu do programovacího jazyka C získáme následující sekvenci příkazů, kterou je možné přímo aplikovat do iterační smyčky:

    zx2=zx*zx;                  // pomocné proměnné s druhými mocninami
    zy2=zy*zy;                  // složek real(z) a imag(z)
    if (zx2+zy2>4) break;       // test na ukončení iterační smyčky
    if (zx>0) {
        zxn=zx2-zy2-1;
        zyn=2.0*zx*zy;
    }
    else {
        zxn=zx2-zy2-1+cx0*zx;
        zyn=2.0*zx*zy+cy0*zx;
    } 

Výše uvedená sekvence příkazů je použita ve funkci recalcJuliaJ3(), která má tvar:

//-----------------------------------------------------------------------------
// Překreslení Juliovy verze Barnsleyovy fraktální množiny J3
//-----------------------------------------------------------------------------
void recalcJuliaJ3(pixmap *pix,                 // pixmapa pro vykreslování
                  int    maxiter,               // maximální počet iterací
                  double scale,                 // měřítko obrazce
                  double xpos,                  // posun obrazce
                  double ypos,
                  int    palette,               // barvová paleta
                  int    rf, int gf, int bf,    // příznaky barvových složek
                  double cx0,
                  double cy0)                   // aktuálně nastavená hodnota C
{
    double zx, zy, zx2, zy2, zxn, zyn;          // složky komplexní proměnné Z a Z^2
    double zx0, zy0;
    double cx, cy;
    double xmin, ymin, xmax, ymax;              // rohy vykreslovaného obrazce v komplexní
                                                // rovině
    int    x, y;                                // počitadla sloupců a řádků v pixmapě
    int    iter;                                // počitadlo iterací
    unsigned char r, g, b;

    calcCorner(xpos, ypos, scale, &xmin, &ymin, &xmax, &ymax);
    zy0=ymin;
    for (y=0; y<pix->height; y++) {             // pro všechny řádky v pixmapě
        zx0=xmin;
        for (x=0; x<pix->width; x++) {          // pro všechny pixely na řádku
            zx=zx0;
            zy=zy0;
            for (iter=0; iter<maxiter; iter++) {// iterační smyčka
                zx2=zx*zx;                      // zkrácený výpočet druhé mocniny složek Z
                zy2=zy*zy;
                if (zx2+zy2>4) break;
                if (zx>0) {
                    zxn=zx2-zy2-1;
                    zyn=2.0*zx*zy;
                }
                else {
                    zxn=zx2-zy2-1+cx0*zx;
                    zyn=2.0*zx*zy+cy0*zx;
                }
                zx=zxn;
                zy=zyn;
            }
            mapPalette(palette, iter, &r, &g, &b);
            r=r*rf;                             // uživatelem řízené vynulování
            g=g*gf;                             // vybraných barvových složek
            b=b*bf;
            putpixel(pix, x, y, r, g, b);
            zx0+=(xmax-xmin)/pix->width;        // posun na další bod na řádku
        }
        zy0+=(ymax-ymin)/pix->height;           // posun na další řádek
    }
} 

fractals25_8.png

Obrázek 8: Barnsleyova fraktální množina J3

fractals25_9.png

Obrázek 9: Barnsleyova fraktální množina J3 s odlišnou konstantou C

fractals25_a.png

Obrázek 10: Další pohled na Barnsleyovu fraktální množinu J3

7. Demonstrační příklad vykreslující fraktální množinu Barnsley J3

Na jedenáctém obrázku je zobrazen screenshot pocházející z dnešního třetího a současně i posledního demonstračního příkladu. Tento příklad po svém spuštění vykreslí okno rozdělené na tři části stejným způsobem, jako tomu bylo u obou příkladů předchozích – viz třetí a pátou kapitolu. Pohybem kurzoru myši po levé polovině okna, ve které je zobrazena fraktální množina M3, se mění hodnota komplexní konstanty c, která následně ovlivní výpočet fraktální množiny J3 zobrazené v pravé polovině okna.

fractals25_b.png

Obrázek 11: Screenshot třetího demonstračního příkladu s Barnsleyovým fraktálem J3

8. Rozšiřující parametrizace Barnsleyho fraktálních množin

Již v několika předchozích částech tohoto seriálu jsme si ukázali, jakým způsobem je možné měnit tvar Mandelbrotových verzí různých fraktálních množin začleněním takzvané hodnoty „perturbation“ do iteračního výpočtu. Princip použitý u kvadratické a kubické Mandelbrotovy množiny lze implementovat i u Barnsleyho množin M1, M2 a M3, což vede k mnohdy radikální změně jejich tvarů. Princip aplikace hodnoty „perturbation“ již nebude vysvětlen na demonstračních příkladech, protože se jedná o velmi jednoduchý zásah do stávajícího kódu.

ict ve školství 24

9. Obsah dalšího pokračování tohoto seriálu

V následující části tohoto seriálu si popíšeme různé typy fraktálních množin nazvaných Magnet. Tento název je odvozen z jejich fyzikálního významu.

ikonka

Zajímá vás toto téma? Chcete se o něm dozvědět víc?

Objednejte si upozornění na nově vydané články do vašeho mailu. Žádný článek vám tak neuteče.

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.