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) > 0) 
    {
        drawLine(color, x2, y2, x1, y1);
        return;
    }

    if (Math.Abs(y2 - y1) > 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) > 0) ? 1 : -1;

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

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


private void bresteepline(Color color, int x3, int y3, int x4, int y4)
{
    if ((x3 - x4) > 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) > 0) ? 1 : -1;

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

        if (sum < 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 < 2; i++)
            {
                for (int j = i; j < 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 < 4; i++)
            {
                for (int j = i; j < 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.
Share:

0 comments:

Post a Comment