OpenGL a nadstavbová knihovna GLU XV

Pavel Tišnovský



Úvodník

V dnešní části seriálu věnovanému nadstavbové grafické knihovně GLU si popíšeme význam nastavených hodnot uzlového vektoru a vah řídicích bodů na výsledný tvar NURB plochy. Uvedené poznatky budou prakticky ukázány na přiložených demonstračních příkladech.

Obsah

1. Význam uzlového vektoru NURB ploch
2. Vytvoření B-spline ploch z NURB ploch
3. Opakující se hodnoty v uzlovém vektoru
4. Váhy řídicích bodů
5. Kladné váhy řídicích bodů
6. Záporné a nulové váhy řídicích bodů
7. Konvexní obálka NURB plochy a její význam
8. Demonstrační příklady
9. Obsah dalšího pokračování
10. Seznam funkcí OpenGL, GLU a GLUT zmíněných v této části
11. Zkomprimovaná verze článku i s přílohami

1. Význam uzlového vektoru NURB ploch

NURB plochy patří do třídy parametrických ploch, u kterých musí být kromě souřadnic jejich řídicích bodů zadána i dvojice takzvaných uzlových vektorů (knot vectors), které mají na tvar NURB plochy stejně významný vliv, jako souřadnice jejich řídicích bodů. První uzlový vektor je přiřazen parametru u, druhý uzlový vektor parametru v v dvojrozměrném parametrickém prostoru U-V. Základní vlastnosti a pravidla pro tvorbu uzlového vektoru jsme si ozřejmili už v předchozích částech tohoto seriálu, zde pouze shrnu nejdůležitější informace.

Počet složek uzlového vektoru je závislý především na počtu řídicích bodů uspořádaných ve směru růstu daného parametru (u nebo v), protože řídicí body tvoří v parametrickém prostoru pravidelnou mřížku. Kromě počtu řídicích bodů také záleží na stupni plochy, který se opět udává ve směru růstu některého z parametrů a může být pro oba parametry odlišný. Počet hodnot uložených v uzlovém vektoru je roven součtu počtu příslušných řídicích bodů a stupni plochy, jež se zvětší o jedničku.

Posloupnost hodnot uložených v uzlovém vektoru musí být neklesající, jinak by nebylo možné vyčíslit rozsah parametrů, pro který je platná každá dvojice řídicích bodů a prostor mezi těmito body. Musí tedy platit nerovnosti:
ui<=ui+1   a
vi<=vi+1

Počet složek, které mají v uzlovém vektoru stejnou hodnotu musí být menší nebo roven stupni křivky. Pokud se složky se stejnou hodnotou v uzlovém vektoru nachází, musí ležet vedle sebe, jinak by neplatily podmínky uvedené v předchozím odstavci.

Z rekurentního vztahu pro výpočet NURB ploch dále vyplývá, že konkrétní hodnoty uložené v uzlovém vektoru nejsou shora ani zdola omezeny. Ve skutečnosti na těchto hodnotách příliš nezáleží, důležité jsou pouze rozdíly vždy mezi hodnotami dvou sousedních složek v uzlovém vektoru.

2. Vytvoření B-spline ploch z NURB ploch

NURB plochy tvoří nadmnožinu základních B-spline ploch, které jsou neracionální a uniformní. Neracionalita značí, že všechny řídicí body B-spline plochy mají váhu nastavenou na konstantní hodnotu jedna, tj. wij=1. Uniformitou je myšleno pravidelné rozmístění řídicích bodů v parametrickém prostoru, tj. z indexu řídicího bodu Cij lze bez dalších informací vyjádřit i jeho souřadnici v parametrickém prostoru U-V, stejně jako tomu bylo i u Bézierových ploch.

Neracionální uniformní B-spline plochy lze vytvořit tím způsobem, že se do uzlového vektoru zadají hodnoty, jež se od sebe liší o konstantu. Příkladem může být uzlový vektor s hodnotami [1,2,3,4,5,6]. Příklad vytvoření B-spline plochy je uveden v následující demonstrační funkci:


//---------------------------------------------------
// Vykreslení 3D scény s B-spline plochou
//---------------------------------------------------
void drawBspline(void)
{
#define U_POINTS 6                 // počet řídicích
#define V_POINTS 6                 // bodů ve směru
                                   // parametrů u a v

#define U_ORDER 4                  // stupeň plochy
#define V_ORDER 4                  // ve směru
                                   // parametrů u a v

#define U_KNOT (U_POINTS+U_ORDER)  // počet složek
#define V_KNOT (V_POINTS+V_ORDER)  // uložených v
                                   // uzlových vektorech

    // uzlové vektory
    GLfloat knots1[]={0.0, 0.1, 0.2,
                      0.3, 0.4, 0.5,
                      0.6, 0.7, 0.8, 0.9};
    GLfloat knots2[]={0.0, 0.1, 0.2,
                      0.3, 0.4, 0.5,
                      0.6, 0.7, 0.8, 0.9};

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

    // nastavení parametrů NURB plochy
    gluNurbsSurface(nurbs,
        U_KNOT,                    // uzlový vektor
        knots1,                    // pro parametr u
        V_KNOT,                    // uzlový vektor
        knots2,                    // pro parametr v
        V_POINTS * 3,              // vzdálenost mezi
        3,                         // položkami pole
        &ctlpoints[0][0][0],   // řídicí body
        U_ORDER, V_ORDER,          // stupeň plochy+1
        GL_MAP2_VERTEX_3);         // typ řídicích bodů

    // konec specifikace NURB plochy
    gluEndSurface(nurbs);
}

Obrázek 1: B-spline plocha vykreslená pomocí NURBS

Obrázek 1: B-spline plocha vykreslená pomocí NURBS

Při práci s B-spline křivkami i plochami se často používají tzv. násobné řídicí body, což jsou sousední řídicí body, které jsou umístěny na stejné souřadnici v ploše či prostoru. Pomocí násobných řídicích bodů lze na křivce/ploše vytvářet zlomy či úseky, které se k řídicím bodům více přimykají (tím se částečně nahrazují váhy řídicích bodů). Také lze zajistit, aby křivka/plocha procházela svými krajními body; u B-spline totiž tato vlastnost běžně splněna není, narozdíl od Bézierových křivek a ploch, které vždy svými krajními body prochází. Příklad B-spline plochy s násobnými řídicími body je uveden na druhém ilustračním obrázku.

Obrázek 2: B-spline plocha s násobnými řídicími body

Obrázek 2: B-spline plocha s násobnými řídicími body

3. Opakující se hodnoty v uzlovém vektoru

V uzlových vektorech se některé hodnoty mohou opakovat. Toho se využívá zejména pro zajištění průchodu plochy svými krajními řídicími body. NURB plocha se v tomto případě svými vlastnostmi podobá spíše Bézierovým plátům, než B-spline plochám. Příklad vytvoření NURB plochy, která prochází svými krajními body, je uvedený v následující funkci, obrázek s vytvořenou plochou je zobrazen na třetím obrázku.


//---------------------------------------------------
// Vykreslení 3D scény s NURB plochou, která
// prochází svými krajními body
//---------------------------------------------------
void drawBspline(void)
{
#define U_POINTS 6                 // počet řídicích
#define V_POINTS 6                 // bodů ve směru
                                   // parametrů u a v

#define U_ORDER 4                  // stupeň plochy
#define V_ORDER 4                  // ve směru
                                   // parametrů u a v

#define U_KNOT (U_POINTS+U_ORDER)  // počet složek
#define V_KNOT (V_POINTS+V_ORDER)  // uložených v
                                   // uzlových vektorech

    // uzlové vektory
    GLfloat knots1[]={0.0, 0.0, 0.0, 0.0,
                      0.33, 0.66,
                      1.0, 1.0, 1.0, 1.0};
    GLfloat knots2[]={0.0, 0.0, 0.0, 0.0,
                      0.33, 0.66,
                      1.0, 1.0, 1.0, 1.0};

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

    // nastavení parametrů NURB plochy
    gluNurbsSurface(nurbs,
        U_KNOT,                    // uzlový vektor
        knots1,                    // pro parametr u
        V_KNOT,                    // uzlový vektor
        knots2,                    // pro parametr v
        V_POINTS * 3,              // vzdálenost mezi
        3,                         // položkami pole
        &ctlpoints[0][0][0],   // řídicí body
        U_ORDER, V_ORDER,          // stupeň plochy+1
        GL_MAP2_VERTEX_3);         // typ řídicích bodů

    // konec specifikace NURB plochy
    gluEndSurface(nurbs);
}

Obrázek 3: NURB plocha, která prochází svými krajními body

Obrázek 3: NURB plocha, která prochází svými krajními body

Jak je z výše uvedeného kódu patrné, opakují se v uzlovém vektoru první a poslední čtyři hodnoty. Tento počet opakování není náhodný, ale vychází ze stupně použité NURB plochy. Pokud by byl počet stejných sousedních hodnot vyšší než stupeň křivky zvětšený o jednotku, generovala by se chyba - v důsledku by se při rekurzivním výpočtu dělilo v posledním kroku nulou.

4. Váhy řídicích bodů

Váhy řídicích bodů mají na tvar NURB plochy velký vliv, protože lokálně ovlivňují chování plochy v okolí řídicích bodů. Implicitně je nastaveno, že řídicí body mají svoji váhu rovnu jedné. Váha však může nabývat libovolných hodnot. Specifikace vah jednotlivých řídicích bodů se provádí ve funkci gluNurbsSurface(), ve které musí být poslední parametr type nastaven na hodnotu GL_MAP2_VERTEX_4 a předávané pole s řídicími body musí pro každý bod obsahovat čtyři složky - x, y, z a váhu w.

5. Kladné váhy řídicích bodů

Pokud je váha řídicích bodů nastavena na hodnotu větší než jedna, bude NURB křivka k těmto bodům více přitahována. Toho se využívá v situacích, kdy je zapotřebí vytvořit ostřejší zlomy na ploše. U Bézierových ploch a neracionálních B-spline ploch musela být tato problematika řešena buď použitím většího množství řídicích bodů, nebo použitím násobných řídicích bodů.

Obě zmíněné techniky však vedou k nárustu dat (tj. samotných řídicích bodů) a ke komplikované editaci ploch. Proto je změna vah řídicích bodů v mnoha případech ideálním řešením. Ukázka vlivu kladných vah na průběh NURB plochy je ukázána na čtvrtém a pátém ilustračním obrázku.

Extrémní případ nastane, pokud je váha řídicích bodů nekonečná. V tomto případě bude NURB plocha bodem procházet a bude se v něm případně lomit - to ovšem záleží na poloze ostatních řídicích bodů.

Obrázek 4: NURB plocha, u které mají všechny řídicí body jednotkovou váhu

Obrázek 4: NURB plocha, u které mají všechny řídicí body jednotkovou váhu

Obrázek 5: NURB plocha, u které mají některé řídicí body váhu zvětšenou

Obrázek 5: NURB plocha, u které mají některé řídicí body váhu zvětšenou

6. Záporné a nulové váhy řídicích bodů

Váhy řídicích bodů mohou být i záporné. V tomto případě je však NURB plocha od těchto bodů lokálně "odtlačována", což se hodí například při vytváření modelů s promáčklinami (páteř u člověka, karoserie automobilů apod.).

Nulové váhy řídicích bodů mají zajímavý efekt: bod s takto nastavenou vahou NURB plochu nijak neovlivní, tj. plocha k němu není ani přitahována ani odpuzována. Na první pohled by se mohlo zdát, že zadávat řídicí body s nulovou vahou je nesmyslné.

V mnoha případech tomu tak opravdu je, někdy však nastane situace, kdy je zapotřebí modelovat plochu, která má v některých místech požadavek na velkou hustotu řídicích bodů a v jiných místech má být naopak hustota velmi malá. V takovém případě lze vytvořit body s nulovou vahou, které nemají na vytvářenou plochu žádný vliv, musí však být uvedeny, protože NURB plocha je specifikována mřížkou řídicích bodů bez vynechaných míst.

7. Konvexní obálka NURB plochy a její význam

Pokud mají všechny řídicí body NURB plochy nastavenou kladnou váhu, leží celá NURB plocha v konvexní obálce těchto bodů, tj. v minimálním konvexním tělese, které těsně NURB plochu obepíná. Tato vlastnost je velmi důležitá, protože pomocí konvexní obálky lze řešit například kolize pohybujících se těles.

Postup při řešení kolizí je následující: nejprve se zkontroluje, zda nastala kolize konvexních obálek (to je výpočetně jednoduché, protože obálka je tvořena několika málo ploškami) a teprve při kolizi obálek se testují kolize NURB ploch, což je výpočetně mnohem složitější a samozřejmě také zdlouhavější.

Konvexní obálku lze mimochodem také využít při zobrazování NURB ploch metodou zpětného sledování paprsku (raytracingem), kdy lze průsečík paprsku prvotně řešit s komplexní obálkou a teprve při protnutí konvexní obálky paprskem s vlastní NURB plochou. Tuto metodu lze zkombinovat i s dalšími typy tzv. obalových těles (koule, osově orientované kvádry atd.).

8. Demonstrační příklady

Po spuštění prvního demonstračního příkladu se zobrazí osvětlená NURB plocha specifikovaná pomocí 36ti řídicích bodů uspořádaných do matice 6x6 bodů. Spolu s řídicími body jsou specifikovány i uzlové vektory, které tvoří bázi pro B-spline plochu. Pro ilustraci jsou znázorněny i řídicí body NURB plochy.

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 6: Screenshot prvního demonstračního příkladu

Ve druhém demonstračním příkladu je ukázáno, jakým způsobem mohou být použity násobné body pro vytváření křivky s prudkými zlomy.

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 7: Screenshot druhého demonstračního příkladu

Třetí příklad ilustruje použití uzlového vektoru, ve kterém jsou na začátku a konci zadány stejné hodnoty. Ty zapříčiní, že plocha prochází svými krajními body, podobně jako Bézierův plát.

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 8: Screenshot třetího demonstračního příkladu

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

V dalším pokračování tohoto seriálu bude vysvětleno, jakým způsobem se dají do NURB plochy vytvářet otvory. Jedná se o takzvané trimování, pomocí něhož lze tvořit složité modely těles (s dírami, konstrukčně složitými spoji apod.) složenými z NURB ploch.

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

gluBeginSurface()
gluNurbsSurface()
gluEndSurface()

11. 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: Pavel Tišnovský   2004