Hlavní navigace

OpenGL evaluátory (6)

15. 6. 2004
Doba čtení: 7 minut

Sdílet

V dnešní části seriálu o evaluátorech OpenGL si povíme, jakým způsobem je možné zjednodušit a v mnohých případech i urychlit vykreslování Bézierových křivek a ploch. Pomocí dvou nových příkazů lze naráz (tj. bez programové smyčky) vykreslit celou Bézierovu plochu nebo křivku.

Obsah

Úvod
Funkce glMapGrid()
Funkce glEvalMesh
()
Zjednodušení vykreslování Bézierových křivek
Zjednodušení vykreslování Bézierových ploch
Demonstrační příklady
Pokračování
Seznam funkcí OpenGL zmíněných v této části
Nové funkce OpenGL popsané v této části
Zkomprimovaná verze článku i s přílohami

Úvod

V předchozích několika částech tohoto seriálu jsme se postupně zabývali programovým výpočtem Bézierových křivek a ploch. Následně jsme si řekli, jakým způsobem lze pro práci s těmito grafickými prvky použít evaluátory, pomocí nichž jsme dosáhli urychlení výpočtu i zobrazení těchto těles. Jak však bylo patrné z demonstračních příkladů, stále se evaluátory používaly poměrně neefektivně – každý bod ležící na křivce či ploše musel být pomocí funkce glEvalCoord*() v programové smyčce samostatně vyhodnocen a poslán jako vrchol vykreslovaného objektu do grafického vykreslovacího řetězce.

Neefektivita výše popsané činnosti spočívá především ve zbytečném posílání dat mezi OpenGL a aplikací, nemluvě o programovém provádění výpočtů. V OpenGL však existuje alternativa ke dříve zmíněnému způsobu práce s Bézierovými křivkami a plochami. Tato alternativa spočívá v tom, že se nejprve zadají všechny parametry Bézierovy křivky či plochy a následně se specifikuje, jakým typem grafických primitiv se má křivka/plocha vykreslit. Po tomto zadání se celý model vykreslí pomocí jednoho příkazu bez zbytečných programových smyček a volání funkcí OpenGL.

Předností tohoto způsobu práce s evaluátory je zjednodušení zápisu programu, protože se pro výpočet jednotlivých vrcholů na křivce nebo ploše nemusí používat programová smyčka – vyhodnocení se provede přímo v OpenGL. Další předností je vyšší rychlost zpracování, protože zejména GPU procesory na novějších grafických kartách mohou veškeré složité výpočty optimalizovat.

Samozřejmě existují také nějaké nevýhody. Značnou nevýhodou je, že nelze manuálně měnit vlastnosti každého vrcholu, například jeho barvu, souřadnice do textury, normálový vektor apod. V případě, že jsou tyto vlastnosti konstantní nebo je lze vypočítat pomocí Bézierovy funkce, nenastávají vážnější problémy. Ty nastanou v případě, že se některá vlastnost (atribut) vrcholu mění podle jiných pravidel, například se načítá z tabulky. Tuto nepříjemnost lze částečně obejít použitím funkce glEvalPoint*().

Funkce glMapGrid*()

První funkcí, kterou si v dnešní části popíšeme, je funkce, pomocí níž se nastavuje rozsah parametrů Bézierovy křivky. Ve druhé části tohoto seriálu jsme si ukázali, jakým způsobem je možné vyčíslit body, které leží na Bézierově křivce. Funkce Bézierovy křivky vrací pro každou hodnotu parametru t souřadnici jednoho bodu. Postupným dosazováním různých hodnot parametru t tak lze vypočítat body, jež na křivce leží.

Funkce glMapGrid1*() provádí přípravu na „hromadné mapování“ mnoha hodnot parametru t. Stačí pouze zadat počáteční a koncovou hodnotu (t1 a t2), kterých má tento parametr nabývat; současně se také musí zadat celkový počet vytvořených hodnot parametru t. Při zavolání dále popsané funkce glEvalMesh1() se parametry postupně začnou vyhodnocovat, tj. bude se vytvářet řada hodnott1, t1+delta, t1+2*delta, … t2, které se budou dosazovat do funkce Bézierovy křivky.

Hodnota delta je vypočtena na základě počáteční a koncové hodnoty spolu s celkovým počtem generovaných hodnot.

Funkce glMapGrid1*() existuje ve dvou variantách, které se liší pouze typem předávaných hodnot:

void glMapGrid1d(
    GLint    tn,
    GLdouble t1,
    GLdouble t2
);

resp.

void glMapGrid1f(
    GLint    tn,
    GLfloat  t1,
    GLfloat  t2
);

kde:

  1. tn představuje celkový počet generovaných hodnot ti.
  2. t1 je počáteční hodnota parametru t: t1.
  3. t2 je koncová hodnota parametru t: t2.

V případě práce s Bézierovými plochami taktéž existují funkce pro automatické vyhodnocení bodů na jejich povrchu. Nejprve se však musí specifikovat rozsah parametrů u a v. Navíc je možné plochu vytvořit s různým počtem vrcholů ve směru parametru u a v. Proto je také specifikace poněkud komplikovanější a zajišťují ji tyto dvě funkce:

void glMapGrid2d(
    GLint    un,
    GLdouble u1,
    GLdouble u2,
    GLint    vn,
    GLdouble v1,
    GLdouble v2
);

resp.

void glMapGrid2f(
    GLint    un,
    GLfloat  u1,
    GLfloat  u2,
    GLint    vn,
    GLfloat  v1,
    GLfloat  v2
);

kde:

  1. un je celkový počet generovaných hodnot ui.
  2. u1 je počáteční hodnota parametru u: u1.
  3. u2 je koncová hodnota parametru u: u2.
  4. vn je celkový počet generovaných hodnot vj.
  5. v1 je počáteční hodnota parametru v: v1.
  6. v2 je koncová hodnota parametru v: v2.

Funkce glEvalMesh*()

Po nastavení rozsahů parametrů pomocí funkcí glMapGrid*() je možné přistoupit k vlastnímu vykreslení Bézierovy křivky. Pro tuto činnost je určena funkce glEvalMesh1(), po jejímž zavolání se celá křivka vykreslí. Tato funkce má následující prototyp:

void glEvalMesh1(
    GLenum mode,
    GLint  ti1,
    GLint  ti2
);

kde:

  1. Parametrem mode se udává, jakou grafickou primitivou se bude křivka vykreslovat. Možné jsou dvě hodnoty: GL_POINT – vykreslují se body a GL_LINE – vykreslí se lomená čára (pozor – není toGL_POINTS, ale pouze GL_POINT – tady se dělá často chyba).
  2. V parametru ti1 se předává index hodnoty pro počáteční bod. Pokud je zde uvedena nula, mapuje se index na hodnotu t1, pokud je zde tn (maximální počet bodů), mapuje se index na hodnotut2.
  3. V parametru ti2 se předává index hodnoty pro koncový bod. Vlastnosti má stejné jako parametr ti1.

Pro vykreslení Bézierových ploch existuje podobný příkaz jako pro Bézierovy křivky:

void glEvalMesh2(
  GLenum   mode,
  GLint    i1,
  GLint    i2,
  GLint    j1,
  GLint    j2
);

kde:

  1. V parametru mode se předává typ grafické primitivy, kterou se plocha vykreslí. Možné hodnoty tohoto parametru jsou: GL_POINT – body,GL_LINE – polyčáry a GL_FILL – pruhy čtyřúhelníků.
  2. V parametrech i1 a i2 jsou uloženy počáteční a koncové indexy generovaných hodnot ui.
  3. V parametrech j1 a j2 jsou uloženy počáteční a koncové indexy generovaných hodnot vj.

V dalších odstavcích si ukážeme použití všech výše popsaných funkcí.

Zjednodušení vykreslování Bézierových křivek

V následujícím fragmentu kódu je ukázáno vykreslení Bézierovy křivky pomocí polyčáry:

//-----------------------------------------------
// Vykreslení Bézierovy křivky pomocí evaluátorů
//-----------------------------------------------
void drawBezierUsingEvaluators(GLfloat points[][3])
{
#define CAR 50

  // dosadit řídící body do evaluátoru
  glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4,
           points[0][0]);

  // křivka se vykreslí z padesáti lomených čar
  glMapGrid1f(CAR, 0.0f, 1.0f);

  // vlastní vykreslení křivky
  glEvalMesh1(GL_LINE, 0, CAR);
}

Zjednodušení vykreslování Bézierových ploch

Vykreslení vyplněné Bézierovy plochy je možné dosáhnout pomocí následujících příkazů:

//-----------------------------------------------
// Vykreslení Bézierovy plochy pomocí evaluátorů
//-----------------------------------------------
void drawBezierSurfaceUsingEvaluators(GLfloat points[][4][3])
{
#define DELENI 30

  glColor3f(0.7f, 0.7f, 0.7f);

  // předání řídících bodů Bézierovy plochy
  glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4,
             points[0][0][0]);

  // plocha se vykreslí ze čtyřúhelníků
  glMapGrid2f(DELENI, 0.0f, 1.0f, DELENI, 0.0f, 1.0f);

  // vlastní vykreslení plochy
  glEvalMesh2(GL_FILL, 0, DELENI, 0, DELENI);
} 

Demonstrační příklad

Po spuštění prvního demonstračního příkladu se vykreslí Bézierova křivka, která je vypočtena pomocí evaluátorů. Výpočet probíhá ve funkci drawBezierUsin­gEvaluators(). Nepoužívá se však programová smyčka, ale dvojice funkcí glEvalMesh1() a glMapGrid1f(). Bézierova křivka je zobrazena pomocí bodů, protože ve funkci glEvalMesh1() je parametr mode nastaven na hodnotu GL_POINT.

Počet vypočtených a zobrazených bodů se řídí konstantou BODU, která je implicitně natavena na hodnotu 50.

Pomocí myši lze jednotlivými řídícími body pohybovat po obrazovce, a měnit tak výsledný tvar Bézierovy křivky. Klávesou ESC je možné program ukončit, klávesa F přepíná zobrazení na celou obrazovku a klávesa W přepne zobrazení zpět do okna o velikosti kreslicí plochy 450×450 pixelů.

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

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

Dnešní druhý demonstrační příklad vychází z příkladu prvního. Jediný rozdíl spočívá v tom, že se ve funkci drawBezierUsin­gEvaluators() Bézierova křivka vykreslí jako lomená čára. Je to způsobeno nastavením parametrumode na hodnotu GL_LINE při volání funkce glEvalMesh(). Počet čar se řídí konstantou CAR, která je implicitně nastavená na hodnotu 50.

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

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

Po překladu a spuštění třetího demonstračního příkladu se zobrazí Bézierova bikubická plocha, která je vypočtena pomocí evaluátorů. Výpočet probíhá ve funkci drawBezierSur­faceUsingEvalu­ators() a jsou při něm použity výše popsané funkce glEvalMesh2() a glMapGrid2f().

Bézierova plocha se zobrazí pomocí jednotlivých bodů vykreslovaných grafickou primitivou GL_POINT. Hustota mřížky, tj. počet bodů ve směru parametru u i parametru v, se specifikuje pomocí konstanty DELENI, ktera je implicitně nastavena na hodnotu 30.

Pomocí klávesy S je možné spustit či zastavit animaci Bézierovy plochy, která spočívá v její rotaci okolo středu. Klávesou ESC neboQ se program ukončí, klávesy W a F slouží ke zvětšení a/nebo zmenšení hlavního okna aplikace.

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

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

Čtvrtý demonstrační příklad se podobá příkladu třetímu. Jediný rozdíl spočívá v tom, že se Bézierova plocha vykreslí pomocí polyčar – ve funkci glMapGrid2f() je první parametr nastaven na GL_LINE. Hustota mřížky se zadává pomocí konstanty DELENI, která je v tomto případě nastavena na hodnotu 20.

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

Screenshot čtvrtého demonstračního příkladu
Obrázek 4: Screenshot čtvrtého demonstračního příkladu

Pátý demonstrační příklad vychází ze třetího a čtvrtého demonstračního příkladu, Bézierova plocha je však vykreslena pomocí pásu ze čtyřúhelníků.

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

Screenshot pátého demonstračního příkladu
Obrázek 5: Screenshot pátého demonstračního příkladu

UX DAy - tip 2

Pokračování

V dalším pokračování tohoto seriálu si ukážeme použití evaluátorů při práci s barvami a souřadnicemi do textury.

Seznam funkcí zmíněných v této části

glEvalCoord()
glEvalPoint
()

Nové funkce popsané v této části

glMapGrid1d()
glMapGrid1f()
glMapGrid2d()
glMapGrid2f()
glEvalMesh1()
glEvalMesh2()
 

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

Zkomprimovaná verze tohoto článku je umístěna zde.

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

Autor článku

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