Vizualizace
Závěrečná zpráva
29. 12. 2002
Téma: Průsečík paprsku s děravou NURBS plochou
Řešitel: Pavel Halabala
Konzultant: Jaroslav Křivánek
1 Zadání
Podle dodaného kódu naimplementujte do raytraceru GOLEM metody pro výpočet průsečíku paprsku a děravé
(trimmed) NURBS plochy.
Pro úspěšnou realizaci zadání je nutné
- Porozumět základním vlastnostem NURBS ploch
- Prostudovat dodaný kód s článkem který ho popisuje
- Adaptovat kód pro použití v programu GOLEM
- Rozšířit parser VRML, který je součástí GOLEMa o načítání NURBS ploch (triviální)
není nutné
- Porozumět architektuře programu GOLEM. Stačí implementovat pouze rozhraní 3D objektu a rozšířit parser
(který je velice modulární).
Odkazy:
1.1 Podrobnosti zadání
Úkolem bylo implementovat metodu sledování paprsku děravých nurbs ploch do školního raytraceru GOLEM. Vstupním formátem
byl VRML, bylo tedy nejprve nutné rozšířit VRML parser o nový objekt - obecnou nurbs plochu - dle standardu VRML. Hlavním
bodem mé práce měl být přenos dodaného kódu do GOLEMu a jeho odladění. K dispozici jsem měl rovněž dokumentaci výpočetních
metod použitých v kódu. Pro test funkčnosti programu jsem měl vytvořit několik scén s obecnou nurbs plochou (plochami),
které budou demonstrovat různé základní vlastnosti materiálu jako je zrdcadlový odraz, lom, atd.
2 Teorie
Tento oddíl popisuje algoritmy používané při sledování paprsku u NURBS ploch.
2.1 Obecný postup při raytracingu NURBS ploch
- Jako pre-proces vytvoříme množinu oblastí, které ohraničují NURBS plochu po částech daných
intervalem parametrů
- Testujeme nejprve průsečíky paprsku s těmito oblastmi, tím zjistíme oblast, resp.
interval parametrů, ve kterém leží průsečík a tímto intervalem inicializujeme
numerický algoritmus pro hledání průsečíku
- Základní myšlenky tohoto postupu jsou:
- Výběr oblastí, kterými by mohl paprsek procházet
- Jakým způsobem efektivně spočítat průsečíky s těmito oblastmi
- Jakým způsobem najít parametry (u,v) průsečíku paprsku s plochou uvnitř oblasti
- Jak efektivně spočítat souřadnice bodu na ploše pro dané parametry (u,v)
Popis obr. 1: Základní metoda užívaná pro nalezení průsečíku paprsku s parametrickým objektem ukázaná na 2D příkladě. Vlevo:
paprsek je testován na průsečík s množinou osově zarovnaných ohraničujících oblastí. Vpravo: Pro každý zásah ohraničující
oblasti se generuje počáteční odhad v parametrickém intervalu, který tato oblast ohraničuje. Hledání průsečíku se počítá iterativně,
dokud není splněno divergentní či konvergentní kritérium.
2.2 Konkrétní implementace raytracingu NURBS ploch (dle dodané dokumentace)
- Otázka: Ve kterých bodech prochází paprsek plochou?
- Paprsek lze definovat jako průsečík dvou ploch {p|P1*(p,1)} a
{p|P2*(p,1)}, kde P1=(N1,d1) a
P2=(N2,d2). Normály N1 a N2
těchto ploch a zároveň jejich obecné (implicitní) rovnice lze jednoduše určit ze směrového vektoru
paprsku d
- Průsečík paprsku s NURBS plochou S se pak spočítá jako průsečík 3 ploch: 2 ploch definujících
papsek a NURBS plochy. musí splňovat 2 podmínky:
- P1*(S(u,v),1)=0
- P2*(S(u,v),1)=0
- Výsledné 2 implicitní rovnice lze řešit pro u a v pomocí numerických metod. V našem případě
byla použita Newtonova metoda.
- Ray Tracing NURBS ploch se provádí v několika krocích:
- Jako pre-process se provede rozdělení plochy hierarchií ohraničujících oblastí na jejím definičním oboru
metodou zjemňování (refinement). Je pro to několik důvodů:
- Prvotní odhad parametrů průsečíku (u,v) musí být již velmi blízko, abychom
dosáhli kvadratické konvergence Newtonovy metody
- Po zjemňení plochy můžeme ohraničit její pod-záplaty (sub-patches) a použím
hierarchie ohraničujících oblastí (BVH - Bounding Volume hierarchy) vybírat
pouze vhodné paprsky a zůžit interval pro další hledání průsečíku numerickou
metodou
- Důležité: Zjemněná plocha nezůstává v paměti, použije se pouze pro vytvoření BVH a pak se smaže!!!
- Při samotném raytracingu se prochází BVH za účelem nalezení oblasti, jíž paprsek
prochází (tj. listu BVH), poté se aplikuje numerická metoda pro nalezení řešení dvou výše popsaných rovnic.
Výsledkem bude buď právě jeden průsečík daný parametry (u,v) nebo průsečík neexistuje
Obr. 2: Implementace raytraceru podle dodané documentace
2.3 Hledání průsečíku
Jde v podstatě o řešení dvou rovnic o dvou neznámých (u,v). Byla použita zmíněná Newtonova metoda, která
iterativně počítá nový odhad místa průsečíku z předchozího odhadu. Zahrnuje 4 základní kritéria pro zastavení iterace:
- Kritérium úspěchu: odchylka od správného řešení je menší než námi definované epsilon
- Kritérium selhání: odchylka následnující iterace je větší než odchylka iterace předchozí, tedy nedovolíme,
aby se řešení "vzdalovalo"
- Kritérium selhání: parametr u nebo parametr v vyběhne ze svého definičního oboru
- Kritérium selhání: počet iterací překročí maximální dovolenou mez
Obr 4: Hledání průsečíku
2.4 Ořezávací křivky (Trimming curves)
- Používají se pro ořezání částí modelu, jak jsou definovány?
- Ořezávací křivka je uzavřená orientovaná křivka, která leží na NURBS ploše. Je definována jako funkce parametrů
plochy (u,v). Nemusí být nutně konvexní!
- Orientace je důležitá pro určení, která část plochy má být zachována. Podle konvence se zachovává pravá část
plochy ve směru orientace křivky
- Důležitá charakteristika ořezávacích křivek je, že se nesmí křížit, ale mohou se vnořovat do sebe a dotýkat
- Protože víceznačné orientace nejsou dovoleny, ořezávací křivky se stejnou hloubkou v hierarchii musí mít stejnou orientaci.
2.5 Tvorba hierarchie ořezávacích křivek
Protože se ořezávací křivky mohou do sebe vnořovat, je pro určení výsledných ořezaných a neořezaných částí plochy
nutné si zapamatovat jejich topologii. K tomu se používá hierarchie ořezávacích křivek.
Je dána množina ořezávacích křivek na obecné B-spline ploše. Vzhledem k tomu, že se křivky nesmí křížit, existují
pouze 3 možné vztahy meze dvěma křivkami c1 a c2. c1 může obsahovat
c2, být obsažena v c2 nebo nesdílet s c2 žádnou oblast. Každý uzel v naší
hierarchii je seznam ořezávacích křivek a každá křivka může odkazovat na další seznam do ní spadajících křivek.
Obr. 5: Množina ořezávacích křivek a výsledná hierarchie.
Dále pro každou křivku a pro každý seznam křivek uložíme ohraničující oblast, kterou použijeme pro urychlení výběru potenciálně
vhodných paprsků (procházejících touto oblastí).
2.6 Ray Tracing děravých NURBS
Nejdříve nalezneme průsečík paprsku s neořezanou NURBS plochou. Pokud byl nalezen průsečík (u,v), podíváme se do
hierarchie ořezávacích křivek, abychom zjistili zda má být bod ořezán nebo zda má být vrácen zásah.
Obr. 5: Raytracing děravých NURBS ploch
3 Implementace
3.1 Rozšíření VRML parseru
Parser byl rozšířen o obecnou NURBS plochu dle standardu VRML následovně:
NurbsSurface {
field SFInt32 uDimension 0 # [0,
)
field SFInt32 vDimension 0 # [0,
)
field MFFloat uKnot [] # (-
,
)
field MFFloat vKnot [] # (-
,
)
field SFInt32 uOrder 3 # [2,
)
field SFInt32 vOrder 3 # [2,
)
exposedField MFVec3f controlPoint [] # (-
,
)
exposedField MFFloat weight [] # (0,
)
exposedField SFInt32 uTessellation 0 # (-
,
)
exposedField SFInt32 vTessellation 0 # (-
,
)
exposedField SFNode texCoord []
field SFBool ccw TRUE
field SFBool solid TRUE
}
Pole, která GOLEM ignoruje jsou:
- uTesselation a vTesselation
- texCoord - textury ještě nejsou podporovány
- ccw
Ostatní pole se ukládají do pamětí a jsou interpretována dle standartu VRML (+vypořádání se s vynechanými nebo špatně zadanými
hodnotami - viz dále). Pro každou logicky rozdílnou část popisu NURBS plochy existuje samostatná třída.
Byly tedy vytvořeny následující třídy:
- CNurbsSurface - reprezentuje NURBS plochu
- CControlMesh - reprezentuje matici řídících bodů
- CKnotVector - reprezentuje uzlový vektor
Rozšíření parseru proběhlo celkem bez problémů. Parser se umí vypořádat i s drobnými chybami v popisu NURBS plochy
jako jsou vynechání nebo chybný zápis uzlového vektoru. V případě vynechání některého uzlového vektoru se automaticky
vytvoří vektor s uniformním dělením uzlů. Zpřeházené hodnoty uzlů se vzestupně setřídí a nakonec se vektor znormuje
do intervalu <0,1>. Chybějící hodnoty vah se nahradí jedničkami.
3.2 Analýza a přenos dodaného kódu do GOLEMu
Jak již bylo uvedeno, měl jsem k dispozici kód pro výpočet průsečíku paprsku s nurbs plochou, který bylo nutno nejprve přenést
do GOLEMu a posléze odladit. Kód bohužel neobsahoval úplnou implementaci raytraceru. Úplně chyběl výpočet hierarchie ořezávacích
oblastí popsanou metodou zjemňování plochy. Tato hierarchie je nezbytně nutná pro stanovení počátečního odhadu parametrů (u,v)
možného průsečíku. Bez její aplikace je Newtonova metoda prakticky nepoužitelná. Při náhodné volbě počátečního odhadu dochází ke
dvěma problémům:
- Newtonova metoda konverguje řádově pomaleji pokud počáteční odhad není dostatečně blízký
- Může se stát, že ke konvergenci nedojde vůbec (ve scéně se objevují černé fleky)
Jelikož by dodatečné zabudování hierarchie ořezávacích oblastí do programu bylo časově velmi náročné ( hlavně díky tomu,
že nevím na jaké problémy bych mohl ještě narazit), vepsal jsem na patřičná místa v kódu podrobné komentáře, jaký
algoritmus či strukturu by bylo vhodné použít pro vyřešení tohoto problému. Podle dodané dokumentace jsem také napsal
metodu, která obsahuje heuristiku určující jemnost dělení plochy podle jejího zakřivení v daném místě. Jediné co je třeba
dopsat a vymyslet, je algoritmus, který rozdělí nurbs plochu na části a z těchto částí vypočítá osově zarovnané kvádry.
Pro dovedení programu do fungujícího stavu jsem použil jediný osově zarovnaný kvádr, který ohraničuje celou nurbs plochu.
Počáteční odhad se obecně bere vždy v polovině intervalu parametrů, které vymezuje daný kvádr, a protože v mém případě
existuje jen jeden, počáteční odhad bude vždy v polovině nurbs plochy. I tímto způsobem se dá dosáhnout rozumných výsledků,
ale pouze v případě použití konvexních objektů, u kterých Newtonova metoda konverguje téměř vždy. Realizovaná implementace
raytraceru vypadá tedy následovně:
Obr. 3: Realizovaná implementace raytraceru
3.3 Hierarchie ohraničujících oblastí
Dodaný kód neobsahoval implementaci hierarchie ohraničujících oblastí, proto zde alespoň popíšu možný postup. Tento postup
jsem rovněž popsal formou komentářů na patřičných místech v kódu.
- BVH je strom ohraničujících oblastí, ve kterém každý rodičovský uzel ohraničuje všechny své potomky. Kořenem tohoto
stromu je oblast ohraničující celou nurbs plochu
- BVH vytvoříme pomocí bodů získaných zjemňovacím algoritmem. Výstupem tohoto algoritmu je zjemněná matice řídících
bodů, ze které se zkonstruují jednotlivé ohraničující oblasti.
- Vlastnost konvexní obálky B-spline ploch nám garantuje, že celá plocha je obsažena v konvexní obálce
svých řídících bodů. V důsledku tedy jakékoliv konvexní objekty ohraničující konvexní obálku budou
ohraničovat i naši plochu. Při použití mé metody FlatteningHeuristic lze dokázat, že každý neprázdný
interval [ui,ui+1)x[vj,vj+1) parametrů (u,v) na ploše
koresponduje se záplatou plochy, která je celá obsažena v konvexní obálce korespondujících řídících bodů, které získáme
zmíněnou procedurou zjemňování plochy. Tedy pokud vyrobíme ohraničující oblasti pro každý z těchto intervalů, bude
celá plocha ohraničena. Strom vytvoříme shora seřazením oblastí podle osy s největší excentricitou a jejich rozdělením
na poloviny, které představují levý a pravý podstrom. Dále pokračujeme stejnou procedurou v levé a pravé polovině.
- Kořen a vnitřní uzly hierarchie budou tedy obsahovat jednoduchá primitiva ohraničující části plochy
- Listy stromu jsou tzv "intervalové objekty", které se použijí pro počáteční odhad v Newtonově metodě.(v našem případě
to budou středy ohraničených intervalů)
- Jaký útvar použít pro ohraničující oblast? Lze použít různá grafická primitiva (kvádr, kouli, ...). Pro kouli je sice
velmi rychlé spočítat průsečík, ale má tu nevýhodu, že není plochá - to se nehodí pro scény, kde
je mnoho rovných oblastí rovnoběžných s osami. Já preferuji osově zarovnané kvádry (se stranami rovnoběžnými s osami x,y,z),
které mají mnoho výhod:
- Dají se zploštit podle podle směru os, a tím umožnit lepší přiléhavot k ploše.
- Sjednocení dvou kvádrů lze spočítat velmi jednoduše, to je potřeba při tvoření BVH od listů ke kořeni
- Mnoho scén obsahuje objekty rovnoběžné s osami, pro které jsou kvádry jako ohraničující objekty téměř ideální
3.4 Ořezávací křivky (Trimming curves)
V GOLEMu jsou implementovány rovněž děravé NURBS plochy, ale kód byl přenesen tak jak je a není tudíž záruka, že bude
správně fungovat!!! Zbývá doplnit parser o definici ořezávacích křivek a vše odladit. Třída CNurbsSurface
obsahuje 2 konstruktory: jeden pro děravou a druhý pro neděravou nurbs plochu. Metoda pro výpočet průsečíku je implementována
i pro děravou plochu, ale pro děravé plochy nebyla testována!!!
Třídy pro ořezávací křivky jsou následující:
- CTrim - reprezentuje ořezávací křivku
- CTrimList - reprezentuje seznam ořezávacích křivek
Algoritmus, který testuje zda se bod nachází uvnitř nebo vně ořezávací křivky pracuje nyní s lineární složitostí
vzhledem k počtu bodů, kterými je křivka definována. GOLEM obsahuje podobný algoritmus s logaritmickou složitostí,
který lze v budoucnosti implementovat i zde.
3.5 Ostatní problémy během implementace
Při přenášení kódu a ladění jsem narazil na spoustu problémů, které byly většinou způsobeny nepochopením rozsáhlého matematického
aparátu použitých metod. Bylo pro mě opravdu obtížné odhalit některé chyby nejen v mém kódu, ale i v tom originálním. Ten obsahoval
dvě nebo tři chyby, které mi dalo práci odhalit. Nemůžu zde popisovat všechny problémy, protože bych musel vysvětlit také některé
matematické principy z uvedené literatury [1].
Měl jsem například problémy s vahami řídících bodů. Domníval jsem se, že mají stejný význam jako váhy v homogenním souřadném systému.
To je sice pravda, ale zapoměl jsem na jednu důležitou věc. Řídící bod s velmi velkou hodnotou váhy by měl ležet téměř na ploše, ale
v homogenním souřadném systému by tento bod po korekci jeho velkou váhou ležel skoro v počátku. Pro vyřešení tohoto problému je
třeba souřadnice bodu (x,y,z) nejprve vynásobit hodnotou váhy a poté je vše v pořádku.
Dále mi občas dělalo problémy porozumět GOLEMu. Měl jsem například problémy s barvami. Nevěděl jsem přesně pro jakou hodnotou
parametru paprsku má být vrácen zásah, aby byl objekt vidět. Nakonec jsem úspěšně všechny tyto problémy vyřešil, protože chyba
nebyla v GOLEMu, ale ve mně.
4 Výsledky
Kvůli výše zmíněným problémům s hierarchií ohraničujících oblastí jsem měl omezenou volbu objektů pro testovací scény. Hlavní
testovací objekt byl proto válec vytvořený pomocí nurbs plochy. Výsledky jsem porovnával s obyčejným válcem (cylinder) a rozdíly
jsou neznatelné.
Zkoušel jsem i složitější objekty skládající se z 10 a více nurbs ploch. Výsledný tvar odpovídá, ale na scéně
se objevují černé fleky v místech, kde Newtonova metoda nezkonvergovala, což je v souladu s přechozím vysvětlením. (viz poslední
obrázek - Stůl)
Pod obrázky scén jsou znázorněny doby výpočtu na počítači s procesorem Intel Celeron 500 MHz v operačním systému Linux RedHat 7.2.
Delší časy u NURBS ploch by měly být zkráceny po implementaci hierarchie ohraničujících oblastí.

Průnik (2 Válce)
Čas: 5.9 s

Průnik (2 Nurbs)
Čas: 36.2 s

Zrdcadlení (Válec)
Čas: 18.5 s

Zrdcadlení (Nurbs)
Čas: 69.4 s

Lom (Válec)
Čas: 18.8 s

Lom (Nurbs)
Čas: 76.2 s

Pokřivená Nurbs
Čas: 66.7 s

Stůl (Golem)
Čas: 535.7 s

Stůl (Cortona)
5 Souhrn
Implementované části:
- Parser VRML uzlu NurbsSurface
- Rutina pro výpočet průsečíku papsku s obecnou NURBS plochou
- Děravé NURBS (bez jakékoli záruky), tato část se musí ještě odladit a otestovat!
Části, které je třeba implementovat v budoucnosti:
- Hierarchie ohraničujících oblastí
- Parser VRML uzlu TrimmedSurface a odladění raytraceru
- Podporu textur
Veškerá kód operující s NURBS plochami se nachází v souboru nurbs.cpp(+nurbs.h), který obsahuje
implementaci následujících tříd:
- CNurbsSurface - reprezentuje nurbs plochu, důležité metody:
- Preprocess - Obsahuje předpočítání některých důležitých hodnot, tvoří hierarchii ohraničujících
oblastí (ikdyž zatím pouze s jedním objektem, který ohraničuje celou plochu)
- Eval - Výpočet bodu na ploše v závislosti na u a v. Existují 2 verze, druhá verze
počítá navíc derivace ve směrech u a v
- BuildBVH - tvoří hierarchii ohraničujících oblastí
- CControlMesh - reprezentuje pole řídících bodů, důležité metody:
- ApplyTransform - Aplikuje transformaci danou maticí na pole řídících bodů
- BBox - vrátí bounding-box pole řídících bodů (dané nurbs plochy)
- CKnotVector - reprezentuje uzlový vektor
- NewtonSolver - obsahuje implementaci Newtonovy metody formou statických metod, důležitá metoda:
- NSolver - provede iterační algoritmus hledání průsečíku, vstupem je mimojiné též počáteční odhad
- CTrim - reprezentuje ořezávací křivku, důležitá metoda:
- IsInterior - vrací zda je bod definovaný parametry na ploše (u,v) uvnitř ořezávací křivky
- CTrimList - reprezentuje seznam ořezávacích křivek, důležitá metoda:
- Keep - vrací zda je bod ořezán nebo ne
- CAABoxNurbsLeaf* - reprezentuje list v hierarchii ohraničujících oblastí, který ohraničuje určitou
část plochy a obsahuje počáteční odhad pro Newtonovu metodu. V současné implementaci se vytvoří jeden takový
objekt pro celou nurbs plochu s počátečním odhadem v polovině plochy.
- CAABoxNurbsNode* - reprezentuje ohraničující oblast v hierarchii ohraničujících oblastí, obsahuje
odkaz na 2 podoblasti typu CAABoxNurbsNode nebo CAABoxNurbsLeaf, které ohraničuje.
* Tento způsob řešení hierarchie ohraničujících oblastí byl převzat z dodané literatury, je třeba
pouze dovést implementaci do konce. Potřebné instrukce jsou obsaženy též v komentářích.
6 Závěr
NURBS plochy jsou v počítačové grafice velmi důležité objekty. Mohou být použity k aproximaci objektů s lepší
přesností než trojůhelníky a výsledná velikost dat je většinou menší. NURBS plochy se dají také jednoduše a
intuitivně tvarovat. Bohužel, za tím vším stojí rozsáhlé matematické pozadí a není už tak jednoduché efektivně
spočítat jejich tvar. Pro dosažení slušného výkonu je třeba použít rafinovaných metod, ale naneštěstí neexistuje
absolutně nejlepší metoda. Jedna z nich byla implementována v dodaném kódu, ale má práce nespočívala pouze v okopírování
už existujícího kódu. Musel jsem podrobně nastudovat použité metody, abych mohl vyřešit všechny problémy, které
nastaly během přenosu kódu do GOLEMu a hlavní dosažený cíl je, že rutina pro výpočet průsečíku s paprskem funguje
v GOLEMu správně.