OpenGL a nadstavbová knihovna GLU (16)

16. 11. 2004
Doba čtení: 7 minut

Sdílet

V dnešním pokračování seriálu budeme hovořit o takzvaném ořezávání NURB ploch (trimování). S využitím ořezávání, jež se provádí v parametrické rovině pomocí ořezových křivek, lze za použití NURB ploch vytvářet i velmi složité modely těles s otvory v povrchu či s konstrukčně složitými spoji. Ořezávání tak představuje jednu z technik pro pokročilejší práci s NURB plochami.

Obsah

1. Ořezání NURB plochy – trimming
2. Postup při ořezávání
3. Orientace ořezové křivky
4. Ořezání NURB plochy polyčarou
5. Ořezání NURB plochy NURB křivkou
6. Demonstrační příklady
7. Obsah dalšího pokračování
8. Seznam funkcí OpenGL, GLU a GLUT zmíněných v této části
9. Zkomprimovaná verze článku i s přílohami
 

1. Ořezání NURB plochy – trimming

Před popisem nových funkcí určených pro ořezávání si nejprve popišme, jakým způsobem ořezávání pracuje. Princip ořezávání spočívá ve specifikaci oblasti, která se bude nacházet na NURB ploše, ostatní oblasti se na ploše nacházet nebudou. Vtip spočívá v tom, že oblast náležející do NURB plochy se zadává v parametrické rovině, nikoli v modelovém prostoru, takže změna této oblasti je velmi jednoduchá.

Každá NURB plocha (resp. její uzlové vektory) je před vykreslením upravena tak, aby její parametry u a v postupně prošly plochou, která je omezena čtvercem s vrcholy (0,0), (1,0), (1,1) a (0,1). Pokud není ořezávání nastaveno, vykreslí se plocha celá, tj. berou se v úvahu parametry u a v z celého rozsahu 0–1.

Pokud je však ořezávání nastaveno, tj. je specifikována alespoň jedna oblast pro ořezávání, musí se původní čtverec rozdělit na části, které do NURB plochy patří, a na ty, které ne. Části, na které se čtverec rozdělí, se nesmí vzájemně překrývat, mohou však být v sobě vnořeny, a vytvářet tak v parametrické ploše jakési „ostrůvky“. Vnořené části musí být specifikovány pomocí křivek, které jsou navzájem orientovány v opačném směru, podrobnosti si popíšeme v dalších odstavcích.

Příklad vytvořených orientovaných oblastí v parametrické rovině je ukázán na prvním ilustračním obrázku, přičemž barevně vyznačené oblasti do NURB plochy nepatří:

Obrázek 1: Specifikace oblastí pro ořezání NURB plochy
Obrázek 1: Specifikace oblastí pro ořezání NURB plochy

2. Postup při ořezávání

Postup, který je nutné dodržet v programu provádějícím ořezání NURB plochy, je velmi jednoduchý a ukazuje na velkou vyjadřovací sílu pouze několika málo funkcí, které se při práci s NURB plochami používají. Vykreslování se začne provádět prakticky stejně jako při práci s běžnou NURB plochou, tj. zavolají se již dříve popsané funkce gluBeginSurface() a gluNurbsSurfa­ce(). Posléze se však musí zadat nový příkaz, který specifikuje začátek ořezové křivky. Tento příkaz se jmenuje gluBeginTrim() a má následující hlavičku:

void gluBeginTrim(
    GLUnurbsObj *nurbsObj
);

Jediným parametrem funkce gluBeginTrim() je ukazatel na objekt NURB plochy, která byla dříve vytvořena pomocí funkce gluBeginSurfa­ce(). Po zavolání funkce gluBeginTrim() je možné specifikovat oblast pro ořezání, která může být určena buď uzavřenou lomenou čarou, uzavřenou NURB křivkou, nebo kombinací lomené čáry a NURB křivky. Bližší popis možností při specifikaci oblasti pro ořezání bude uveden v kapitolách 4 a 5. Po zadání jedné nebo několika oblastí pro ořezání se musí zavolat funkce gluEndTrim(), která má hlavičku:

void gluEndTrim(
    GLUnurbsObj *nurbsObj
);

Parametry této funkce jsou stejné jako u výše popsané funkce gluBeginTrim(). Celý proces při specifikaci trimované NURB plochy je možné naznačit následující sekvencí příkazů:

gluBeginSurface(nurbs);
    gluNurbsSurface(nurbs, ...);
    gluBeginTrim(nobj);
        gluPwlCurve(. . ., GLU_MAP1_TRIM_2);
        gluNurbsCurve(. . ., GLU_MAP1_TRIM_2);
        gluNurbsCurve(. . ., GLU_MAP1_TRIM_3);
    gluEndTrim(nobj);
gluEndSurface(nurbs);

Na výše uvedené sekvenci příkazů je ukázáno vytvoření komplikované ořezové křivky, která se skládá ze tří částí. První část je tvořena polyčarou (lomenou čarou) zadanou funkcí gluPwlCurve() a další dvě části jsou tvořeny NURB křivkou zadanou dvojicí funkcí gluNurbsCurve(). Ořezová křivka může být jakkoli složitá, tj. může obsahovat téměř libovolný počet volání výše zmíněných dvou funkcí.

3. Orientace ořezové křivky

Při specifikaci oblastí, které mají být z NURB plochy odstraněny, se používají uzavřené orientované křivky. Podle orientace těchto křivek je určeno, které části NURB plochy mají být ponechány a které odstraněny. Orientace ořezových křivek je dána posloupností svých vrcholů tak, jak to bylo naznačeno šipkami na prvním obrázku.

Zjednodušeně lze říci, že oblasti, které se nacházejí nalevo od křivky, se stanou součástí NURB plochy, samozřejmě za předpokladu, že křivky nejsou vloženy do sebe – potom nastává střídání oblastí, které do NURB plochy patří a které ne. Křivka tedy vždy představuje hranici mezi vnitřní a vnější oblastí. Příklad různě orientovaných křivek je opět patrný z prvního ilustračního obrázku.

V některých případech (například tehdy, kdy je zapotřebí v NURB ploše vytvářet otvory beze změny její hranice) je zapotřebí, aby byla vytvořena i křivka (v tomto případě spíš lomená čára) okolo celého jednotkového čtverce. Tato křivka, jež na na prvním obrázku označena symbolem A, musí vždy směřovat proti směru hodinových ručiček, aby se oblast nalevo od křivky nalézala uvnitř jednotkového čtverce. Ukázka bude patrná z demonstračních příkladů.

4. Ořezání NURB plochy polyčarou

Nejjednodušší možností vedoucí k ořezání NURB plochy je specifikace polyčáry, tj. lomené čáry v parametrické ploše. Polyčára je zadána množinou bodů, které jsou uspořádány buď ve směru, nebo proti směru pohybu hodinových ručiček. Polyčára se nesmí sama se sebou protínat a nesmí se také protínat s ostatními ořezovými křivkami ani s jednotkovým čtvercem. Jestliže má být křivka specifikována pouze polyčarou, musí být první a poslední bod shodný. Polyčára je specifikována pomocí funkce gluPwlCurve(), jež má následující hlavičku:

void gluPwlCurve(
    GLUnurbsObj *nobj,
    GLint       count,
    GLfloat     *array,
    GLint       stride,
    GLenum      type
);

V prvním parametru funkce gluPwlCurve() je předán ukazatel na objekt reprezentující NURB plochu. Pomocí druhého parametru se specifikuje počet vrcholů polyčáry. Ve třetím parametru se předává ukazatel na pole, jež obsahuje souřadnice jednotlivých vrcholů. Čtvrtý parametr má stejný význam jako stejně pojmenovaný parametr použitý při vytváření NURB křivky – zadává se zde počet hodnot typu GLfloat mezi začátky jednotlivých vrcholů polyčáry. V posledním parametru se ve většině případů zadává konstantaGLU_MAP1_TRIM­2, která určuje typ specifikované polyčáry.

V dalším odstavci je ukázán fragment kódu, ve kterém je pro ořezávání NURB plochy použita uzavřená polyčára.

// začátek specifikace NURB plochy
gluBeginSurface(nurbs);

// nastavení parametrů NURB plochy
gluNurbsSurface(nurbs,
    U_KNOT, knots1,
    V_KNOT, knots2,
    V_POINTS * 3,
    3,
    &ctlpoints[0][0][0],
    U_ORDER, V_ORDER,
    GL_MAP2_VERTEX_3);

    // začátek ořezávání NURB plochy
    // v parametrickém prostoru
    gluBeginTrim(nurbs);
        // polyčára
        gluPwlCurve(nurbs, 5, &edgePt[0][0],
            2, GLU_MAP1_TRIM_2);
    // konec ořezávání NURB plochy
    gluEndTrim(nurbs);

// konec specifikace NURB plochy
gluEndSurface(nurbs);

5. Ořezání NURB plochy NURB křivkou

Druhým typem ořezové křivky je NURB křivka, která musí být taktéž specifikována v parametrické rovině. Pro vytvoření ořezové NURB křivky je použita funkce gluNurbsCurve() s následující hlavičkou:

void gluNurbsCurve(
    GLUnurbsObj *nobj,
    GLint nknots,
    GLfloat *knot,
    GLint stride,
    GLfloat *ctlarray,
    GLint order,
    GLenum type
);

Tato funkce byla podrobně popsána v XI. a XII. části tohoto seriálu. Při práci s ořezovými křivkami se však mění hodnota posledního parametru type, která by nyní měla obsahovat hodnotu symbolické konstantyGLU_MAP1_TRIM2 nebo GLU_MAP1_TRIM3. V prvním případě se jedná o křivku vytvořenou v parametrické rovině, ve druhém případě se k souřadnicím každého řídícího bodu křivky přidává jeho váha w, kterou je každá souřadnice vydělena.

V dalším odstavci je ukázán fragment kódu, ve kterém je pro ořezávání NURB plochy použita uzavřená NURB křivka spolu s ohraničujícím čtvercem vytvořeným z uzavřené polyčáry, jež je orientována v opačném směru než NURB křivka. Pozor! při uzavírání NURB křivky je zapotřebí dbát na korektní hodnoty zapsané v uzlovém vektoru, protože NURB křivka obecně neprochází svými krajními řídícími body.

// začátek specifikace NURB plochy
gluBeginSurface(nurbs);

// nastavení parametrů NURB plochy
gluNurbsSurface(nurbs,
    U_KNOT, knots1,
    V_KNOT, knots2,
    V_POINTS * 3,
    3,
    &ctlpoints[0][0][0],
    U_ORDER, V_ORDER,
    GL_MAP2_VERTEX_3);

    // začátek ořezávání NURB plochy
    // v parametrickém prostoru
    gluBeginTrim(nurbs);
        gluPwlCurve(nurbs,
        5, &edgePoints[0][0],
        2, GLU_MAP1_TRIM_2);
    gluEndTrim(nurbs);
    // vnitřní oblast pro ořezání
    // zadaná NURB křivkou
    gluBeginTrim(nurbs);
        gluNurbsCurve(nurbs, 8,
            curveKnots, 2,
            &curvePoints[0][0],
            4, GLU_MAP1_TRIM_2);
    // konec ořezávání NURB plochy
    gluEndTrim(nurbs);

// konec specifikace NURB plochy
gluEndSurface(nurbs);

6. Demonstrační příklady

Po spuštění prvního demonstračního příkladu se zobrazí osvětlená NURB plocha, která je specifikovaná pomocí 36 řídících bodů. Díky opakujícím se hodnotám v uzlovém vektoru prochází plocha svými krajními řídícími body. Kromě toho je provedeno ořezání NURB plochy tak, že v parametrickém prostoru je vytvořen čtverec o mezních souřadnicích [0.2, 0.2]-[0.8, 0.2]-[0.8, 0.8]-[0.2, 0.8]. Vrcholy čtverce musí být uvedeny v pořadí ve směru hodinových ručiček.

Zdrojový kód prvního demonstračního příkladu je dostupný zde, jeho HTML verze se zvýrazněním syntaxe zde.

Screenshot prvního demonstračního příkladu
Obrázek 2: Screenshot prvního demonstračního příkladu

Druhý demonstrační příklad je podobný příkladu prvnímu s tím rozdílem, že křivka vytvářející čtverec je orientována opačně. To znamená, že oblast uvnitř čtverce s vrcholy [0.2, 0.2]-[0.8, 0.2]-[0.8, 0.8]-[0.2, 0.8] není součástí NURB plochy. Z tohoto důvodu je nutné, aby se pomocí křivky směřující proti směru hodinových ručiček nastavil i velký obalující čtverec o souřadnicích [0,0]-[1,0]-[1,1]-[0,1], jinak by byla plocha pro ořezání nekonečná, což je nepřípustné.

Zdrojový kód druhého demonstračního příkladu je dostupný zde, jeho HTML verze se zvýrazněním syntaxe zde.

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

Ve třetím demonstračním příkladu se ořezání neprovádí lomenou čarou (polyčarou), ale NURB křivkou třetího stupně, jejíž řídící body leží v parametrické rovině. Proto je také hrana otvoru zakulacená i při zadání pouze čtyř řídících bodů NURB křivky.

Zdrojový kód třetího demonstračního příkladu je dostupný zde, jeho HTML verze se zvýrazněním syntaxe zde.

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

bitcoin školení listopad 24

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

V následující části tohoto seriálu se budeme věnovat práci s rastrovými obrazy a texturami.

8. Seznam funkcí OpenGL, GLU a GLUT zmíněných v této části

gluBeginSurface()
gluBeginTrim()
gluEndSurface()
gluEndTrim()
gluNurbsSurface()
gluNurbsCurve()
gluPwlCurve()
 

9. Zkomprimovaná verze článku i s přílohami

Zkomprimovaná verze tohoto článku i s přílohami a demonstračními příklady je uložena zde.

Autor článku

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