Obsah
1. Konstruktivní geometrie těles (CSG)
2. Množinové operace s tělesy
3. Zápis prostorových scén v jazyce POV-Raye
4. První demonstrační příklad – ukázka jednoduché prostorové scény
5. Základní vyplněná konečná tělesa podporovaná POV-Rayem
6. Druhý demonstrační příklad – vykreslení základních konečných těles
7. Obsah další části seriálu
1. Konstruktivní geometrie těles (CSG)
Reprezentace modelů pomocí konstruktivní geometrie těles (Constructive Solid Geometry – CSG) je založena na analytickém popisu těles jejich objemem, tj. podmnožinou trojrozměrného prostoru ležícího uvnitř tělesa. V literatuře se lze občas setkat s tvrzením, že konstruktivní geometrie těles patří mezi metody popisující hranici těles. To však není zcela přesné, protože v CSG můžeme pro libovolný bod jednoduše zjistit, zda leží uvnitř či vně tělesa, což se v POV-Rayi velmi často používá.
Na druhou stranu se pro mnohá tělesa složitě zjišťují souřadnice bodů na povrchu těles, mnohdy je dokonce nutné použít numerické metody. Minimálně těmito vlastnostmi se tedy CSG odlišuje od běžně chápané hraniční reprezentace, kterou jsme si popsali v předchozí části tohoto seriálu. Vývoj konstruktivní geometrie byl zahájen v roce 1973 s cílem definovat složité útvary pomocí operací sjednocení, průniku, rozdílu a hladkého napojení nad základními tělesy (primitivy).
Jako primitiva se většinou používají objekty popsatelné pomocí jedné či soustavou několika nerovnic, v nichž se vyskytují polynomy nízkého stupně. Typicky se jedná o následující objekty: koule, válec, kužel, elipsoid (vše jsou to kvadriky), toroid a poloprostor. Model vychází ze skutečnosti, že těleso v prostoru lze popsat jako množinu bodů, které splňují určité vlastnosti. Ty mohou být u jednoduchých objektů popsány jednou nerovnicí či soustavou nerovnic. Například kouli S (tj. množinu bodů X v prostoru) se středem C a poloměrem r lze popsat jednoduchou nerovnicí ve tvaru:
S={ X | (C – X)2 < r2 }
Jednoduché těleso vytvořené z koule a krychle spojené operací rozdílu (difference). Zdrojový kód této scény bude uveden příště.
2. Množinové operace s tělesy
Popisovat složitý objekt s mnoha nerovnostmi na povrchu pomocí soustavy nerovnic může být velmi obtížné a může vést ke komplikacím při vyhodnocování (vykreslování) scény. Aby k uvedeným problémům nedocházelo, skládají se v CSG reprezentaci složitější objekty z objektů jednoduchých pomocí množinových operací. Množinovými operacemi, které při popisu přicházejí v úvahu, jsou binární nebo n-ární průnik (∩), sjednocení (∪) a rozdíl (-). Často se používá i unární operace doplněk ('), která znamená „převrácení“ tělesa, tedy smyslu bodů ležících uvnitř a vně tělesa se zachováním hranice.
Složité těleso je tedy v CSG reprezentaci popsáno stromem, který obsahuje ve svých listech geometrické, popř. i doplňkové informace o primitivních tělesech. V uzlech tohoto stromu jsou uloženy množinové operace, popřípadě i transformace. Lineárními transformacemi může být popsána změna polohy, popř. změna měřítka části tělesa ležícího v daném podstromu. Kromě lineárních transformací lze použít i obecnější transformace, které potom mohou reprezentovat například různé deformace buď celého tělesa nebo jeho části (zde se však již jedná o poměrně složitou technologii).
Aby při vytváření tělesa nedocházelo k anomáliím, je zapotřebí objekty i množinové operace definovat tak, aby výsledkem operace byla vždy množina s „neostrými“ hranicemi. Zmíněné anomálie by mohly při určitém použití klasických množinových operací způsobit například vznik izolovaných bodů či množiny bodů reprezentujících povrch tělesa, případně nějakou křivku na jeho povrchu, ale ne již objem tělesa. Například průnikem dvou poloprostorů X1∩ X2, které jsou geometricky totožné až na opačnou orientaci, by byla rovina a ne těleso s nenulovým objemem. Povrch vzniklý pomocí CSG musí obalovat konečný objem a musí se jednat o takzvaný 2-manifold. Tuto podmínku lze zajistit částečným upravením vztahů pro množinové operace prováděné nad CSG stromem, konkrétní podoba vztahů vychází ze změny okrajových podmínek.
Binární množinové operace lze s výhodou rozšířit na n-ární operace. To nám v mnoha případech umožní efektivnější reprezentaci CSG stromů s mnoha objekty, nad kterými se provádí shodné operace. Efektivnost v tomto případě spočívá v tom, že výsledný strom má menší hloubku a jeho uložení v paměti může být efektivnější. V případě použití n-árních operací však budou místo binárních stromů použity obecné n-ární stromy.
Jednoduché těleso vytvořené z koule a krychle spojené operací průniku (intersection). Zdrojový kód této scény bude uveden příště.
Vykreslování složitých těles reprezentovaných CSG operacemi nad základními tělesy (primitivy) se nejsnáze provádí metodami zpětného sledování paprsku (raytracing) a vrhání paprsku (raycasting – což je jednodušší verze raytracingu). Výhoda těchto dvou metod v souvislosti s CSG spočívá v tom, že průchodem CSG stromu a jednoduchým vyhodnocováním CSG operací (za pomoci logických operací sjednocení, průniku, doplňku a rozdílu) lze zjistit reálné průsečíky paprsku s modelovaným tělesem.
Při vykreslování lze použít i převodu CSG reprezentace na polygonální či parametrickou reprezentaci, tento převod je však vždy doprovázen různými obtížemi, například vznikem viditelných artefaktů v místech protínání dvou či více základních těles. Převodem se také ztrácí informace o historii vzniku tělesa pomocí základních těles, transformací a CSG operací. V POV-Rayi se tento převod samozřejmě nedělá, protože se vykreslení těles v CSG reprezentaci provádí přímo.
V následujících částech tohoto seriálu si mj. popíšeme reprezentaci těles pomocí implicitních ploch, na kterou se můžeme dívat jako na rozšíření klasické CSG reprezentace, ve které jsou místo CSG operací použity obecnější funkce.
Jednoduché těleso vytvořené z koule a krychle spojené operací spojení (union). Zdrojový kód této scény bude uveden příště.
3. Zápis prostorových scén v jazyce POV-Raye
Po spíše teoretickém úvodu se nyní zaměřme na praktický problém – jakým způsobem se vlastně programu POV-Ray „předloží“ trojrozměrná scéna, která se má vykreslit? Pro POV-Ray je, podobně jako pro mnoho dalších raytracerů, vytvořen samostatný jazyk nazvaný Scene Description Language, v jehož formátu (přesněji řečeno syntaxi) je nutné celou scénu zapsat. V prvních verzích této aplikace se jednalo o jazyk čistě popisný neboli deklarativní, současná varianta jazyka se však již přibližuje běžným programovacím jazykům, protože je v něm možné tvořit proměnné a makra, používat podmínky a smyčky atd.
Nemusíte se však bát, že bez znalosti programování v POV-Rayi nic nevykreslíte. Naopak, pokud nebudete vyžadovat některé pokročilejší věci (přímo v POV-Rayi lze například naprogramovat dynamický částicový systém, ale to je již velmi pokročilá věc), vystačíte si s čistě deklarativní podmnožinou tohoto jazyka – což ostatně platí pro více než 90 % všech scén, jejichž popis lze najít na internetu.
S nadsázkou se dá říci, že i zde platí poněkud upravené známé pravidlo 80/20, tj. pro 80 % veškeré funkcionality je zapotřebí znát a použít pouze 20 % nabízených funkcí.
Samotná syntaxe jazyka pro popis scén v POV-Rayi připomíná syntaxi použitou v mnoha programovacích jazycích C-čkové větve – podobný zápis má například programovací jazyk C, C++, Java, JavaScript, ale třeba i Perl. To je jistě výhodné, protože se jedná o poměrně čitelnou syntaxi, na druhou stranu s ní mohou mít některé programy (například modeláři) problémy. Není tedy vůbec výjimečné se setkat s programem, který sice umí vytvářet scény pro POV-Ray, ale již je neumí zpětně načíst.
I v tomto ohledu tedy POV-Ray připomíná již v první části zmiňovaný TeX. Celá syntaxe jazyka je popsána v přiložené dokumentaci, konkrétně v kapitole Scene Description Language. My si zde nebudeme popis syntaxe uvádět, protože je to poněkud nezáživné. Raději jsem zvolil jiný postup – s prakticky celou syntaxí se budeme postupně seznamovat na demonstračních příkladech, které budou současně vysvětlovat i jednotlivé vlastnosti POV-Raye.
Na tělesa lze nabalit i textury uložené v rastrovém formátu (zde byla textura ryby upravena v grafickém editoru)
4. První demonstrační příklad – ukázka jednoduché prostorové scény
Zdrojový tvar prvního demonstračního příkladu jsme si již ukazovali v úvodní části tohoto seriálu. Dnes se však již podíváme, které konkrétní prvky se ve zdrojovém kódu objevují. Hned na prvních řádcích jsou uvedeny komentáře, které začínají dvojicí znaků //. Tyto komentáře platí do konce řádku. V případě, že je zapotřebí vložit komentáře delší (popř. zakomentovat část scény), lze použít „komentářové závorky“, které začínají dvojicí znaků / a končí dvojicí znaků /. Dvojí typ komentářů má svůj význam, protože lze zakomentovat i část scény, ve které jsou použity jednořádkové komentáře – podobné je to v céčku, C++ atd. Zajímavé však je, že i víceřádkové komentáře mohou být zanořeny, což je poněkud unikátní, zejména při porovnání s výše uvedenými programovacími jazyky.
Dále můžeme ve zdrojovém kódu vidět několik objektů POV-Raye. Ty začínají uvedením jména objektu (v podstatě se jedná o klíčové slovo), za nímž následuje otevírací složená závorka {, je uveden obsah objektu a následuje ukončující složená závorka }. V demonstračním příkladu jsou použity například objekty camera (definice pozorovatele), light_source (definice světelného zdroje), torus (jedno konkrétní těleso ve tvaru anuloidu), texture (textura) či pigment (barevná mapa textury).
Dále můžeme ve scéně vidět způsob zápisu atributů objektů. Ty základní jsou uvedeny přímo za otevírací složenou závorkou svojí hodnotou (pozice světelného zdroje, poloměry anuloidu), další atributy pak klíčovým slovem, za kterým je potom hodnota uvedena. Příkladem pojmenovaných atributů je location (pozice pozorovatele), look_at (bod, na který pozorovatel hledí), color (barva) atd.
Některé atributy mají jako svoji hodnotu vektor; ten je zapsán v úhlových závorkách. Například pozice pozorovatele je uvedena vektorem <0, 20, –15>, změna měřítka textury pak atributem (zde transformací) scale <3.5, 1, 1>. Složitější objekty mohou mít jako svůj atribut hodnotu pole, což je případ barevné mapy – color_map. Syntaxe je jednoduchá – jednotlivé položky jsou zapsány v hranatých závorkách [ a ].
Pro začátek nám budou tyto informace stačit, podívejme se tedy, jak zápis celé scény vypadá:
// ------------------------------------------------------------
// Jednoduchá scéna s uzavřeným objektem, dvojicí světel
// a jednou kamerou (pozorovatelem)
// ------------------------------------------------------------
/* - začátek víceřádkového komentáře
Založeno na souboru původně vytvořeném Danem Farmerem (leden 2002)
rendering lze spustit příkazem: povray +W800 +H600 +B100 +FN +D +Iscena1.pov +Oscena1.png
(pro náhled postačí zadat povray scena1.pov)
*/
// globální nastavení parametrů scény
global_settings {
assumed_gamma 2.2
}
// nastavení kamery (pozorovatele)
camera {
location <0, 20, -15> // pozice kamery
look_at <0, -2, 0> // bod, na který kamera směřuje
}
// první (silnější) světelný zdroj s bílou barvou
light_source {
<-50, 100, -80> // pozice světelného zdroje
color rgb 1 // barva světla (všech tří složek)
}
// druhý (slabší) světelný zdroj
light_source {
<250, 25, -100> // pozice světelného zdroje
color red 0.85 green 0.53 blue 0.10 // barva světla
}
// jediný objekt ve scéně: torus (anuloid)
torus {
7.0, 4.0 // geometrické informace
// (poloměry celého toroidu a "trubky")
// spodní (podkladová) textura se základním vzorkem
texture {
pigment { // definice vzorku textury
bozo // typ vzorku
color_map { // dvě barvy, které se na vzorku střídají
[0.0 0.4 color red 0.36 green 0.20 blue 0.09 color red 0.36 green 0.20 blue 0.09 ]
[0.4 1.01 color red 0.858824 green 0.576471 blue 0.439216 color red 0.858824 green 0.576471 blue 0.439216]
}
scale <4, 0.15, 0.15> // změna měřítka (velikosti) namapovaného vzorku
rotate 45*y
}
}
// horní textura, která přidává jemnější vzorek
texture {
finish { // vlastnosti materiálu
phong 1 // intenzita a
phong_size 100 // velikost odlesků
brilliance 3 // míra změny odlesků s úhlem dopadu světelných paprsků
ambient 0.2 // ambientní složka (pro simulaci všesměrového světla)
diffuse 0.8 // difúzní složka (pro simulaci směrového světla)
}
pigment { // definice vzorku textury
wood // typ vzorku
turbulence 0.025
color_map { // čtyři barvy, které se ve vzorku střídají
[0.00 0.15 color red 0.42 green 0.26 blue 0.15 color red 0.85 green 0.53 blue 0.10 ]
[0.15 0.40 color red 0.85 green 0.53 blue 0.10 color rgbf 1 ]
[0.40 0.80 color rgbf 1 color red 0.85 green 0.53 blue 0.10 ]
[0.80 1.01 color red 0.85 green 0.53 blue 0.10 color red 0.42 green 0.26 blue 0.15 ]
}
scale <3.5, 1, 1> // změna měřítka a natočení vzorku
translate -50*y
rotate 1.5*z
}
}
}
// ------------------------------------------------------------
// finito
// ------------------------------------------------------------
První demonstrační scéna po vykreslení POV-Rayem
5. Základní vyplněná konečná tělesa podporovaná POV-Rayem
V POV-Rayi je možné při tvorbě prostorových scén použít mnoho různých typů těles. My si dnes představíme pět základních těles, z nichž je možné vytvářet i tělesa složitější pomocí výše teoreticky popsaných množinových operací (CSG). Všechna dnes popsaná tělesa mají dvě společné vlastnosti – jejich tvar je popsán pomocí jednoho či několika atributů a jedná se o konečná a uzavřená, tj. je u nich přesně definován jejich vnějšek a vnitřek.
Obě vlastnosti jsou samozřejmě výhodné, protože práce s těmito tělesy je poměrně jednoduchá a názorná. Současně jde i o vyplněná tělesa (solid), ovšem, jak uvidíme v dalších dílech, lze provést převod na tělesa prázdná (hollow), což se projeví při CSG operacích. Mezi základní tělesa patří krychle či kvádr (box), koule (sphere), válec (cylinder), kužel (cone) a anuloid (torus).
Krychle či kvádr, který se pomocí raytracingu vykresluje velmi rychle, je v POV-Rayi specifikován pomocí objektu nazvaného box. Z geometrických informací se zadávají pouze souřadnice libovolných dvou vrcholů ležících na tělesové úhlopříčce, tj. jedná se o dvojice „nejvzdálenějších“ bodů v kvádru. Následuje příklad zápisu krychle ve scéně – střed krychle leží ve středu souřadného systému a délka jejích hran je rovna dvěma:
// krychle
box {
<-1, -1, -1>, // první vrchol na tělesové úhlopříčce
<1, 1, 1> // druhý vrchol na tělesové úhlopříčce
texture {color White}
}
Koule patří mezi nejjednodušší a současně také nejrychleji vykreslovaná tělesa. Z tohoto důvodu se s těmito geometrickými objekty velmi často setkáváme ve scénách vytvářených pomocí raytracingu. Především se jedná o starší modely (scény), které byly počítány na pomalejších počítačích (PC 386, Amiga atd.). Koule je specifikována objektem sphere, především svým středem a poloměrem. Příklad zápisu koule ležící ve středu souřadné soustavy a s poloměrem rovným jedné ve scéně:
// koule
sphere {
<0,0,0>, // souřadnice středu koule
1 // poloměr koule
texture {color White}
}
Poněkud složitějším tělesem než kvádr a koule je válec, který je v POV-Rayi zapisovaný pomocí objektu cylinder. U válce se specifikují souřadnice středů horní i dolní podstavy a samozřejmě také poloměr válce, který by měl být kladný (nenulový). V ukázce uvedené níže jsou souřadnice středů podstav specifikovány pomocí předdefinovaného vektoru x, samozřejmě je však možné použít jakýkoli vektor zapsaný do úhlových závorek:
// válec
cylinder {
x, // střed horní podstavy
-x, // střed dolní podstavy
1.0 // poloměr válce
texture {color White}
}
Kužel (cone) je z pohledu POV-Raye vlastně zobecněním válce, protože je možné pomocí tohoto objektu popsat jak „ostrý“ kužel, tak i kužel komolý. Od válce se kužel liší především tím, že se kromě středů obou podstav udává i jejich poloměr, který může být navzájem odlišný. V případě, že je poloměr jedné podstavy nulový, vykreslí se běžný „ostrý“ kužel, v opačném případě kužel komolý. Válec je možné zapsat také jako kužel, v tomto případě se dvěma shodnými poloměry.
// kužel
cone {
x,1.0, // střed a poloměr horní podstavy
-x,0.0 // střed a poloměr dolní podstavy
texture {color White}
}
Zajímavým tělesem je toroid (anuloid) neboli torus. Toto těleso je možné díky CSG použít například pro tvorbu různých potrubí, zaoblených částí objektů atd. U toroidu se i přes jeho zdánlivě složitý tvar specifikují pouze dva poloměry. Prvním je hlavní (major) poloměr toroidu, který udává velikost kružnice tvořící osu „trubky“. Druhým poloměrem (minor) se specifikuje tloušťka „trubky“. Nulová hodnota hlavního poloměru vede k tvorbě koule, ovšem jde velmi neefektivní a současně i poměrně nebezpečný způsob, neboť plášť takové koule je vlastně „dvojitý“. Způsob vložení toroidu do scény:
// anuloid (toroid)
torus {
1.2, // hlavní poloměr toroidu
0.5 // poloměr "trubky"
texture {color White}
}
6. Druhý demonstrační příklad – vykreslení základních konečných těles
Všech pět základních těles je (spolu se superelipsoidem popsaným v následující části seriálu) použito při tvorbě scény popsané níže uvedeným kódem. Oproti prvnímu demonstračnímu příkladu se zde můžeme setkat s několika odlišnostmi. První odlišnost spočívá v použití příkazu #include, pomocí nějž se před vykreslením načte další externí soubor, jehož (jazykové) objekty mohou být ve scéně použity. Externí soubory se hledají ve stejném adresáři, v jakém se popis scény nachází a dále v adresářích uvedených v konfiguraci. Ve standardním „include“ adresáři se nachází i soubor colors.inc s definicí mnoha odstínů barev, které jsou v příkladu použity (White, Green).
Dále se zde můžeme poprvé setkat s příkazem #declare. Ten slouží k vytvoření pomocných identifikátorů, na které můžeme nahlížet jako na proměnné. Buď se jedná o jednoduché proměnné, což je případ proměnných Radius či Row3, nebo se může jednat o libovolně složité objekty. Ve scéně jsou například všechna tělesa (kromě podkladové plochy) vykreslena jasně zelenou barvou s odlesky. Aby se nemusela v kódu několikrát opakovat celá specifikace zelené textury, je nadeklarován pomocný identifikátor Solid, který je dále u jednotlivých těles použit. Všimněte si, že se klíčové slovo texture používá jak při deklaraci pomocného identifikátoru, tak i při jeho vlastním použití – to je jeden z rozdílů oproti Céčkovému #define.
V předchozí kapitole jsem se zmínil o použití předdefinovaného vektoru x. Ten opravdu existuje, protože POV-Ray před načítáním každé scény deklaruje mimo jiné i následující vektory:
// zabudované vektory v POV-Rayi
#declare x = <1, 0, 0>;
#declare y = <0, 1, 0>;
#declare z = <0, 0, 1>;
#declare t = <0, 0, 0, 1>;
#declare u = <1, 0>;
#declare v = <0, 1>;
Nyní již následuje výpis celé scény:
// ------------------------------------------------------------
// Jednoduchá scéna se základními geometrickými tělesy POV-Raye
// ------------------------------------------------------------
#include "colors.inc"
// deklararace pomocných identifikátorů (lze na ně pohlížet jako na proměnné)
#declare Radius =2.5;
#declare RowSpace=1.1;
#declare ColSpace=1.1;
#declare Dist=-3;
#declare Row3= 2;
#declare Row2=Row3+Radius*RowSpace*2;
#declare Row1=Row2+Radius*RowSpace*2;
#declare Col1= -Radius*ColSpace*3;
#declare Col2= Col1+Radius*ColSpace*2;
#declare Col3= Col2+Radius*ColSpace*2;
#declare Col4= Col3+Radius*ColSpace*2;
// nastavení kamery (pozorovatele)
camera {
location <0, 0, -60> // pozice kamery
direction <0, 0, 10> // směr kamery
look_at <0, 0, 0> // bod, na který kamera směřuje
translate <0,Row2,-110> // posun
}
// světelný zdroj s bílou barvou
light_source {
<1000, 1000, -2000> // pozice světelného zdroje
color White // barva světla
}
// rovina umístěná za objekty (pozadí)
plane {
z, 1.01 // orientace roviny (normálový vektor) a vzdálenost od počátku
pigment { // textura
checker // vzorek
color White // první barva vzorku
color rgb <1,.8,.8> // druhá barva vzorku
}
hollow on
}
// deklarace textury použité u všech objektů
#declare Solid=
texture {
pigment {Green} // barva
finish {phong 1} // odlesky
}
// krychle
box {
<-1, -1, -1>, // první vrchol na tělesové úhlopříčce
<1, 1, 1> // druhý vrchol na tělesové úhlopříčce
texture {Solid}
rotate <-25, 15, 0>
translate <Col1, Row1, Dist>
}
// koule
sphere {
<0,0,0>, // souřadnice středu koule
1 // poloměr koule
texture {Solid}
translate <Col2, Row1, Dist>
}
// válec
cylinder {
x, // střed horní podstavy
-x, // střed dolní podstavy
1.0 // poloměr válce
rotate y*30
texture {Solid}
translate <Col3, Row1, Dist>
}
// anuloid (toroid)
torus {
1.2, // poloměr toroidu
0.5 // poloměr "trubky"
rotate x*40
texture {Solid}
translate <Col4, Row1, Dist>
}
// kužel
cone {
x,1.0, // střed a poloměr horní podstavy
-x,0.0 // střed a poloměr dolní podstavy
rotate y*30
texture {Solid}
translate <Col1, Row2, Dist>
}
// kužel
cone {
x,1.0,
-x,0.5
rotate y*30
texture {Solid}
translate <Col2, Row2, Dist>
}
// kužel
cone {
x,0.5,
-x,1.0
rotate y*30
texture {Solid}
translate <Col3, Row2, Dist>
}
// kužel
cone {
x,0.0,
-x,1.0
rotate y*30
texture {Solid}
translate <Col4, Row2, Dist>
}
// superelipsoid
superellipsoid {
<0.0, 1.0>
rotate y*30
texture {Solid}
translate <Col1, Row3, Dist>
}
// superelipsoid
superellipsoid {
<0.3, 0.7>
rotate y*30
texture {Solid}
translate <Col2, Row3, Dist>
}
// superelipsoid
superellipsoid {
<0.6, 0.4>
rotate y*30
texture {Solid}
translate <Col3, Row3, Dist>
}
// superelipsoid
superellipsoid {
<1.0, 0.2>
rotate y*30
texture {Solid}
translate <Col4, Row3, Dist>
}
// ------------------------------------------------------------
// finito
// ------------------------------------------------------------
Druhá demonstrační scéna po vykreslení POV-Rayem
7. Obsah další části seriálu
Ve čtvrté části seriálu o raytraceru POV-Ray si uvedeme další demonstrační příklady na tvorbu složitějších trojrozměrných těles pomocí množinových operací (CSG). Kromě toho si popíšeme i velmi užitečné těleso nazvané superellipsoid, které lze použít v mnoha případech (krychle se zaoblenými hranami, jehlan se zaoblenými hranami, střecha východního stylu atd.), ve kterých by se jinak muselo použít CSG či modelování pomocí parametrických ploch.
Postupná aplikace CSG