Hlavní navigace

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

14. 3. 2006
Doba čtení: 11 minut

Sdílet

V dnešním pokračování seriálu o fraktálech používaných (nejenom) v počítačové grafice bude vysvětlen princip vytváření Mandelbrotových a Juliových množin s libovolnou reálnou mocninou použitou v jejich iterační smyčce. Také si ukážeme zajímavé animace Mandelbrotovy a Juliovy množiny při průběžné změně mocniny.

Obsah

1. Reálná mocnina použitá ve vzorci Zn+1=Znr+C
2. Mandelbrotova množina s reálnou mocninou použitou v iterační smyčce
3. Juliovy množiny s reálnou mocninou použitou v iterační smyčce
4. Animace Mandelbrotovy množiny při průběžné změně mocniny
5. Vztah mezi symetrií Mandelbrotovy i Juliovy množiny a mocninou hodnoty Znr
6. Komplexní mocnina použitá v iterační smyčce
7. Obsah dalšího pokračování tohoto seriálu

1. Reálná mocnina použitá ve vzorci Zn+1=Znr+C

Už v několika předchozích pokračováních tohoto seriálu jsme se zabývali tvorbou Mandelbrotových a Juliových množin, přičemž při jejich výpočtu se v iterační smyčce používal vztah Zn+1=Znk+C, kde k bylo celé kladné číslo. Prozatím jsme odvodili vztahy platné pro k=2, k=3 a k=4. Reálné a imaginární složky hodnoty Zk bylo možné vypočítat prostým umocněním rozpisu komplexního čísla (zre+izim)k s nás­ledným rozdělením výsledku na reálnou a imaginární část, jak je ostatně patrné z následujících rovnic (blíže viz devatenáctou a dvacátou část tohoto seriálu):

Re(Z2): zre2-zim2
Im(Z2): 2zrezim

Re(Z3): zre3-3zrezim2
Im(Z3): -zim3+3zre2zim

Re(Z4): zre4+zim4-6zre2zim2
Im(Z4): 4zre3zim-4zrezim3

Vztahy pro vyšší mocniny jsme si neuváděli, ostatně jednalo by se o stále složitější výrazy. Zajímavější a přínosnější bude odvození vztahu platného pro libovolnou mocninu komplexního čísla Z, a to nejenom mocninu celočíselnou, ale i reálnou – samozřejmě včetně záporných mocnin, protože i v oboru komplexních čísel platí, že z-r=1/zr, i když samotný výpočet převrácené hodnoty komplexního čísla je složitější než v oboru čísel reálných. Při odvození vztahu platného pro libovolnou mocninu je možné s výhodou použít alternativního zápisu komplexních čísel. Místo rozpisu komplexního čísla na jeho reálnou a imaginární složku je možné komplexní číslo zapsat také jako Z=|Z|e, kde |Z| je absolutní hodnota (velikost) komplexního čísla Z a φ je úhel v komplexní rovině počítaný od reálné osy. Tento alternativní zápis budeme nazývat exponenciálním tvarem.

Porovnáním číselných řad (rozvoje) pro exponenciální funkci eφ, sin φ a cos φ je možné dojít k závěru, že Z=|Z|e=|Z|(cos φ+i sin φ) – tento výsledek se ostatně dal odvodit i z geometrických vlastností komplexního čísla Z, které je v komplexní rovině představováno vektorem. Pro výpočet reálné mocniny libovolného komplexního čísla platí takzvaná Moivrova věta, která vychází ze vztahu:

(cos φ+i sin φ)r=cos(nφ)+i s­in(nφ)

což v exponenciálním tvaru odpovídá vztahu:

(e)n=eiφn

Tento vztah je možné rozšířit na libovolné komplexní číslo Z:

Zr=|Z|r(cos rφ+i sin rφ)

Tuto větu použijeme v demonstračních příkladech pro výpočet Mandelbrotových i Juliových mocnin využívajících reálnou mocninu ve své iterační smyčce.

2. Mandelbrotova množina s reálnou mocninou použitou v iterační smyčce

Při výpočtu a následném vykreslení Mandelbrotovy množiny je možné využít v předchozí kapitole uvedenou Moivrovu větu, avšak pouze za předpokladu, že dokážeme převést libovolnou komplexní hodnotu reprezentovanou svou reálnou a imaginární složkou na exponenciální tvar, na něj Moivrovu větu aplikovat a posléze komplexní číslo opět převést z exponenciálního tvaru na reálnou a imaginární složku. Pro převod komplexních čísel tedy vytvoříme pomocné funkce, které mohou vypadat následovně:

//-----------------------------------------------------------------------------
// Převod reálné a imaginární složky komplexního čísla na exponenciální tvar
//-----------------------------------------------------------------------------
void parts2exp(double cx, double cy, double *abs, double *phi)
{
    *abs=sqrt(cx*cx+cy*cy);
    *phi=atan2(cy, cx);
}

//-----------------------------------------------------------------------------
// Převod komplexního čísla z exponenciálního tvaru na reálnou a imaginární
// složku
//-----------------------------------------------------------------------------
void exp2parts(double abs, double phi, double *cx, double *cy)
{
    *cx=abs*cos(phi);
    *cy=abs*sin(phi);
} 

Výše uvedené funkce by bylo možné zapsat i jako makra, nebo – pro lepší přehlednost – jako inline (vkládané) funkce, které jsou v některých překladačích jazyka C, například i v GCC, podporovány. Dále si všimněte použití funkce atan2(), která dává korektní výsledky i v případě, že je druhý parametr nulový, což by při naivním použití matematické funkce atan() vedlo k dělení nulou. Kromě převodních funkcí parts2exp() a exp2parts() si ještě vytvoříme funkci aplikující Moivrovu větu na komplexní číslo zapsané v exponenciálním tvaru (pro jednoduchost se nezabýváme vrácením úhlu φ do rozsahu 0..2π, pro další výpočty to není nutné):

//-----------------------------------------------------------------------------
// Výpočet obecné neceločíselné mocniny komplexního čísla aplikací
// Moivrovy věty
//-----------------------------------------------------------------------------
void cplx_exp(double *abs, double *phi, double e)
{
      *abs=pow(*abs, e);
      *phi=e**phi;
} 

S využitím všech tří předchozích funkcí již můžeme zapsat funkci recalcMandelbrot(), která bude počítat a zobrazovat Mandelbrotovu množinu s libovolnou reálnou mocninou hodnoty Znr. Tato funkce bude mít tvar:

//-----------------------------------------------------------------------------
// Překreslení Mandelbrotovy množiny s libovolnou mocninou hodnoty Z(n)
//-----------------------------------------------------------------------------
void recalcMandelbrot(pixmap *pix,              // pixmapa pro vykreslování
                  int    maxiter,               // maximální počet iterací
                  double scale,                 // měřítko obrazce
                  double xpos,                  // posun obrazce
                  double ypos,
                  double power,                 // mocnina použitá při výpočtu Z(n)
                  int    palette,               // 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 cx, cy;                              // složky komplexní konstanty C
    double cx0, cy0;
    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);
    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;                          // nastavení nulového orbitu
            for (iter=0; iter<maxiter; iter++) {// iterační smyčka
                double abs, phi;
                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
                parts2exp(zx, zy, &abs, &phi);  // převod složek komplexního čísla na exp. tvar
                cplx_exp(&abs, &phi, power);    // výpočet mocniny komplexního čísla
                exp2parts(abs, phi, &zx, &zy);  // převod exp. tvaru komplexního čísla na složky
                zx+=cx;
                zy+=cy;                         // přičtení konstanty C
            }
            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);
            cx0+=(xmax-xmin)/pix->width;        // posun na další bod na řádku
        }
        cy0+=(ymax-ymin)/pix->height;           // posun na další řádek
    }
} 

Výše uvedená funkce recalcMandelbrot() je použita v dnešním prvním demonstračním příkladu. Ovládání tohoto demonstračního příkladu se provádí pomocí klávesnice; jednotlivé klávesové zkratky jsou vypsány v tabulce. Screenshoty získané z tohoto demonstračního příkladu jsou zobrazeny na prvním, druhém a třetím obrázku.

Ovlá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“
Z snížení hodnoty mocniny použité v iteračním vztahu o hodnotu 0,1
X zvýšení hodnoty mocniny použité v iteračním vztahu o hodnotu 0,1

fractals21_1
Obrázek 1: Mandelbrotova množina, v jejímž iteračním vztahu je použita mocnina 1,8

fractals21_2
Obrázek 2: Mandelbrotova množina, v jejímž iteračním vztahu je použita mocnina 2,3

fractals21_3
Obrázek 3: Mandelbrotova množina, v jejímž iteračním vztahu je použita mocnina 5,0

3. Juliovy množiny s reálnou mocninou použitou v iterační smyčce

Pro výpočet Juliových množin s reálnou mocninou použitou v iterační smyčce jsou použity stejné pomocné funkce jako u výpočtu Mandelbrotovy množiny. Jedná se o funkce parts2exp(), exp2parts() a cplx_exp(). Funkce určená pro výpočet a vykreslení Juliovy množiny, jež je mj. použita i v dnešním druhém demonstračním příkladu, bude vypadat následovně:

//-----------------------------------------------------------------------------
// Překreslení Juliovy množiny s libovolnou mocninou hodnoty Z(n)
//-----------------------------------------------------------------------------
void recalcJulia( pixmap *pix,                  // pixmapa pro vykreslování
                  int    maxiter,               // maximální počet iterací
                  double scale,                 // měřítko obrazce
                  double xpos,                  // posun obrazce
                  double ypos,
                  double power,                 // mocnina použitá při výpočtu Z(n)
                  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;                    // složky komplexní proměnné Z a Z^2
    double cx, cy;                              // složky komplexní konstanty C
    double zx0, zy0;
    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
            cx=cx0;                             // nastavit počáteční hodnotu Z(0)
            cy=cy0;
            zx=zx0;
            zy=zy0;
            for (iter=0; iter<maxiter; iter++) {// iterační smyčka
                double abs, phi;
                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
                parts2exp(zx, zy, &abs, &phi);  // převod složek komplexního čísla na exp. tvar
                cplx_exp(&abs, &phi, power);    // výpočet mocniny komplexního čísla
                exp2parts(abs, phi, &zx, &zy);  // převod exp. tvaru komplexního čísla na složky
                zx+=cx;
                zy+=cy;                         // přičtení konstanty C
            }
            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);
            zx0+=(xmax-xmin)/pix->width;        // posun na další bod na řádku
        }
        zy0+=(ymax-ymin)/pix->height;           // posun na další řádek
    }
} 

fractals21_4
Obrázek 4: Juliova množina, v jejímž iteračním vztahu je použita mocnina 6,0

fractals21_5
Obrázek 5: Juliova množina, v jejímž iteračním vztahu je použita mocnina 5,0

fractals21_6
Obrázek 6: Juliova množina, v jejímž iteračním vztahu je použita mocnina 1,1

4. Animace Mandelbrotovy množiny při průběžné změně mocniny

Ve druhé kapitole byl (mimo dalších informací) uveden i zdrojový kód funkce recalcMandelbrot(). Tato funkce je určená pro výpočet Mandelbrotovy množiny, přičemž v jejím parametru power je možné zadat libovolné reálné číslo, které je následně použito jako mocnina v iteračním vztahu. Funkci recalcMandelbrot() můžeme použít také ke generování zajímavých animací Mandelbrotovy množiny, při jejichž vytváření se hodnota předávaná do parametru power plynule mění, takže se ve výsledné animaci do značné míry eliminují velké rozdíly mezi sousedními snímky. Tento princip je implementován ve třetím demonstračním příkladu, který je, na rozdíl od obou příkladů předchozích, neinteraktivní. Po spuštění tohoto příkladu se vytvoří sekvence souborů typu TGA (Targa), které je možné vhodným programem spojit a zakódovat do video streamu.

Třetí demonstrační příklad byl použit pro vytvoření této zajímavé animace, která obsahuje celkem 240 snímků, každý o rozlišení 352×288 pixelů. Animace je zakódována do video streamu podle normy MPEG-1, takže je ji možné přehrát s prakticky jakýmkoli (i starším) přehrávačem.

5. Vztah mezi symetrií Mandelbrotovy i Juliovy množiny a mocninou hodnoty Znr

Od začátku našeho povídání o fraktálech vytvářených v komplexní rovině pomocí iteračního vztahu Zn+1=Znk+C, kde k je celé kladné číslo, jste si mohli povšimnout, že symetrie Juliových množin přesně odpovídá číslu k a symetrie Mandelbrotových množin je vždy o jedničku menší než k. Samozřejmě to není náhoda, tento fenomén totiž vychází z vlastností komplexních čísel a především mocnině použité v iteračním vztahu, protože při mocnění se komplexní číslo v komplexní rovině otáčí okolo počátku komplexní roviny (a mění se i jeho velikost, tj. vzdálenost od počátku), což je také patrné z výše uvedeného vztahu vycházejícího z Moivrovy věty. Rozdíl mezi Mandelbrotovými a Juliovými množinami spočívá pouze v odlišném nastavení počátečních podmínek před každou iterační smyčkou a jiné nastavení hodnoty aditivní komplexní konstanty C. Ale právě odlišné počáteční podmínky a jiná hodnota C mají za následek rozdíl v symetriích obou fraktálních množin. Na sedmém obrázku jsou zobrazeny Mandelbrotovy a Juliovy množiny pro k=2, k=3, k=4 a k=5.

fractals21_7
Obrázek 7: Mandelbrotovy a Juliovy množiny pro k=2, k=3, k=4 a k=5

6. Komplexní mocnina použitá v iterační smyčce

Ve všech předchozích příkladech provádějících výpočet a následné vykreslení Mandelbrotových a Juliových množin jsme v iterační smyčce používali „pouze“ reálnou mocninu, tj. ve vztahu Zn+1=Znr+C bylo za r dosazeno reálné číslo, a to číslo kladné a teoreticky i záporné. U klasických kvadratických množin platilo r=2, u množin kubických r=3 a v dnešním pokračování jsme si v předchozích kapitolách ukázali tvorbu Mandelbrotových i Juliových množin pro libovolné r>0. Vztah použitý v iterační smyčce je však možné dále rozšířit tak, že se místo reálné mocniny použije mocnina komplexní, tj. bude se používat vztah Zn+1=Znw+C, kde w je libovolné komplexní číslo. Tento velmi obecný vztah používá například známý program FractInt pro generování dalších neobvyklých tvarů Mandelbrotových i Juliových množin. Odvození iteračního vztahu s komplexní mocninou si v tomto článku již nebudeme uvádět, protože je matematicky náročnější.

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

V následujícím pokračování tohoto seriálu si popíšeme další zajímavé fraktální množiny, které je možné vytvořit v komplexní rovině. Vysvětlíme si princip výpočtu a vykreslení Newtonovy množiny a fraktálů pojmenovaných podle svého fyzikálního významu Magnet.

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.

Byl pro vás článek přínosný?

Autor článku

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