16 August, 2025

Face snapping

Operáciou, ktorá je podobná operácii vrcholového prichytávania, je prichytávanie polygónu (Face). Niekde sa uvádza Face ako štvorec (obdĺžnik), ktorý súvisí s dvoma trojuholníkmi a niekde sa uvádza ako polygón. V každom prípade sa jedná o synonymum. Vytvoriť teda Face snapping je už na základe Vertex snappingu relatívne jednoduchá záležitosť. Pri algoritme Vertex snappingu sa pri nájdení jedného vertexu, ostatné vertexy dohľadávali. V tomto prípade to bude analogické. Zistia sa súradnice (x, y, z) jednotlivých vertexov pre štvorec (Face) a nad týmito súradnicami vertexov sa postupne budú generovať úsečky, v prípade Unity to bude predstavovať trieda LineRenderer. Určite by bolo možné použiť aj objekt štvorca, ak by existovala nejaká funkcia pre tento objekt, avšak aj štvorec obsahuje 4 vrcholy, ktoré by sme museli definovať, priradiť. Takže z hľadiska výpočtového času by bolo vykreslenie za pomoci úsečiek alebo štvorca ekvivalentné. Je zaujímavé spomenúť jednu vec. V počítačovej grafike pojem hrana ako taká prakticky neexistuje. Je len záležitosťou indexu v indices poli. Teda, nie je prakticky možné označiť hranu explicitne z nejakých súradníc tak ako je možné označiť jednotlivé vrcholy za pomoci špecifických súradníc, x, y, z. Mohla by existovať nejaká metaprezentácia, napr. grafová reprezentácia z matematiky, ale v konečnom dôsledku by musela byť táto reprezentácia pregenerovaná do indices poľa. Pretože na vykreslenie objektov v 3D súradnicovej sústave v priemyselných grafických štandardoch, DirectX/OpenGL a mnohých iných, je nutné definovať vrcholy a hrany. Aj keď definujeme hranu, nevieme ju explicitne označiť akosi jednotkovo. Že teda zavoláme nejakú funkciu, kde budú uložené hrany a po dopyte napr. s označením hrany H7, nám systém vráti hranu H7. Mohlo by to existovať, ale museli by sme si my dodatočne doimplementovať vrstvu, ktorá by toto riešila. Takže z toho vyplýva, že na označenie hrany budeme potrebovať 2 vrcholy, pretože hrana ako taká je definovaná dvoma vrcholmi.

Moje programové kódy pre LineFace a MeshFace snapping:

(GIT, LineFace: https://github.com/indicatrix-git/tycoon-terrain-enhanced/blob/main/SnapLineFace.cs)

(GIT, MeshFace: https://github.com/indicatrix-git/tycoon-terrain-enhanced/blob/main/SnapMeshFace.cs)

public Vector3 SnapLineFace(Vector3 hitPoint)
    {
        float[] Xaxis = new float[4];
        float[] Yaxis = new float[4];
        float[] Zaxis = new float[4];

        int xx = (int)hitPoint.x; float x = hitPoint.x - xx;
        int zz = (int)hitPoint.z; float z = hitPoint.z - zz;


        if (x >= 0.0f && x <= 1.0f)
        {
            Xaxis[0] = xx;
            Xaxis[1] = xx;
            Xaxis[2] = xx + 1;
            Xaxis[3] = xx + 1;
        }


        if (z >= 0.0f && z <= 1.0f)
        {
            Zaxis[0] = zz;
            Zaxis[1] = zz + 1;
            Zaxis[2] = zz + 1;
            Zaxis[3] = zz;
        }


        Yaxis[0] = FunctionalValueY(Xaxis[0], Zaxis[0]);
        Yaxis[1] = FunctionalValueY(Xaxis[1], Zaxis[1]);
        Yaxis[2] = FunctionalValueY(Xaxis[2], Zaxis[2]);
        Yaxis[3] = FunctionalValueY(Xaxis[3], Zaxis[3]);


        lineRenderer01.SetPosition(0, new Vector3(Xaxis[0], Yaxis[0], Zaxis[0]));
        lineRenderer01.SetPosition(1, new Vector3(Xaxis[1], Yaxis[1], Zaxis[1]));

        lineRenderer02.SetPosition(0, new Vector3(Xaxis[1], Yaxis[1], Zaxis[1]));
        lineRenderer02.SetPosition(1, new Vector3(Xaxis[2], Yaxis[2], Zaxis[2]));

        lineRenderer03.SetPosition(0, new Vector3(Xaxis[2], Yaxis[2], Zaxis[2]));
        lineRenderer03.SetPosition(1, new Vector3(Xaxis[3], Yaxis[3], Zaxis[3]));

        lineRenderer04.SetPosition(0, new Vector3(Xaxis[0], Yaxis[0], Zaxis[0]));
        lineRenderer04.SetPosition(1, new Vector3(Xaxis[3], Yaxis[3], Zaxis[3]));


        return GetCenterFace(hitPoint);
    }



    public Vector3 SnapMeshFace(Vector3 hitPoint)
    {
        Vector3[] verticesFace;
        Vector3[] normalsFace;
        Vector2[] uvFace;
        int[] trianglesFace;

        float[] Xaxis = new float[4];
        float[] Yaxis = new float[4];
        float[] Zaxis = new float[4];

        int xx = (int)hitPoint.x; float x = hitPoint.x - xx;
        int zz = (int)hitPoint.z; float z = hitPoint.z - zz;

        float dy = 0.01f;

        if (x >= 0.0f && x <= 1.0f)
        {
            Xaxis[0] = xx;
            Xaxis[1] = xx;
            Xaxis[2] = xx + 1;
            Xaxis[3] = xx + 1;
        }


        if (z >= 0.0f && z <= 1.0f)
        {
            Zaxis[0] = zz;
            Zaxis[1] = zz + 1;
            Zaxis[2] = zz + 1;
            Zaxis[3] = zz;
        }


        Yaxis[0] = FunctionalValueY(Xaxis[0], Zaxis[0]);
        Yaxis[1] = FunctionalValueY(Xaxis[1], Zaxis[1]);
        Yaxis[2] = FunctionalValueY(Xaxis[2], Zaxis[2]);
        Yaxis[3] = FunctionalValueY(Xaxis[3], Zaxis[3]);


        verticesFace = new Vector3[]
        {
            new Vector3(Xaxis[0], Yaxis[0] + dy, Zaxis[0]),
            new Vector3(Xaxis[1], Yaxis[1] + dy, Zaxis[1]),
            new Vector3(Xaxis[2], Yaxis[2] + dy, Zaxis[2]),
            new Vector3(Xaxis[3], Yaxis[3] + dy, Zaxis[3])
        };


        trianglesFace = new int[]
        {
            0, 1, 2,
            2, 3, 0
        };


        normalsFace = new Vector3[]
        {
            -Vector3.forward,
            -Vector3.forward,
            -Vector3.forward,
            -Vector3.forward
        };


        uvFace = new Vector2[]
        {
            new Vector2(Xaxis[0], Zaxis[0]),
            new Vector2(Xaxis[1], Zaxis[1]),
            new Vector2(Xaxis[2], Zaxis[2]),
            new Vector2(Xaxis[3], Zaxis[3])
        };


        meshFace.Clear();
        meshFace.vertices = verticesFace;
        meshFace.triangles = trianglesFace;
        meshFace.normals = normalsFace;
        meshFace.uv = uvFace;
        meshFace.RecalculateNormals();

        return GetCenterFace(hitPoint);
    }
Uvedený kód je platný pre tzv. LineFace a tzv. MeshFace. Je to analogické, v prvom prípade máme Face pozostávajúci z 4 úsečiek a v druhom prípade máme Face pozostávajúci z Mesh-u. Mesh je pojem z počítačovej grafiky, ktorý označuje plochu, ktorá môže mať definovanú farbu alebo aj textúru (obrázok). V prípade LineFace je to z kódu pochopiteľné v zmysle toho, že teda nájdeme 4 vertexy a tieto 4 vertexy priradíme jednotlivým úsečkám. V prípade MeshFace je to analogické, kde vytvoríme bázicky vertices a indices (triangleFace) a dodatočne aj UV mapy a normálu. Rovnako pre 4 vertexy v rámci tvorby Mesh plôšky, priradíme jednotlivé súradnice týmto vertexom. Pre indices maticu už len priradíme priradené 4 vertexy v smere hodinových ručičiek v niektorej z 3 konštelácií podľa definície tvorby Mesh plôšky. Pri VerticesFace figuruje malý prírastok dy pre y súradnicu (y = 0.01). Je to z toho dôvodu, aby bolo vidieť samotný Face. Ak by tam tento prírastok nebol, tak úroveň plôšky, Face by bola na rovnakej úrovni ako úroveň samotných vertexov pre terén, ktorý obsahuje tiež textúru. Viditeľnosť dvoch Mesh objektov s textúrou s rovnakými súradnicami znemožňuje viditeľnosť jedného z nich, resp. v limitnom prípade by figurovala Mesh plôška pod úrovňou terénu a teda toto je najpravdepodobnejší dôvod toho, že sa samotná Face plôška nezobrazí na teréne, ale tesne pod ňou. Z toho dôvodu som zadal prírastok dy na y súradnicu. Pridávam ešte kód pre získanie funkčnej hodnoty zo súradníc x a z:
float FunctionalValueY(float snapX, float snapZ)
    {
        float snapY = 0;

        for (int Ci = 0, Cz = 0; Cz <= TerrainManager.instance.terrainWidth; Cz++)
        {
            for (int Cx = 0; Cx <= TerrainManager.instance.terrainWidth; Cx++, Ci++)
            {
                if (TerrainManager.instance.coords[Ci].x == snapX &&
                    TerrainManager.instance.coords[Ci].z == snapZ)
                {
                    snapY = TerrainManager.instance.coords[Ci].y;
                }
            }
        }
        return snapY;
    }
Napísal som to úplne intuitívne, tak jednoducho. Samozrejme, je možné to napísať sofistikovanejšie, ale minimálne v prvej verzii môjho kódu to nechávam zatiaľ v takejto základnej forme a možno to už bude aj konečná forma. V nasledujúcom videu to popisujem podrobnejšie:

14 August, 2025

Vertex snapping

Relatívne častou operáciou v počítačovej grafike je prichytávanie kurzoru myši k vertexu v 3D scéne. Niektoré grafické engine poskytujú metódy na realizáciu tohto problému vo forme vstavaných funkcionalít. Napr. grafický engine Unity používa svoje prvky na realizáciu snappingu, teda prichytávania objektov vo všeobecnosti. Osobne som vedel o tom, že Unity niečo také poskytuje, pretože som najprv hľadal množstvo materiálov na túto tému. Avšak, potreboval som naprogramovať snapping bez obslúžnych funkcionalít grafických engine. Prečo ? Pretože použitie funkcionalít z rôznych enginov je vzťahované na daný engine, nie je to univerzálne riešenie. Po vzore programovania terénu ma napadlo použiť systém priesečníka bodu na realizáciu prichytávania bodov. Povedal som si, že akonáhle mám bod prieniku na Face, plôške, tak tento bod je možné prepočítať na okolité body vertexov, pretože musí platiť, že daný bod sa nachádza presne na rovine zarovno s okolitými vertexmi, čo je zrejmé. Ak to takto je, tak stačí abstrahovať bod prieniku a porovnať tento bod z okolitými bodmi, vertexmi. Zámerne píšem slovo abstrahovať a nie zaokrúhľovať. Uvediem môj programový kód: (GIT: https://github.com/indicatrix-git/tycoon-terrain-enhanced/blob/main/SnapVertex.cs)
public Vector3 SnapVertex(Vector3 hitPoint)
    {
        float snapX = 0, snapY = 0, snapZ = 0;

        int xx = (int)hitPoint.x; float x = hitPoint.x - xx;
        int zz = (int)hitPoint.z; float z = hitPoint.z - zz;

        if (x >= 0.0f && x <= 0.5f) { snapX = xx; }
        else snapX = xx + 1;

        if (z >= 0.0f && z <= 0.5f) { snapZ = zz; }
        else snapZ = zz + 1;

        snapY = FunctionalValueY(snapX, snapZ);
        snapSphere.transform.position = new Vector3(snapX, snapY, snapZ);

        return new Vector3(snapX, snapY, snapZ);
    }
Ako je možné vidieť, nefiguruje tam zaokrúhľovanie bodu. Pôvodne som to takto mal, ale zistil som, že aplikácia funguje dosť pomaly. Operácia bola časovo náročná. Napadlo ma abtrahovať bod prieniku a riešiť kvázi dichotomický problém, teda to, že dané číslo je väčšie alebo menšie ako separované číslo. Takže máte vedený bod prieniku, napr. 2.1447859 a toto číslo abstrahujeme, teda odstrihneme celú časť čísla tak, aby nám ostalo číslo 0.14478569. A toto číslo bude môcť nadobúdať rozsah od 0 až 1. Teda jednotlivá plôška, Face, rozdelí túto plôšku na štyri časti, kvázi kvadranty. A potom už len kontrolujeme, či sa dané číslo nachádza v danom rozsahu. Ak áno, tak sa priradí celá časť čísla do snapping premennej, ak nie, tak tam bude číslo o +1 väčšie, pretože to bude číslo druhej súradnice po rovnakej línii. Otázka je, že ak som riešil toto pre súradnice x a z, tak situácia bola schodná, ale akonáhle som tento princíp chcel uplatniť pre y súradnicu, tak nastal problém. Jednak v tom, že y súradnica sa navyšuje, čo by ešte bolo možné vyriešiť, ale problém bol v tom, že kurzor nezachytával exaktne vždy daný bod, preskakoval na iný vertex. Napadlo ma použiť funkčnú hodnotu Y pre coords, teda zoznam súradníc celého terénu. Zadáme hodnoty X a Z a jednoduchá funkcia nám vráti hodnotu Y. Teda, vždy bude isté, že ak zadáme dve funkčné hodnoty, tak tretia funkčná hodnota musí byť presná v každom prípade, pretože je to originálna hodnota pre coords. V nasledujúcom videu to popisujem podrobnejšie:

21 March, 2025

Jazz Standard: Over the Rainbow

Uvedený jazzový štandard som sa učil hrať na gitare v rámci výuky hry na gitare ako samouk. Chcel som zistiť a vedieť aké to je naučiť sa celý jazzový štandard. Tento jazzový štandard je z knihy Ballads for Jazz Guitar od Antonia Ongarella.



Hudobná ukážka v mojom podaní: (Harold Arlen - Over the Rainbow)

Jazz Standard: The Girl from Ipanema

Uvedený jazzový štandard som sa učil hrať na gitare v rámci výuky hry na gitare v Prahe v roku 2015. Poskytuje vhodné jazzové a bluesové akordy, ktoré sú veľmi dobre široko uplatniteľné.


Hudobná ukážka v mojom podaní: (Antonio Carlos Jobim - The Girl from Ipanema)

 

Jazz Standard: Nature Boy

Uvedený jazzový štandard som sa učil hrať na gitare v rámci výuky hry na gitare v Prahe v roku 2015. Poskytuje vhodné jazzové a bluesové akordy, ktoré sú veľmi dobre široko uplatniteľné.


Hudobná ukážka v mojom podaní: (Eden Ahbez - Nature Boy)

20 March, 2025

Differential calculus

Diferenciálny počet a jeho proces derivácie ma fascinoval už od strednej školy. Princíp som poznal už na základnej škole, avšak aj na strednej škole a neskôr mi podstata ostala viac-menej utajená. Pritom použitie bolo pomerne jednoduché. Určitú sadu vzorcov sme aplikovali na nejaké funkcie, v ktorých sme očakávali určité výstupy. Avšak čo sa dialo na pozadí tohto procesu bolo určitým spôsobom záhadné a jedinečné. NewtonIsaac Newton ako "objaviteľ" kalkulu (diferenciálneho a integrálneho počtu), ak sa to tak dá nazvať, trpel chorobným strachom z kritiky. Takýto strach často sprevádzajú úzkosti a pocity menejcennosti. Aj preto väčšinu svojich prác nepublikoval, vrátane kalkulu. Až astronóm Edmund Halley ho presvedčil, aby pripravil k vydaniu niektoré časti o zákonoch pohybu a gravitácie. Jeho dielo Matematické základy prírodovedy navždy zmenili pohľad na prírodné vedy. Gottfried Wilhelm Leibniz objavil a vydal kalkulus o niečo málo neskôr ako Newton, čo mu mnohí vytýkali, že to od neho prebral. Newton mu niektoré zápisky poslal dobrovoľne, na jeho požiadanie. Newtonové pojatie kalkulu sa odlišovalo od Leibnizovho hlavne zápisom. Newtonová verzia nebola dobre zrozumiteľná, Leibniz používal geometrický princíp, jeho metóda bola prirodzenejšia. Takže Newtonová symbolika sa používa dodnes skôr vo fyzike a Leibnizova v matematike (aj keď sa prelínajú samozrejme). Dnes považujeme obidvoch za otcov kalkulu. Ale treba podotknúť, že samotné zárodky kalkulu, jeho metódy, a vôbec myšlienka ako taká už bola známa niektorím vedcom už dávnejšie. Bolo to niečo podobné ako s Einsteinovou teóriou relativity. Tieto teórie nevznikali zo dňa na deň, boli výsledkom mnohých vedcov po mnohé stáročia. Diferenciálnym počtom už ako z názvu vyplýva je možné počítať nejaké diferencie, zmeny. Teda diferenciálnym počtom je možné analyzovať zmeny objektu, jeho pohyb. Ale medzi pohybom a zmenou musí existovať vzťah. A tento vzťah hľadáme práve diferenciálnym počtom. Čiže snažíme sa nájsť mieru zmeny nejakej meniacej sa veličiny, objektu. A práve na to slúži derivovanie. Smer pohybu daného objektu musí byť popísaný vzorcom. Derivovaním tohto vzorca sa vytvorí nový vzorec, ktorý už udáva hľadanú mieru zmeny. Typickým príkladom je klasický príklad z fyziky, kde máme auto idúce po ceste. Premenná s bude označovať dráhu auta, ktorá sa mení v závislosti na čase t podľa vzorca:
 

Podľa diferenciálneho počtu je rýchlosť auta v, čiže miera zmeny polohy, v ľubovoľnom čase t daná vzorcom:

Výraz 10t+3 vznikol derivovaním výrazu 5t^2+3t. Rýchlosť auta nie je konštantná a mení sa s časom, tak ako aj dráha. Ak aplikujeme proces derivovania ešte raz získame zrýchlenie a = 10, ktoré je konštantné. Základom všetkého je funkcia. Bez funkcie by neexistovala derivácia. Funkcia je predpis podľa ktorého je ku každému číslu z danej množiny priradené iné číslo, tzv. funkčná hodnota. Takáto funkcia je zobraziteľná v euklidovskom priestore, čo je pri derivácii výhodné z hľadiska pochopenia. Derivácie ako také je možné použiť aj v iných odvetviach vedy, často vo fyzike, geometrii a technických disciplínach. Derivácia je dobrá v tom (okrem iného), že nám dokáže povedať niečo o priebehu pôvodnej funkcie. Takže napr. máme funkciu (na obrázku nižšie znázornená červenou farbou):
 

Táto môže predstavovať čokoľvek, od rastu cien HDP až po priebeh teploty. Máme potom druhú funkciu, jej deriváciu v tvare (na obrázku znázornená modrou farbou):
 

Pôvodná (nederivovaná) funkcia f je rastúca, keï jej derivácia nadobúda kladné hodnoty a klesajúca, keď derivácia nadobúda záporné hodnoty. Bod, v ktorom má funkcia f maximum je v bode, kde je hodnota derivácie nulová. Teda z derivácie je možné vyčítať niektoré informácie o priebehu pôvodnej funkcie.

Takúto deriváciu je možné naprogramovať. Ale ešte predtým ju treba zadefinovať. Teda, nech funkcia f(x) je definovaná v okolí bodu a. Potom môžme napísať niečo také:


Túto limitu nazveme deriváciou funkcie f(x) v bode a, čo sa označuje f'(a). Príklad: Máme vypočítať deriváciu funkcie v bode a.
 

Výsledkom tejto derivácie bude hodnota, číslo, v bode a. Toto číslo dostaneme tak, že spočítame limitu pre x idúce do a. Prečo počítame toto a prečo takto sa dozvieme neskôr. Zatiaľ vypočítame príklad podľa vzťahov. Takže dostaneme niečo také:
 


Čiže, ak zderivujeme funkciu f(x) = x^3 dostaneme f'(x) = 3x^2. V našom prípade v bode a a dosadením ľubovoľného čísla v tomto bode dostaneme hodnotu zderivovanej funkcie f(x). Program vyzerá nasledovne. Bol použitý programovací jazyk C s hlavičkovými súbormi:
#include <stdio .h="">
#include<stdlib .h="">
#include <math .h="">
</math></stdlib></stdio></pre>
                    
Zadefinujeme si funkciu <code><font size="3">f</font></code> s jedným vstupným parametrom typu <code><font size="3">float</font></code>, ktorý vráti hodnotu funkcie typu <code><font size="3">float</font></code> v tomto bode:

<pre>float f(float x)
{
    return x*x*x;
}
A teraz si definujeme funkciu, ktorá bude počítať, aproximovať, funkciu v bode a:
float PrvaDerivacia(float bod)
{
    float xt = bod;
    float h = 0.01 * (1 +  abs(xt));
    bod = xt + h;
    float fp = f(bod);
    bod = xt - h;
    float fm = f(bod);
    bod = xt;
    return (fp - fm)/2/h;
}
A ešte hlavnú funkciu main, ktorá len zavolá funkciu prvaDerivacia a vypíše výsledok na obrazovku:
void main(void)
{
    printf("%f", PrvaDerivacia(2));
    system("pause");
}
Ak program preložíme a spustíme, vypíše hodnotu 12.000895, čo je približne 12. A naozaj. Funkcia x^3 v bode 2 je 8 a jej derivácia 3x^2 v bode 2 je 12. Teda naozaj nami naprogramovaná funkcia derivuje. Newton a Leibniz mali za úlohu vyriešiť ako stanoviť "rýchlosť" zmeny funkcie, teda pomer zmeny y = f(x) k zmene x. V grafickom znázornení, na obrázku nižšie, to znamená, nájsť v danom bode x sklon krivky, ktorý je daný veľkosťou uhlu medzi dotyčnicou ku krivke v danom bode (P, Q) a osou x. V číselnom vyjadrení sa veľkosť uhlu vyjadruje ako smernica dotyčnice k danej krivke alebo aj tangens uhla. Táto veľkosť smernice nie je konštantná, závisí od charakteru krivky. Smernica dotyčnice ku krivke závisí od hodnoty x a táto hodnota závisí od y. Čiže hodnoty smerníc definujú našu krivku.
 


Budeme uvažovať kvôli jednoduchosti funkciu y = x^2 znázornenú na obrázku vyššie. Na konci uvažovania získame všeobecný predpis pre funkciu y = x. Ako je možné vidieť na obrázku, s rastúcou hodnotou x rastie nie len hodnota funkcie y, ale aj smernica, teda uhol. Nebudeme počítať priamo uhol, ale len pomer sklonu medzi bodmi P a Q, teda smernicu úsečky spájajúcu bod P a Q. Výsledkom bude len pomerné číslo. Takže pri danej hodnote x je výška krivky daná výrazom x^2. Ako spočítame smernicu pre danú hodnotu x ? Čiže ako spočítame v trojuholníku na obrázku uhol medzi osou x a úsečkou PQ ? Myšlienka bola nasledujúca. Pozrieme sa na bod Q, ktorého súradnica je x + h. Výška bodu P je x^2 a výška bodu Q je (x + h)^2. Od bodu P do bodu Q sa krivka ohýba smerom hore. Nemôžeme počítať smernicu v takomto rozpoložení, pretože body P a Q sú od seba prive¾mi vzdialené. Sú od seba vzdialené natoľko, že úsečka PQ nesplýva s krivkou (je medzi nimi medzera). Musíme teda úsečku PQ zmenšiť na menšie úsečky, aby tzv. (lepšie) aproximovala danú krivku. Vtip spočíva v tom, že pri veľmi malých hodnotách hodnoty h bude platiť, že v bode P sa smernica bude blížiť smernici tejto úsečky. Čiže stačí nám spočítať smernicu v bode P, teda prírastok výšky, toho trojuholníka, vydelíme prírastkom vo vodorovnom smere. Prírastok výšky je daný výrazom:
 

Tento prírastok označíme ako dy (dy = funkčná hodnota v bode Q - funkčná hodnota v bode P). Takto ho označil aj Leibniz, ovšem Newton bol viac fyzik než matematik a označil ho ako dr, podľa vzorca r = t^2. To preto, pretože Newton používal fyzikálny model podľa ktorého označil r ako vzdialenosť polomeru a t ako čas. Leibnizov model sa používa častejšie, najmä v matematike. Newtonové označenie sa používa z veľkej časti vo fyzike. Prírastok vo vodorovnom smere je h, pretože rozdiel x + h - x = h. Tento prírastok si označíme ako dx. Takže smernica úsečky spájajúca bod P a Q je daná pomerom dy ku dx, z čoho dostaneme:
 

Čitateľ upravíme na tvar:



Po úprave dostaneme:

Tento výraz udáva smernicu úsečky PQ. Ale nespočítali sme smernicu krivky v bode P! Teraz sme v podstate len zderivovali funkciu y = x^2, čoho výsledkom je funkcia y = 2x. Ovšem nie 2x, ale 2x + h. Newton a Leibniz považovali túto hodnotu h = 0, čo nebolo celkom správne. Ak sa pozrieme na obrázok tak so zmenšujúcou sa hodnotou h sa bod Q približuje k bodu P. Pre každý prírastok h odpovedá výraz 2x + h, tzn. že pre akýkoľvek najmenší prírastok h platí smernica úsečky PQ, ktorú sme spočítali. Takže, ak zvolíme za x = 5 a budeme postupne zmenšovať krok h z 0.1 na 0.010.000001 atï, tak dostaneme postupne hodnoty, 10.1, 10.01, až 10.000001 atï. A tu je jasne vidieť, že so zmenšujúcim sa krokom h sa smernica blíži k celej hodnote čísla, v našom prípade hodnote 10. Teda počítame vlastne limitu od x idúcu k 0. V reálnej praxi by to znamenalo dosadiť h = 0. Newton a Leibniz uvažovali priamo h = 0, čo z matematického hľadiska nebolo správne, pretože ak dosadíme do poslednej rovnice h = 0 tak výraz na ľavej strane nebude definovaný, pretože podiel 0/0 v matematike nie je definovaný. Preto bolo potrebné uvažovať limitné prípady. To znamená, že z geometrického h¾adiska sa so zmenšujúcim sa h bod Q približuje bodu P a rozdiel medzi smernicou úsečky PQ a smernicou krivky v bode P postupne mizne, čo znamená, že limitná hodnota smernice úsečky PQ bude presnou hodnotou smernice krivky v bode P! Čiže smernica krivky pre hodnotu x je daná výrazom 2x, čo je limitná hodnota výrazu 2x + h pre h blížiace sa k nule. Tento nedostatok neskôr odstránil Karl Weierstrass zavedením pojmu limity. 

Použitá inšpirácia: 
• Jazyk matematiky | K. Devlin | Dokořán | Praha 2002 
• Prehľad matematiky | P. Horák, L. Niepel | SPN | Bratislava 1982 
• Introduction to Differential Calculus | Ch. Thomas | Lecture Notes | Sydney 1997

19 March, 2025

Projection of space onto a plane

V minulej časti bol popísaný postup ako vykresliť dvojrozmerné objekty na obrazovku počítača (na tzv. plátno). Takýto postup je priamočiary, to znamená, akýmkoľvek spôsobom zadáme súradnice objektu, na plátno vykresľujeme v tých istých súradniciach, prípadne obohatené o transformačné vzťahy, najčastejšie posunu objektu, otočenie alebo zmenu mierky objektu. camSituácia sa zmení pridaním ďalšieho rozmeru obrazu - rozmeru håbky. V tomto okamihu už dochádza k simulácii ľudského videnia, avšak stále len k simulácii, napodobovaniu, pretože zostrojiť úplne dokonalý obraz ľudského videnia reálneho sveta nie je doposiaľ možné. Z fyzikálneho hľadiska si môžme predstaviť okolitý svet ako spojitý, analógový a svet elektroniky ako nespojitý, digitálny. Najlepšie to reprezentuje videokamera alebo fotoaparát, ktorým snímame jednotlivé obrázky. Procesom snímania okolitého sveta prostredníctvom objektívu je vždy nejaký prevod obrazu z tohto objektívu (analógový signál) na obrazový formát (digitálny signál). Tento prevod je daný nejakou transformáciou. Doposiaľ žiadny objektív nedokáže zachytiť obraz bezchybne, takže okrem samotnej transformácie obrazu dochádza aj k transformačným, korekčným chybám objektívov. Tieto korekcie, transformácie sú vo všeobecnosti vzťahy, ktoré vyjadrujú spojitosť, väzbu, medzi jednotlivými prvkami. Premietanie v našom prípade je transformácia, ktorá charakterizuje prevod trojrozmerných súradníc objektu do dvojrozmerných súradníc, pričom v takejto reprezentácii, ako už bolo spomínané, dochádza vždy k strate priestorovej informácie. Deskriptívna geometria je jedna z matematických disciplín, ktorá sa zaoberá rôznymi prepismi geometrických scén. eyeProces ľudského videnia je založený v konečnom dôsledku na vyhodnotení signálov v mozgu. Preto, čím vernejšie vykreslíme 3D scénu na monitore počítača, tým prirodzenejšie bude schopný mozog spracovať jednotlivé signály. Pri spracovaní videnia v určitej fáze prebieha v mozgu k tzv. stranovému kríženiu obrazu (okrem toho aj k zmenšeniu a dvojitému stranovému otočeniu obrazu) z ľavého a pravého oka. Týmto procesom vnímame okolitý svet priestorovo, vzniká dojem håbky. Takýto priestor nie je možné vytvoriť v mozgu z roviny akéhoko¾vek monitora, takže aj keï prakticky neustále dochádza k stranovému kríženiu obrazu, výsledný obraz bude priestorový len do kvality poskytnutej 3D transformácie. Poznámka: V súvislosti s procesom videnia mozgom, pozná neurológia určitý stav, kedy vidíme obraz, nejaké predmety, ale nedokážeme ich identifikovať. Teda, na sietnici oka sa premieta obraz z okolitého prostredia, ale napriek tomu náš mozog nedokáže jednotlivé signály spracovať a v dôsledku toho, vlastne nevidíme nič, sme slepí. Takýto stav sprevádzajú pomerne ťažké neurologické ochorenia, ale aj keï nie presne v tomto význame ich občas môže zažiť aj zdravý človek, napr. nieko¾ko sekúnd po prebudení zo spánku, alebo tesne pri zaspávaní a v neposlednom rade aj pri požití omamných a psychotropných látok.

Premietanie

Premietanie je transformácia, ktorá charakterizuje prevod trojrozmerných súradníc objektu do dvojrozmerných súradníc. Obraz, ktorý vznikne premietaním sa nazýva priemet. Takyto priemet sa premieta na priemetni, co je možné si predstaviť ako monitor počítača, na ktorom vidíme už výsledný obraz scény. Tento obraz na priemetni vznikne premietajúcimi lúčmi, ktoré dopadnú na túto priemetòu. Premietací lúč si je možné predstaviť matematicky ako polpriamku, ktorá vychádza v určitom konkrétnom bode a ktorej smer určuje konkrétna premietacia metóda. Smer premietacich lucov moze byt vo vseobecnosti v zasade len bud rovnobezny, tzv. rovnobezne premietanie (paralelne), kde jednotlive polpriamky su navzajom rovnobezne, alebo roznobezny, tzv. stredove premietanie (perspektivne), kde vychadzaju z jedneho bodu. Cieľom je nájdenie transformačných matíc, ktoré definujú transformáciu súradníc premietaných bodov. V nasledujúcich častiach budú popísané najpoužívanejšie typy premietania, rovnobežné premietanie, ktorej zástupcom je axonometrické premietanie a stredové premietanie, alebo tiež perspektívne. Ostatné typy sú podobné týmto typom a líšia sa len zmenou niektorých parametrov.

Rovnobežné premietanie

Do rovnobežného premietania patrí axonometria a jej špeciálne prípady ako dimetria, trimetria, izometria. Špeciálny prípad rovnobežného premietania je kolmé premietanie a jej charakteristickým prípadom je Mongeové premietanie, kedy premietacie lúče sú kolmé na priemetòu, čím vytvárajú priemet objektu v troch smeroch, spredu (nárys - Front), zboku (bokorys - Side), zhora (pôdorys - Top). axnImgV súradnicovej sústave P, x_1, x_2 je axonometria reprezentovaná súradnicovou sústavou O_a, E_{a}^{x}, E_{a}^{y}, E_{a}^{z}, uhlami \xi, \eta a tzv. axonometrickým trojuholníkom, ktorý tvoria jednotkové vektory e^x, e^y, e^z. Nech bod A \in E^3 má súradnice A(x^A, y^A, z^A) a jeho pôdorys A_1(x^A, y^A, 0). V axonometrii \phi: E^3 \rightarrow \varepsilon \times \varepsilon[/latex] je obrazom bodov A, A_1 usporiadaná dvojica bodov (A_a, A_{1a}) pre ktorú platí [latex]A_a A_{1a} \parallel z_a. Vzh¾adom na axonometrickú súradnicovú sústavu O_a, E_{a}^{x}, E_{a}^{y}, E_{a}^{z} majú body A_a A_{1a} súradnice A_a(x^A e^x, y^A e^y, z^A e^z), A_{1a}(x^A e^x, y^A e^y, 0). Výpočet konkrétneho bodu A_a(x_1^A, x_2^A) vzhľadom na súradnicovú sústavu Px_1x_2 bude záležitosťou použitia goniometrických funkcií. Teda, podľa daného obrázku platí:
 


Jednotlivé uhly vyjadrujú určité typy axonometrie, napr. dimetria, izometria, vojenská axonometria a podobne. Tento zápis je možné napísať aj pomocou maticového zápisu vo forme transformačného vzťahu v homogénnych súradniciach: 





 Samotný programový kód v podstate na každý bod obrazu aplikuje uvedené transformačné vzťahy pre určitý typ axonometrie.
namespace _3D_Renderer
{
    class Projection
    {
        int[,] transformation = new int[8, 2];
        

        public void Axonometric(int x, int y, int z, int n)
        {
            transformation[n, 0] = (int)(-x * Math.Cos(-0.7) + y * Math.Cos(0.8) + 800 / 2);
            transformation[n, 1] = (int)(-x * Math.Sin(-0.3) - y * Math.Sin(-0.2) + z + 600 / 2);
        }
        

        public int[,] Transformation
        {
            get
            {
                return transformation;
            }
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            int width = 800;
            int height = 600;

            Canvas canvas = new Canvas(width, height);
            Projection projection = new Projection();

            Color background = new Color(0, 0, 0);
            Color white = new Color(255, 255, 255);

            canvas.clearColor(background);

            int[,] mVertices = { { 0, 0, 0 }, { 100, 0, 0 }, { 100, 0, 100 }, { 0, 0, 100 }, { 0, 100, 0 }, { 100, 100, 0 }, { 100, 100, 100 }, { 0, 100, 100 } };


            int[,] mIndices = { 
                                          { 0, 1, 0, 1, 1, 0, 0, 0 },
                                          { 1, 0, 1, 0, 0, 1, 0, 0 },
                                          { 0, 1, 0, 1, 0, 0, 1, 0 },
                                          { 1, 0, 1, 0, 0, 0, 0, 1 },
                                          { 1, 0, 0, 0, 0, 1, 0, 1 },
                                          { 0, 1, 0, 0, 1, 0, 1, 0 },
                                          { 0, 0, 1, 0, 0, 1, 0, 1 },
                                          { 0, 0, 0, 1, 1, 0, 0, 0 },
                                      };


            for (int n = 0; n < 8; n++)
            {
                projection.Axonometric(mVertices[n, 0], mVertices[n, 1], mVertices[n, 2], n);
            }


            for (int i = 0; i < 8; i++)
            {
                for (int j = i; j < 8; j++)
                {
                    if (mIndices[i, j] == 1)
                    {
                        canvas.drawLine(white, projection.Transformation[i, 0], projection.Transformation[i, 1], projection.Transformation[j, 0], projection.Transformation[j, 1]);
                    }
                }
            }

            canvas.render();
            System.Diagnostics.Process.Start("Frame.exe", "frame.ppm");
        }
    }
}
V našom prípade zobrazíme všeobecný typ axonometrie, kedy sú všetky tri uhly navzájom rôzne (trimeria).

 


Stredové premietanie

Stredové premietanie je charakteristické premietajúcimi lúčmi vychádzajúcich z jedného bodu. Týmto spôsobom tento model premietania odpovedá najviac ľudskému videniu, pretože zobrazuje objekty tak, že sa nám na ploche javia s určitou håbkou, perspektívou, a aj z toho dôvodu sa označuje ako perspektívne premietanie. prsImgPri hľadaní perspektívneho zobrazenia sú určujúce jednotlivé priesečníky priemetne \varepsilon so stenou \nu = xz prakticky vytvárajúci pravouhlý trojhran, a tiež voľba stredu lineárnej perspektívy S(s, -d, v). Podľa obrázku (niektoré nasledujúce symboly nie sú na obrázku označené) reprezentuje v, výšku začiatočného bodu S, úsečky SR (teda táto výška rovnobežná s osou z=x_2) a z hľadiska manipulácie perspektívneho obrazu bude možné týmto parametrom meniť výšku celkového pohľadu výsledného obrazu, teda uhol otočenia okolo osy x. Jedná sa o vzdialenosť od tzv. základnej roviny. V prípade v=0 sa budeme nachádzať priamo v horizonte, na obrázku s označením h. Vzdialenosť s je daná začiatočným bodom S, úsečky SR a osy y, resp. pomyselnej roviny yz a bodu S. Týmto parametrom bude možné meniť šírku celkového pohľadu, teda uhol natočenia okolo osy z. A napokon parameter d je tzv. dištancia a je to veľkosť úsečky SH, teda vzdialenosť bodu S od bodu horizontu H. Týmto parametrom bude možné nastaviť celkovú håbku perspektívy. Z uvedeného popisu perspektívnej scény potrebujeme vyjadriť parametrické rovnice priamky SR. Tie vyjadríme pre každú zložku nasledujúcim spôsobom:
 



Parameter d (dištancia) je v tomto prípade záporný, pretože os y nadobúda vo vyznačenom smere kladné hodnoty, a zároveň preto, že na stanovenie priesečníka R_s by mal začiatočný bod S ležať za priemetňou \varepsilon. Bod R_s je priesečník úsečky SR a priemetne \varepsilon, ktorá má rovnicu y=0. Hodnotu parametra t vypočítame nasledovne:


odtiaľ


Po dosadení a úprave získame súradnice bodu, priesečníka R_s v sústave E^3:
 



V súradnicovej sústave E^2 platia vzťahy pre x_1 a x_2. Tieto vzťahy budú obsahom nasledovného kódu, ktorý jednotlivé body obrazu z priestoru konvertuje uvedenými transformačnými vzťahmi do roviny.
namespace _3D_Renderer
{
    class Projection
    {
        int[,] transformation = new int[8, 2];


        public void Perspective(int x, int y, int z, int n)
        {
            int d = 150;
            int s = 200;
            int v = -75;

            transformation[n, 0] = (int)((d * x + s * y) / (d + y) + 800 / 2);
            transformation[n, 1] = (int)((v * y + d * z) / (d + y) + 600 / 2);
        }
        

        public int[,] Transformation
        {
            get
            {
                return transformation;
            }
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            int width = 800;
            int height = 600;

            Canvas canvas = new Canvas(width, height);
            Projection projection = new Projection();

            Color background = new Color(0, 0, 0);
            Color white = new Color(255, 255, 255);

            canvas.clearColor(background);

            int[,] mVertices = { { 0, 0, 0 }, { 100, 0, 0 }, { 100, 0, 100 }, { 0, 0, 100 }, { 0, 100, 0 }, { 100, 100, 0 }, { 100, 100, 100 }, { 0, 100, 100 } };


            int[,] mIndices = { 
                                          { 0, 1, 0, 1, 1, 0, 0, 0 },
                                          { 1, 0, 1, 0, 0, 1, 0, 0 },
                                          { 0, 1, 0, 1, 0, 0, 1, 0 },
                                          { 1, 0, 1, 0, 0, 0, 0, 1 },
                                          { 1, 0, 0, 0, 0, 1, 0, 1 },
                                          { 0, 1, 0, 0, 1, 0, 1, 0 },
                                          { 0, 0, 1, 0, 0, 1, 0, 1 },
                                          { 0, 0, 0, 1, 1, 0, 0, 0 },
                                      };


            for (int n = 0; n < 8; n++)
            {
                projection.Perspective(mVertices[n, 0], mVertices[n, 1], mVertices[n, 2], n);
            }


            for (int i = 0; i < 8; i++)
            {
                for (int j = i; j < 8; j++)
                {
                    if (mIndices[i, j] == 1)
                    {
                        canvas.drawLine(white, projection.Transformation[i, 0], projection.Transformation[i, 1], projection.Transformation[j, 0], projection.Transformation[j, 1]);
                    }
                }
            }

            canvas.render();
            System.Diagnostics.Process.Start("Frame.exe", "frame.ppm");
        }
    }
}
Teda, z hľadiska kódu sa jedná o analogický postup ako pri axonometrii. Jednotlivé vstupné parametre metódy Perspective(), predstavujú už spomínané perspektívne parametre, pričom tieto parametre sa uplatòujú po korekcii šírky a výšky obrazu, takže z toho dôvodu sa obraz nachádza v štvrtom kvadrante celkového obrazu. Ak chceme zobraziť pohľad smerujúci zhora na daný objekt, tak parameter v by mal mať zápornú hodnotu, pretože začiatok súradnicovej sústavy (0,0) je v ľavom hornom rohu obrazu a keï chceme posunúť bod S smerom kolmo nahor, tak potrebujeme odpočítať určitú hodnotu od obrazovej osy y. Výsledný obrázok perspektívneho zobrazenia kocky pomocou jedného úbežníka je na nasledujúcom obrázku:

 


Renderovanie

glassesUvedené spôsoby poukázali na niektoré najčastejšie typy premietaní. Vykresľovanie doposiaľ prebiehalo spôsobom známym z OpenGL/DirectX, kedy len pomocou bodu a úsečky vykresľujeme komplexné objekty. Grafické priemyselné štandardy pracujú v režime vykresľovania scény v reálnom čase, to znamená, že akékoľvek grafické objekty sú na priemetni prekresľované v pravidelných časových intervaloch a navyše takýto spôsob vykresľovania úzko súvisí s hardwarovými vymoženosťami grafickej karty. V celom seriály ako aj u uvedenom prípade bol použitý spôsob vykresľovania objektov z grafických priemyselných štandardov, avšak vykresľovanie scény bolo realizované jednorazovym prepisom prostredníctvom zaznamenanych bodov z platna do obrazového suboru typu PPM. Prakticky to znamenalo to, že sme pouzili vektorovy sposob zapisu objektov, a tento zapis sme priamo aplikovali na priemetnu v povodnom stave, ktory sme uz len jednoducho ulozili do uz spominaneho obrazového suboru. Každe vykreslovanie grafických objektov urcitym systemom sa nazyva renderovanie, avšak v povodnom vyzname tohto oznacenia sa jedna o proces, ktory vedie k fotorealistickému zobrazovaniu geometrických objektov. V nasledujucich castiach serialu pojde o tzv. renderovanie v klasickom vyzname, to znamena, proces tvorby obrazu z 2D/3D modelov pouzitim textur, osvetlovacich modelov a algoritmami tienovania. Jedna sa o systém znamy z integrovanych renderovacich nastrojov od spolocnosti Autodesk, ktora vydava svoje jednotlive produkty ako napr. 3D Studio MAX, Maya, alebo niektore verzie AutoCADu.

18 March, 2025

Representation of graphic objects

Možností ako reprezentovať geometrické objekty je niekoľko. To, ktorý spôsob použijeme závisí na tom, čo chceme zobrazovať a ako chceme s danými objektmi manipulovať. Niekedy nám stačí zobraziť len pár hlavných bodov objektu, inokedy potrebujeme vedieť detailne zobraziť celú polygónovú sieť objektu s riešením viditeľnosti hrán, textúrou povrchu ako aj osvetlením. obrazok terenuV zásade je jedno, či sa bude jednať o reprezentáciu 2D alebo 3D objektov. Odlišnost v zápise je len v počte súradníc, aj keï reprezentácia v 3D je z hľadiska ïalšieho spracovania zložitejšia. Samotná reprezentácia v podstate vychádza z možností zobrazovania grafickej karty počítača. Pomocou grafickej karty sme schopný vykresľovať len body a úsečky, pričom grafickým procesorom je podporované vykresľovanie trojuholníkov. Práve tento trojuholník sa javí z hľadiska reprezentácie ako efektívny, pretože existujú algoritmy, ktoré popisujú jeho vyplňovanie, je možné tiež veľmi efektívne optimalizovať výpočty priesečníkov lúčov s trojuholníkmi. Túto skutočnosť používajú rôzne vykresľovacie (renderovacie) techniky. Takúto sieť trojuholníkov je možné vidieť napr. na jednoduchom teréne. Intuitívne by nás napadlo vyskladať takúto sieť tak, že zadáme súradnice každého bodu a nejakým spôsobom pospájame jednotlivé body hranami. Ako bolo spomenuté, ideálne je pospájať tieto body ako vrcholy trojuholníka, kde každý trojuholník bude obsahovať teda tri vrcholy (vertexy) a tri hrany (edges).

Hranová reprezentácia

Jeden zo spôsobov akým je možné reprezentovať objekty je tzv. hranová reprezentácia, alebo tiež drátová (wire-frame). Tento typ budeme používať v našom programe, pretože je jednoduchý a názorný. Ide o spôsob, kde zapisujeme do zoznamu súradnice vrcholov a jednotlivé hrany, osobitne. Takto získame zoznam, ktorý bude popisovať geometriu objektu v podobe vrcholov. Lenže, ak si predstavíme 4 vrcholy, tak spôsobov akým môžeme pospájať jednotlivé vrcholy je niekoľko, napr. spojenie môže predstavovať štvorec (obdlžník), ale tiež aj tvar písmena X alebo spojenie, kde z jedného vrcholu pôjde hrana do všetkých ostatných. Práve na toto odlíšenie potrebujeme ešte ïalší zoznam, ktorý bude popisovať topológiu objektu v podobe hrán, teda ktoré vrcholy budú tvoriť jednotlivé hrany. Tieto zoznamy sú z matematického hľadiska v podstate množiny obsahujúce informácie o danom objekte. Spolu tieto množiny môžu tvoriť graf, ako matematickú štrukúru, ktorá pozostáva z týchto dvoch množín. Graf je usporiadaná dvojica (V, H), kde V je množina vrcholov a H je množnina hrán, ktorá je navyše ako dvojprvková podmnožina tejto množiny. Dvojprvková preto, pretože obsahuje dvojice vrcholov medzi ktorými sa nachádza hrana. Napísať a nakresliť takýto graf je možné vo forme diagramu. Máme napr. jeden diagram grafu G_1 = (V_1, H_1), kde V_1 = \left \{ 1,2,3,4,5 \right \} a H_1 = \left \{ \left \{ 1,2 \right \}\left \{ 2,3 \right \}\left \{ 3,4 \right \}\left \{ 4,5 \right \}\left \{ 5,1 \right \} \right \} a diagram grafu G_2 = (V_2, H_2), kde V_2 = \left \{ a,b,c,d,e \right \} a [latex]H_2 = \left \{ \left \{ a,b \right \}\left \{ b,c \right \}\left \{ c,d \right \}\left \{ d,e \right \}\left \{ e,a \right \} \right \}.

Na obrázku s diagramami jasne vidieť rozdiel v spôsobe reprezentácie vrcholov a hrán. Takto je možné zapisovať rôzne diagramy a navyše nad takto zapísanými diagramami počítať ďalšie charakteristiky. Navyše jednotlivé hrany znázornené na diagrame môžu byť neorientované, teda bez označenia smeru od jednotlivých vrcholov, alebo orientované, s označením smeru, čím dané možnosti výpočtu rozširuje. Ono, teória okolo grafov je zložitá približne do miery kombinatoriky ako takej, ale na popis objektov stačí základná definícia. Diagramy sú názorné, celkom dobre je z nich čítať a vyhodnocovať rôzne charakteristiky, avšak na to, aby sme tieto charakteristiky podrobili počítačovému spracovaniu je potrebné zaviesť maticový zápis, pretože programovacie jazyky obsahujú dátovú štruktúru po¾a, ktorá umožňuje takýto maticový zápis realizovať. V zásade potrebujeme vyjadriť diagramový popis na číslicový, kde v podstate diagram zapíšeme vo forme príznakov v podobe číslic. Keďže diagram popisujú dve množiny, tak budeme potrebovať dve matice. V hranicovej reprezentácii, ako bolo spomenuté, budeme mať jeden zoznam pre maticu súradníc vrcholov a druhý zoznam pre identifikáciu hrán medzi jednotlivými vrcholmi. Z hľadiska grafov budeme diagramy v maticovom vyjadrení reprezentovať pomocou matice incidencie a matice susednosti. Obidve tieto matice popisujú spôsob reprezentácie hrán medzi vrcholmi. Teda súradnice vrcholov tieto dve matice neobsahujú. V podstate je jedno, ktorou maticou budeme popisovať jednotlivé hrany medzi vrcholmi. V obidvoch prípadoch dostaneme tú istú reprezentáciu hrán. Matica incidencie grafu G bude predstavovať maticu A s prvkami a_{i,j} typu (n, m), ak pre jej prvky bude platiť:


V tejto matici bude počet riadkov predstavovať počet vrcholov a počet stĺpcov bude predstavovať počet hrán. Incidencia v tomto zmysle znamená, príznak, kde vrchol incidujúci s hranou znamená, že vrchol a hrana sú vo vzájomnej zhode, spolu tvoria súčasť, alebo tiež opačne, kde hrana inciduje s vrcholom, teda hrana tvorí súčasť s vrcholom. Potom môže napr. hrana incidovať s dvoma vrcholmi, ktoré budú navzájom susedné. Takže majme nasledujúci diagram:
 

Je vidieť, že vrchol v_1 inciduje s hranou h_1, zároveò s hranou h_3 a h_5. Na týchto miestach budú jednotky, k ostatným hranám sa z vrcholu v_1 dostať nevieme, teda neincidujú s nimi, preto budú obsahovať nuly. A postupne vrchol v_2 inciduje z hranou h_1, h_2 a h_4. Analogicky ostatné vrcholy. Takto získame maticu incidencie:
 


V hranovej reprezentácii potrebujeme vedieť, kde je hrana a to z tejto matice získame tak, že si všímame jednotky v jednotlivých stĺpcoch matice. Takto jednoducho vytvoríme program, ktorý bude vedieť podľa tejto matice bezpečne určiť, kde je hrana. Ono, výhoda oproti matici susednosti je taká, že v tejto matici môžme priamo určiť o ktorú hranu v poradí sa jedná, čiže môžme priamo identifikovať hranu v poradí. Matica susednosti grafu G bude predstavovať maticu B s prvkami _{i,j} typu (n, n), ak pre jej prvky bude platiť:
 


V tomto prípade, ak medzi dvoma rôznymi vrcholmi bude hrana, tak v matici bude reprezentovať jednotku, v opačnom prípade nulu. Ak si vezmeme predchádzajúci diagram tak matica susednosti bude vyzerať nasledovne:
 

Pôjde o symetrickú maticu, pretože nezáleží na poradí vrcholov tvoriacich hranu. Ak by išlo o orientované grafy, tak by to bolo v niektorých prípadoch jednosmerné, v tomto prípade je to všade obojsmerné. Teraz potrebujeme v našom programe nakresliť úsečku. Náš program dokáže kresliť len bod a úsečku. Grafické priemyselné štandardy ako OpenGL a DirectX na vykreslenie primitív (útvarov) nepoužívajú viac elementov ako bod a úsečku. Funguje to tak, že sa definuje počet vertexov (bodov) a spôsob akým sa majú dané body navzájom pospájať. Ako bolo uvedené vyššie nie je jedno aký spôsob sa použije. V našom prípade to bude podobné. Na vykreslenie úsečky môžme použiť priamo metódu drawLine(), ktorá vykreslí úsečku so zadanými súradnicami.
namespace _2D_Renderer
{
    class Program
    {
        static void Main(string[] args)
        {
            int width = 800;
            int height = 600;

            Canvas canvas = new Canvas(width, height);
            
            Color background = new Color(0, 0, 0);
            Color white = new Color(255, 255, 255);

            canvas.clearColor(background);

            canvas.drawLine(white, 300, 200, 400, 300);

            canvas.render();
            System.Diagnostics.Process.Start("Frame.exe", "frame.ppm");
        }
    }
}
Vizuálne to bude vyzerať nasledovne:
 


Čo sa týka zdrojového kódu úsečky, metódy drawLine(), bol použitý Bresenhamov algoritmus úsečky, ktorých je na internete kvantum. Pre konkrétnosť uvedieme zdrojový kód daného algoritmu:
public void drawLine(Color color, int x1, int y1, int x2, int y2)
{
    if ((x1 - x2) &gt; 0) 
    {
        drawLine(color, x2, y2, x1, y1);
        return;
    }

    if (Math.Abs(y2 - y1) &gt; Math.Abs(x2 - x1)) 
    {
        bresteepline(color, y1, x1, y2, x2); 
        return; 
    }

    int x = x1, y = y1, sum = x2 - x1,  Dx = 2 * (x2 - x1), Dy = Math.Abs(2 * (y2 - y1));
    int deltaDy = ((y2 - y1) &gt; 0) ? 1 : -1;

    for (int i = 0; i &lt;= x2-x1; i++) 
    {
        setPixel(x, y, color);
        x++;
        sum -= Dy;

        if (sum &lt; 0) 
        {
            y = y + deltaDy; sum += Dx;
        }
    }
}


private void bresteepline(Color color, int x3, int y3, int x4, int y4)
{
    if ((x3 - x4) &gt; 0) 
    {
        bresteepline(color, x4, y4, x3, y3);
        return;
    }

    int x = x3, y = y3, sum = x4 - x3,   Dx = 2 * (x4 - x3), Dy = Math.Abs(2 * (y4 - y3));
    int deltaDy = ((y4 - y3) &gt; 0) ? 1 : -1;

    for (int i = 0; i &lt;= x4-x3; i++)
    {
        setPixel(y, x, color);
        x++;
        sum -= Dy;

        if (sum &lt; 0) 
        {
            y = y + deltaDy; sum += Dx;
        }
    }
}
Ak by sme chceli vykresliť napr. štvorec, mohli by sme 4x zavolať metódu drawLine() s relevantnými súradnicami vrcholov. Ale ak by sme chceli ponechať súradnice vrcholov a nakresliť jednotlivé úsečky z iných vrcholov museli by sme pomerne pracne prepisovať jednotlivé súradnice vrcholov. Práve pre tento účel bola definovaná matica susednosti a incidencie. Nasledujúci zdrojový kód popisuje ich použitie.
namespace _2D_Renderer
{
    class Program
    {
        static void Main(string[] args)
        {
            int width = 800;
            int height = 600;

            Canvas canvas = new Canvas(width, height);

            Color background = new Color(0, 0, 0);
            Color white = new Color(255, 255, 255);

            canvas.clearColor(background);


            int[,] mVertices = { { 300, 200 }, { 400, 300 } };
            int[,] mIndices = { 
                                 { 0, 1 }, 
                                 { 1, 0 } 
                              };

            int x1, y1, x2, y2;

            for (int i = 0; i &lt; 2; i++)
            {
                for (int j = i; j &lt; 2; j++)
                {
                    if (mIndices[i, j] == 1)
                    {
                        x1 = mVertices[i, 0];
                        y1 = mVertices[i, 1];
                        x2 = mVertices[j, 0];
                        y2 = mVertices[j, 1];
                        canvas.drawLine(white, x1, y1, x2, y2);
                    }
                }
            }

            canvas.render();
            System.Diagnostics.Process.Start("Frame.exe", "frame.ppm");
        }
    }
}
Vykreslenie tejto úsečky má rovnaké súradnice ako v predošlom prípade len s tým rozdielom, že sme použili maticu susednosti. Pôvodne sme definovali úsečku so súradnicami, teraz definujeme súradnice v matici vrcholov (mVertices) a kreslíme danú úsečku podľa akejsi šablóny, matice susednosti (mIndices), ktorá určuje, ktoré hrany majú byť vykreslené od jednotlivých vrcholov. V tomto prípade podľa matice susednosti by mal program kresliť úsečku 2x, pretože matica je symetrická, teda od jedného vrcholu k druhému a naopak. Aby zápis korešpondoval s definíciou matice susednosti budeme zapisovať celú maticu susednosti, avšak program reálne kreslí len prvky nad diagonálou tejto matice. Samozrejme, mohli by sme použiť analogicky aj maticu incidencie. 

Poznámka: Maticu susednosti v programe označujeme ako mIndices, teda ako maticu indexov. Použil som toto pomenovanie známe z knižnice DirectX, kde sa takto označuje matica, ktorá nejakým spôsobom definuje vzťahy medzi vertexami. Použitie pomenovania napr. mAdjacent alebo mAdj by mohlo byť zavádzajúce vzhľadom k tomu, že existuje tzv.adjungovaná matica, čo je transponovaná matica, kde sú navzájom vymenené riadky za stĺpce. 

Ono, napr. v OpenGL, ale aj DirectX sa súradnice vrcholov zapisujú do osobitnej metódy, kde sa definuje len jeden vertex (bod), pričom je tiež možné definovať pole vertexov, podobne ako v našom prípade. Priemyselné štandardy už niečo také ako vykresľovanie podľa matice susednosti v takom rozpoložení ako ukazuje tento kód primárne nepoznajú, ale určite by to bolo možné aplikovať na nejaké čiastkové vykresľovanie. Principiálne to tam existuje vo forme dodefinovania vertexu, alebo pri samotnom vykreslení sa určí o aký typ vykreslenia ide, napr. o zoznam (zreťazených) úsečiek, zoznam (zreťazených) trojuholníkov, alebo veárovitý spôsob, kde jeden vrchol prináleží viacerým vrcholom spojených hranou. Teraz podobným spôsobom môžme vykresliť štvorec, ale aj akékoľvek iné útvary. Ako sme už spomínali, stačí definovať vrcholy a spôsob, akým jednotlivé vrcholy budú pospájané je už na matici susednosti. Nasledujúci zdrojový kód teda ilustruje vykreslenie štvorca:
namespace _2D_Renderer
{
    class Program
    {
        static void Main(string[] args)
        {
            int width = 800;
            int height = 600;

            Canvas canvas = new Canvas(width, height);

            Color background = new Color(0, 0, 0);
            Color white = new Color(255, 255, 255);

            canvas.clearColor(background);


            int[,] mVertices = { { 300, 200 }, { 400, 200 }, { 300, 300 }, { 400, 300 } };
            int[,] mIndices = { 
                                   { 0, 1, 1, 0 },
                                   { 1, 0, 0, 1 },
                                   { 1, 0, 0, 1 },
                                   { 0, 1, 1, 0 }
                              };

            int x1, y1, x2, y2;

            for (int i = 0; i &lt; 4; i++)
            {
                for (int j = i; j &lt; 4; j++)
                {
                    if (mIndices[i, j] == 1)
                    {
                        x1 = mVertices[i, 0];
                        y1 = mVertices[i, 1];
                        x2 = mVertices[j, 0];
                        y2 = mVertices[j, 1];
                        canvas.drawLine(white, x1, y1, x2, y2);
                    }
                }
            }

            canvas.render();
            System.Diagnostics.Process.Start("Frame.exe", "frame.ppm");
        }
    }
}
Vizuálne to bude vyzerať nasledovne:
 


Ak napr. chceme spojiť diagonálu, môžme tak urobiť spojením vrcholu v_2 z v_3, čo v matici susednosti reprezentuje jednotka na pozícii [1, 2] a [2, 1].



Podobne môžme kresliť rôzne útvary pozostávajúce z vrcholov a úsečiek. Pri kreslení kriviek sa používajú interpolačné a aproximačné vzťahy. Krivky a plochy budú obsahom ďalších článkov. Priestorové objekty zatiaľ kresliť nemôžme. Mohli by sme imitovať hĺbku obrazu tak, že by sme vytvorili pomocou dvojrozmerných súradníc tretiu súradnicu hĺbky, ale nešlo by skutočne o rozšírenie rozmeru, pretože z hľadiska programu by sme mali k dispozícii len rovinné súradnice. Vždy sa jedná o rovinu, plochu ako obraz (2D), teda reálne nie je možné nakresliť akýkoľvek priestorový objekt tak, aby sme skutočne vnímali hĺbku. Stále pôjde len o napodobenie skutočného obrazu sveta. Teda súradnica hĺbky obrazu bude vždy zobraziteľná (premietaná) na rovnakej ploche obrazu ako ostatné súradnice, avšak jej súradnice vzhľadom na plochu sa budú prepočítavať rôznymi vzťahmi v závislosti na požadovanom zobrazení, napr. axonometrické zobrazenie, perspektívne a iné. Navyše sa môžu už prepočítané súradnice znovu prepočítavať vzhľadom na pozíciu a pohľade pozorovateľa, pri kamere alebo fotoaparáte v zmysle pozície a natočenia objektívu. Teda, nato, aby sme mohli kresliť, premietať, priestorové objekty na rovinu potrebujeme najskôr rôzne transformačné vzťahy pre vytvorenie priestoru. K tomuto účelu bude slúžiť nasledujúci článok.

Hraničná reprezentácia

brepNiekedy nie je potrebné zaznamenávať informácie o všetkých hranách objektu, ale len o tých, ktoré sú z hľadiska viditeľnosti prístupné nášmu pohľadu. Ostatné hrany sa ignorujú. To je prípad hraničnej reprezentácie (boundary representation, B-rep). Mohlo by sa zdať, že tento typ rieši určitú problematiku viditeľnosti hrán, avšak je to len zdanie, pretože zadávame len tie hrany, ktoré z hľadiska pohľadu majú byť viditeľné. Teda, popisujeme len plášť objektu (aj keď samotný plášť netvorí plocha, ktorá je nejakým spôsobom definovaná), jeho obrysy. Pri jednoduchých objektoch je táto metóda ešte zvládnuteľná z hľadiska manuálnej selekcie hrán, ale pri zložitejších objektoch to býva časovo náročné.

Jednoduchá a štruktúrovaná plôšková reprezentácia

Plôšková reprezentácia obsahuje informácie o plochách objektu, z ktorého pozostáva. Je to už reálna plocha narozdiel od predchádzajúcej reprezentácie, takže štruktúra obsahuje okrem vrcholov a hrán aj informáciu o plochách. To znamená napr. to, že je možné daný objekt osvetľovať, keďže už obsahuje plochy, a tiež je možné realizovať viditeľnosť jednotlivých častí objektu, aj keď v princípe je možné riešiť viditeľnosť pri akomkoľvek type reprezentácie, ale nie vždy to bude efektívne. V každom prípade budeme môcť zobrazovať objekty na pomerne slušnej optickej úrovni. Lenže z hľadiska vnútornej reprezentácie plôch objektu nám môžu chýbať údaje o type hrán alebo susednosti jednotlivých plôch s inými plochami. Práve tento nedostatok rieši štruktúrovaná reprezentácia, ktorá je známa pod názvom okrídlená hrana (winged-edge), okrídlená preto, pretože jedna hrana spolu so susednými hranami pripomína krídla v tom zmysle, že ku každej jednej hrane ústia v jej vrcholoch práve dve ďalšie hrany, ktorá takto spoločne na čele s danou hranou vizuálne tvoria krídla.

Bodová reprezentácia

Objekt môžme popísať aj hranicami bodov, z ktorých pozostáva jeho povrch. Nepotrebujeme vedieť informácie o hranách, ktoré by tak spolu tvorili daný objekt, stačí nám vedieť informácie každého jedného bodu, z ktorého pozostáva objekt. Je to jednoduchšie v tom, že získať informáciu o bode a z neho vytvoriť objekt je menej náročné než získať informácie o hranách, ktoré sa spolupodieľajú na objekte. Jednotlivé body objektu nesú informácie o svojej polohe, normálovom vektore, farbe a ostatných vlastnostiach materiálu alebo svetla. Obyčajne sa tieto body získavajú cestou digitálneho snímania reálnych objektov. Je zrejmé, že táto metóda bude najpresnejšia, ale zároveň najnáročnejšia na pamäťové nároky. V tomto zmysle je to podobné ako pri metóde sledovania lúča, kedy v podstate počítame priesečníky objektov a zobrazujeme jednotlivé body povrchu objektov. Avšak pri týchto metódach existujú rôzne metódy optimalizácie zobrazenia. 

Pre ostatné typy reprezentácie objektov ako napr. konštruktívna geometria telies, modelovanie pomocou deformácii, objemová reprezentácia, fraktálová geometria, modelovanie krajín alebo časticové systémy budú čiastočne popísané na inom mieste.