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

1. 2. 2006
Doba čtení: 13 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 předchozí díl, ve kterém byly popsány některé často používané techniky vybarvení vnitřních bodů Mandelbrotovy množiny. Dnes se naopak budeme zabývat různými způsoby vybarvování bodů, které leží vně tohoto velmi zajímavého fraktálního útvaru.

Obsah

1. Možnosti obarvení bodů ležících vně Mandelbrotovy množiny
2. Použití barvové palety
3. Barva odvozená od hodnoty reálné složky orbitu
4. Barva odvozená od hodnoty imaginární složky orbitu
5. Barva odvozená od součtu reálné a imaginární složky orbitu
6. Barva odvozená od úhlu orbitu
7. Obsah dalšího pokračování tohoto seriálu

1. Možnosti obarvení bodů ležících vně Mandelbrotovy mno­žin

Podobně jako při výpočtu barev bodů ležících uvnitř Mandelbrotovy množiny (bližší informace jsou uvedeny v předchozím dílu tohoto seriálu), i při nastavování barev bodů ležících vně této množiny můžeme brát v potaz hodnotu orbitu, tj. aktuální komplexní hodnotu zn. Kromě toho je také možné využít znalosti o počtu iterací iter, po kterých došlo ke splnění podmínky divergence: |zn|>2 (pokud by tato podmínka splněna nebyla, ležel by daný bod uvnitř Mandelbrotovy množiny). Možností, jak alespoň trochu smysluplně zkombinovat komplexní hodnotu zn a počet iterací iter, je samozřejmě velmi mnoho, v dalších kapitolách si popíšeme pouze nejpoužívanější metody. Ve třetí kapitole bude pojednáno o metodě využívající reálnou složku orbitu, v kapitole čtvrté o metodě využívající imaginární složku orbitu. Metoda využívající současně reálnou i imaginární složku bude popsána v kapitole páté a konečně v šesté kapitole bude pojednáno o metodě vypočítávající úhel orbitu. Před vysvětlením všech těchto metod si však ukažme příklad implementující vyhledávací tabulky s barvovými paletami.

2. Použití barvové palety

Již v předchozích částech tohoto seriálu jsme si řekli, že jednou ze základních metod zvýraznění dynamických systémů ležících v komplexní rovině, je použití barvové palety, tj. běžné vyhledávací tabulky obsahující barvové složky RGB (Red, Green, Blue). Vhodně zvolená barvová paleta také slouží k dosažení „estetického“ vzhledu zobrazeného fraktálu. V dnešním prvním demonstračním příkladu (15.1) je ukázáno použití barvových palet, které mají délku 256 položek. Tyto barvové palety byly převzaty ze známého programu FractInt. V následující tabulce jsou jednotlivé barvové palety vyjmenovány spolu s jejich autory (u některých barvových palet jsem se však jména autorů nedozvěděl):

Barvové palety
Název palety Autor
Blues Daniel Egnar
Greens Tom Schumm
Ice Tom Schumm
Gold autor neznámý
Phong autor neznámý
Rose autor neznámý
Mandman Jussi Kantola
Juteblue Jussi Kantola
Jutemap Jussi Kantola
Jutes Jussi Kantola

Tyto barvové palety jsou přímo uloženy do zdrojového kódu prvního demonstračního příkladu ve formě statických polí, ze kterých je možné po zadání indexu (který běžně odpovídá počtu iterací) získat hodnoty jednotlivých barvových položek RGB. Ve zdrojovém kódu stojí za povšimnutí způsob deklarace polí s barvovými paletami. Pokud by v této deklaraci chyběl zdánlivě nepodstatný modifikátor static, celý program by se několikanásobně zpomalil, protože by pro každý pixel docházelo ke kopiím všech polí z kódového segmentu na zásobník. Ovládání prvního demonstračního příkladu je sepsáno v následující tabulce:

Ovldádání
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
šipky posun obrazce ve směru šipek (o cca pět procent)
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í)
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“

Zdrojový kód prvního demonstračního příkladu je dostupný ke stažení, k dispozici je i HTML verze se zvýrazněním syntaxe. Procedura, která provádí výpočet a zobrazení Mandelbrotovy množiny s pomocí předdefinovaných barvových palet, může být zapsána takto:

//-----------------------------------------------------------------------------
// Překreslení Mandelbrotovy množiny s využitím vybrané barvové palety
//-----------------------------------------------------------------------------
void recalcMandelbrot1( 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,               // vybraná barvová paleta
                  int    rf, int gf, int bf)    // příznaky barvových složek
{
    double zx, zy, zx2, zy2;                    // složky komplexní proměnné Z a Z^2
    double cx0, cy0;
    double cx, cy;                              // složky komplexní konstanty C
    int    x, y;                                // počitadlo sloupců a řádků v pixmapě
    int    iter;                                // počitadlo iterací
    double xmin, ymin, xmax, ymax;              // rohy vykreslovaného obrazce v komplexní
                                                // rovině
    unsigned char r, g, b;                      // barvové složky pixelu

    calcCorner(xpos, ypos, scale, &xmin, &ymin, &xmax, &ymax);
    cy0=ymin;
    for (y=0; y<pix->height; y++) {             // pro všechny řádky v pixmapě
        cx0=xmin;
        for (x=0; x<pix->width; x++) {          // pro všechny pixely na řádku
            cx=cx0;                             // nastavit počáteční hodnotu Z(0)
            cy=cy0;
            zx=zy=0.0;                          // bude se počítat orbit nuly
            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.0) break;         // kontrola překročení meze divergence
                zy=2.0*zx*zy+cy;                // výpočet Z(n+1)
                zx=zx2-zy2+cx;
            }
            if (iter==maxiter)                  // pixely uvnitř Mandelbrotovy
                r=g=b=0;                        // množiny jsou černé
            else                                // výpočet barev podle počtu iterací
                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);       // vykreslení pixelu
            cx0+=(MAXX-MINX)/pix->width;        // posun na další bod na řádku
        }
        cy0+=(MAXY-MINY)/pix->height;           // posun na další řádek
    }
} 

fractals15_1
Obrázek 1: Obrázek výřezu Mandelbrotovy množiny vykreslená s využitím barvové palety „Blues“

fractals15_2
Obrázek 2: Obrázek výřezu Mandelbrotovy množiny vykreslená s využitím barvové palety „Gold“

fractals15_3
Obrázek 3: Obrázek výřezu Mandelbrotovy množiny vykreslená s využitím barvové palety „Mandmap“

fractals15_4
Obrázek 4: Obrázek výřezu Mandelbrotovy množiny vykreslená s využitím barvové palety „Jutemap“

3. Barva odvozená od hodnoty reálné složky orbitu

Jednou z dostupných a snadno implementovatelných možností, jak využít znalosti o koncové hodnotě orbitu, tj. o hodnotě zn, je zvýraznění pouze reálné složky zn. Na první pohled velmi jednoduchá metoda vede k tomu, že se – především při zobrazení detailů Mandelbrotovy množiny – na obrázcích začínají objevovat pruhy, které při správně nastavené barvové paletě obrázek činí zajímavějším. Tvorba pruhů souvisí s velkou citlivostí iteračního výpočtu na počátečních podmínkách. Při výpočtu barvy bodů je možné brát do úvahy buď pouze reálnou složku komplexní hodnoty zn, nebo se k této hodnotě přičte počet dosažených iterací. Procedura, která provádí výpočet barev právě popsanou metodou (s voláním funkce mapPalette() pro aplikaci barvové palety), může vypadat následovně:

//-----------------------------------------------------------------------------
// Překreslení Mandelbrotovy množiny s využitím vybrané barvové palety.
// Při výpočtu barev bodů ležících vně Mandelbrotovy množiny se využívá
// i informace o reálné složce orbitu.
//-----------------------------------------------------------------------------
void recalcMandelbrot2( 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,               // vybraná barvová paleta
                  int    rf, int gf, int bf)    // příznaky barvových složek
{
    double zx, zy, zx2, zy2;                    // složky komplexní proměnné Z a Z^2
    double cx0, cy0;
    double cx, cy;                              // složky komplexní konstanty C
    int    x, y;                                // počitadlo sloupců a řádků v pixmapě
    int    iter;                                // počitadlo iterací
    double xmin, ymin, xmax, ymax;              // rohy vykreslovaného obrazce v komplexní
                                                // rovině
    unsigned char r, g, b;                      // barvové složky pixelu

    calcCorner(xpos, ypos, scale, &xmin, &ymin, &xmax, &ymax);
    cy0=ymin;
    for (y=0; y<pix->height; y++) {             // pro všechny řádky v pixmapě
        cx0=xmin;
        for (x=0; x<pix->width; x++) {          // pro všechny pixely na řádku
            cx=cx0;                             // nastavit počáteční hodnotu Z(0)
            cy=cy0;
            zx=zy=0.0;                          // bude se počítat orbit nuly
            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.0) break;         // kontrola překročení meze divergence
                zy=2.0*zx*zy+cy;                // výpočet Z(n+1)
                zx=zx2-zy2+cx;
            }
            if (iter==maxiter)                  // pixely uvnitř Mandelbrotovy
                r=g=b=0;                        // množiny jsou černé
            else                                // výpočet barev podle počtu iterací
                mapPalette(palette, iter+10.0*zx, &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);       // vykreslení pixelu
            cx0+=(MAXX-MINX)/pix->width;        // posun na další bod na řádku
        }
        cy0+=(MAXY-MINY)/pix->height;           // posun na další řádek
    }
} 

Výše uvedená procedura recalcMandelbrot2() je použita i v dnešním druhém demonstračním příkladu (zdrojový kód a HTML verze se zvýrazněním syntaxe). Po překladu a spuštění tohoto demonstračního příkladu se zobrazí Mandelbrotova množina, ve které jsou body ležící mimo množinu zobrazeny pomocí kombinace reálné složky orbitu a počtu dosažených iterací. Body uvnitř množiny jsou zobrazeny barvou černou. Podobně jako v prvním demonstračním příkladu, i zde je možné pomocí kláves [0]-[9] zvolit jednu z deseti přednastavených barvových palet. Ovládání tohoto demonstračního příkladu je stejné jako u příkladu prvního.

fractals15_5
Obrázek 5: Screenshot druhého demonstračního příkladu

4. Barva odvozená od hodnoty imaginární složky orbitu

Odvození barvy pixelu od hodnoty imaginární složky orbitu je prakticky stejné, jako v předešlém případě, pouze se bere do úvahy imaginární složka zn a nikoli složka reálná. Výsledkem však je odlišný obraz fraktálu, což je ukázáno i na šestém obrázku, který se od pátého obrázku odlišuje v posunech barvy – pro ilustraci rozdílů je ideální „přepínání“ mezi těmito obrázky v prohlížeči rastrových obrázků. Procedura, která provádí výpočet barev s využitím imaginární složky orbitu, vypadá následovně:

//-----------------------------------------------------------------------------
// Překreslení Mandelbrotovy množiny s využitím vybrané barvové palety.
// Při výpočtu barev bodů ležících vně Mandelbrotovy množiny se využívá
// i informace o imaginární složce orbitu.
//-----------------------------------------------------------------------------
void recalcMandelbrot3( 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,               // vybraná barvová paleta
                  int    rf, int gf, int bf)    // příznaky barvových složek
{
    double zx, zy, zx2, zy2;                    // složky komplexní proměnné Z a Z^2
    double cx0, cy0;
    double cx, cy;                              // složky komplexní konstanty C
    int    x, y;                                // počitadlo sloupců a řádků v pixmapě
    int    iter;                                // počitadlo iterací
    double xmin, ymin, xmax, ymax;              // rohy vykreslovaného obrazce v komplexní
                                                // rovině
    unsigned char r, g, b;                      // barvové složky pixelu

    calcCorner(xpos, ypos, scale, &xmin, &ymin, &xmax, &ymax);
    cy0=ymin;
    for (y=0; y<pix->height; y++) {             // pro všechny řádky v pixmapě
        cx0=xmin;
        for (x=0; x<pix->width; x++) {          // pro všechny pixely na řádku
            cx=cx0;                             // nastavit počáteční hodnotu Z(0)
            cy=cy0;
            zx=zy=0.0;                          // bude se počítat orbit nuly
            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.0) break;         // kontrola překročení meze divergence
                zy=2.0*zx*zy+cy;                // výpočet Z(n+1)
                zx=zx2-zy2+cx;
            }
            if (iter==maxiter)                  // pixely uvnitř Mandelbrotovy
                r=g=b=0;                        // množiny jsou černé
            else                                // výpočet barev podle počtu iterací
                mapPalette(palette, iter+10.0*zy, &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);       // vykreslení pixelu
            cx0+=(MAXX-MINX)/pix->width;        // posun na další bod na řádku
        }
        cy0+=(MAXY-MINY)/pix->height;           // posun na další řádek
    }
} 

Procedura recalcMandelbrot3() je použita ve třetím demonstračním příkladu (zdrojový kód a HTML verze se zvýrazněním syntaxe), jehož ovládání je shodné s příkladem prvním i druhým.

fractals15_6
Obrázek 6: Screenshot třetího demonstračního příkladu

5. Barva odvozená od součtu reálné a imaginární složky orbitu

Obě předchozí metody určené pro výpočet barvy bodů ležících vně Mandelbrotovy množiny je možné sloučit do metody jediné, ve které se počítá jak s reálnou, tak i imaginární složkou orbitu, tj. s hodnotou real(zn) a imag(zn). Při vhodně zvolené barvové paletě je možné dosáhnout plastického vzhledu okrajů Mandelbrotovy množiny. Dále uvedená procedura, která počítá s reálnou i imaginární složkou orbitu, je použita ve třetím demonstračním příkladu (zdrojový kód a příslušná HTML verze). Na sedmém obrázku, jenž je pomocí tohoto příkladu vytvořen, si všimněte zejména obloukovitých oblastí v izoplochách vykreslených původně barvou s jedním odstínem.

//-----------------------------------------------------------------------------
// Překreslení Mandelbrotovy množiny s využitím vybrané barvové palety.
// Při výpočtu barev bodů ležících vně Mandelbrotovy množiny se využívá
// informace o reálné a současně i imaginární složce orbitu.
//-----------------------------------------------------------------------------
void recalcMandelbrot4( 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,               // vybraná barvová paleta
                  int    rf, int gf, int bf)    // příznaky barvových složek
{
    double zx, zy, zx2, zy2;                    // složky komplexní proměnné Z a Z^2
    double cx0, cy0;
    double cx, cy;                              // složky komplexní konstanty C
    int    x, y;                                // počitadlo sloupců a řádků v pixmapě
    int    iter;                                // počitadlo iterací
    double xmin, ymin, xmax, ymax;              // rohy vykreslovaného obrazce v komplexní
                                                // rovině
    unsigned char r, g, b;                      // barvové složky pixelu

    calcCorner(xpos, ypos, scale, &xmin, &ymin, &xmax, &ymax);
    cy0=ymin;
    for (y=0; y<pix->height; y++) {             // pro všechny řádky v pixmapě
        cx0=xmin;
        for (x=0; x<pix->width; x++) {          // pro všechny pixely na řádku
            cx=cx0;                             // nastavit počáteční hodnotu Z(0)
            cy=cy0;
            zx=zy=0.0;                          // bude se počítat orbit nuly
            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.0) break;         // kontrola překročení meze divergence
                zy=2.0*zx*zy+cy;                // výpočet Z(n+1)
                zx=zx2-zy2+cx;
            }
            if (iter==maxiter)                  // pixely uvnitř Mandelbrotovy
                r=g=b=0;                        // množiny jsou černé
            else                                // výpočet barev podle počtu iterací
                mapPalette(palette, iter+10.0*(zx+zy), &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);       // vykreslení pixelu
            cx0+=(MAXX-MINX)/pix->width;        // posun na další bod na řádku
        }
        cy0+=(MAXY-MINY)/pix->height;           // posun na další řádek
    }
} 

fractals15_7
Obrázek 7: Screenshot čtvrtého demonstračního příkladu

6. Barva odvozená od úhlu orbitu

Již v předchozím pokračování tohoto seriálu jsme si ukázali, že při zvýrazňování struktury vnitřních částí Mandelbrotovy množiny je možné barvu vnitřních pixelů spočítat na základě úhlu koncového orbitu. Stejnou metodu je možné použít i při vykreslování pixelů, které leží vně této množiny. Úhel mezi koncovou hodnotou orbitu a reálnou či imaginární osou Gaussovy roviny je možné vypočítat například pomocí céčkovské funkce atan2(), která (na rozdíl od funkce atan()) zajistí i limitní případ, ve kterém by se provádělo dělení nulou. Dále uvedená procedura recalcMandelbrot5() je použita v pátém demonstračním příkladu. Zde se nachází zdrojový kód, příslušná HTML stránka je uložena zde.

//-----------------------------------------------------------------------------
// Překreslení Mandelbrotovy množiny s využitím vybrané barvové palety.
// Při výpočtu barev bodů ležících vně Mandelbrotovy množiny se využívá
// i o úhlu koncové hodnoty orbitu (počítá se úhel vůči imaginární ose).
//-----------------------------------------------------------------------------
void recalcMandelbrot5( 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,               // vybraná barvová paleta
                  int    rf, int gf, int bf)    // příznaky barvových složek
{
    double zx, zy, zx2, zy2;                    // složky komplexní proměnné Z a Z^2
    double cx0, cy0;
    double cx, cy;                              // složky komplexní konstanty C
    int    x, y;                                // počitadlo sloupců a řádků v pixmapě
    int    iter;                                // počitadlo iterací
    double xmin, ymin, xmax, ymax;              // rohy vykreslovaného obrazce v komplexní
                                                // rovině
    unsigned char r, g, b;                      // barvové složky pixelu

    calcCorner(xpos, ypos, scale, &xmin, &ymin, &xmax, &ymax);
    cy0=ymin;
    for (y=0; y<pix->height; y++) {             // pro všechny řádky v pixmapě
        cx0=xmin;
        for (x=0; x<pix->width; x++) {          // pro všechny pixely na řádku
            cx=cx0;                             // nastavit počáteční hodnotu Z(0)
            cy=cy0;
            zx=zy=0.0;                          // bude se počítat orbit nuly
            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.0) break;         // kontrola překročení meze divergence
                zy=2.0*zx*zy+cy;                // výpočet Z(n+1)
                zx=zx2-zy2+cx;
            }
            if (iter==maxiter)                  // pixely uvnitř Mandelbrotovy
                r=g=b=0;                        // množiny jsou černé
            else                                // výpočet barev podle počtu iterací
                mapPalette(palette, iter+5.0*atan2(zx, zy), &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);       // vykreslení pixelu
            cx0+=(MAXX-MINX)/pix->width;        // posun na další bod na řádku
        }
        cy0+=(MAXY-MINY)/pix->height;           // posun na další řádek
    }
} 

bitcoin_skoleni

fractals15_8
Obrázek 8: Screenshot pátého demonstračního příkladu

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

V dalším pokračování tohoto seriálu se budeme zabývat dvěma dalšími problémy: způsobem animace „průletu“ Mandelbrotovou množinou a takzvanou mapou Mandelbrotovy množiny, tj. kódováním nejtypičtějších tvarů, které je možné v této nekonečně členité množině nalézt.

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.