Odvahu. Odvahu do života. Nic víc.
-- František Křižík
Inspirací této práci bezesporu je Křižíkova fontána na pražském výstavišti. Tajným přáním bylo udělat si takovou malou vlastní virtuální fontánu.
Křižíkova fontána byla vybudována u příležitosti jubilejní výstavy v roce 1891 a její současná podoba vychází z rekonstrukce uskutečněné o sto let později. Je bez nadsázky evropským unikátem. Efekt produkce fontány zajišťuje množství vodních okruhů ovládaných padesáti čerpadly a zakončených téměř třemi tisíci trysek. Barevné efekty vytváří 1248 podvodních reflektorů různých barev, které mohou docílit široké škály barevných odstínů. Bazén má rozměry 25x45 metrů a pojme na 1650 kubických metrů vody. K dalším zajímavostem patří, že rozvod vody nerezovým potrubím měří přes dva kilometry. Ozvučení zabezpečuje 55 elektroakustických zářičů. Program fontány je řízen počítačem.
Přilehlé stěny okolních pavilonů byly architektonicky a stavebně pojaty jako amfiteátr pro více než 6 000 diváků, další mohou sledovat program z ochozů na střechách Křižíkových pavilónů.
Tato kapitola je věnována teoretickému pozadí této diplomové práce. Jsou zde popsány způpsoby simulace kapaliny, slévání kapek, vlnění hladiny, zobrazení kapek. Také je zde stručně popsaná práce J. S. Millera zabívající se podobnou tematikou.
Pro popis scénáře byl vyvinut jednoduchý jazyka JA (Jazyk animace).
V této kapitole je tento jazyk podrobně popsán spolu s uvedením příkladů.
Je zde i velmi stručný popis preprocesoru cpp
, který se
požívá k předpracování jazyka.
Výsledkem této diplomové práce je vlastně soustava programů, které z popisu scénáře vytvoří animaci. V této části jsou popsány programy z uživatelksého hlediska formou velmi podobnou unixovým manuálovým stránkám.
V této části jsou popsány rozhraní jednotlivých modulů a stručný náhled do datových struktur.
Zhodnocení dosažených výsledků.
Výsledné obrázky, animace a screenshoty jsou na přiloženém CD. Struktura adresářů je poměrně přehledná, ale přesto je tu tato drobná kapitolka, která ulehčí hledání těchto výsledků.
Názvy těchto kapitol mluví samy za sebe.
V této části je vysvětleno teoretické pozadí diplomové práce. Jsou zde popsány způpsoby simulace kapaliny, slévání kapek, vlnění hladiny, zobrazení kapek. V závěru je krátky výtah z práce J. S. Millera, který se zabýval podobnou tématikou.
Cílem bylo udělat systém, který by jednoduchým způsobem popisoval ``choreografii'' fontány. A tuto ``choreografii'' pak zpracovat až po výslednou animaci.
Zároveň zde byla možnost, že by program mohl být použit při návrhu nových fontán podobného typu. Nešlo by o nějaké testování parametrů fontány, ale spíše o prezentaci.
Původní inspirace si přímo říká o to, aby do prostředků jazyka byly přidány prostředky pro práci s hudbou. Nicméně tomu tak není. Je sice možné synchronizovat scénář s nějakými konkrétními časovými okamžiky, ale již například nelze zahrnout zdroje hudby přímo do scény.
Aby vůbec mohla proběhnout simulace tryskající vody, bylo třeba navrhnout a implementovat popis celé situace (scénář animace).
Pro tento účel byl navrhnut a vyvinut jazyk scénáře JA. Od jazyka scénáře se očekávalo, že bude schopen vyjádřit spojité chování určitých veličin v čase.
Popis scénáře musí zvládnout popis rozmístění trysek, světel a kamery. Nejen jejich rozmístění, ale i změnu jejich vlastností jako je například poloha. U trysky je to dále směr a síla vodního proudu. U světel barva. A tak dále.
Dalším prvkem popisu scénáře jsou tzv. pohlcovací plochy (vodní hladiny). Ty určují místo, kde kapky ``mizí''.
Díky poměrně unifornímu přístupu k těmto objektům je možné, aby se tyto plochy taktéž pohybovaly a měnily velikost a polohu. Nicméně tato vlastnost nevypadá příliš využitelně.
Od popisu scénáře se naopak neočekává, že by definoval objekty scény. Scéna je přidána až při závěrečném raytracingu.
Dá se očekávat, že v animaci se nemusí pohybovat pouze předměty zájmu simulace (tedy trysky, světla, vodní plochy, kamera), ale i jiné blíže nespecifikované. Proto jazykem popisu scénáře můžeme definovat proměnné (typu vektor) a jejich případný pohyb, bez nějakého konkrétního významu v simulaci. Tyto ``propašované'' proměnné budou parametry konkrétního popisu scény.
Původní budovatelský nápad vytvořit i ``intuitivně'' ovládaný grafický editor pro editaci scénáře se ukázal být nad rámec rozsahu diplomové práce.
Největší váha však spočívá na provedení simulace. Zobrazení tryskající kapaliny byl tak trochu krok do neznáma, neboť v době počátku této práce nebylo známo, že by se někdo přímo tryskající vodou zabýval. Blíže viz kapitola teo_jini .
Cílem tedy bylo vymyslet nějaké přístupy a tyto vyzkoušet a porovnat.
Pro simulování kapaliny byl zvolen částicový systém. Jde o to, že celá kapalina je reprezentována mnoha částicemi (v tomto případě jim říkejme ``kapky''). Tyto kapky se v diskrétních krocích pohybují dle ``nějakých'' pravidel.
Pro uchování výsledku simulace byl vyvinut formát buran. Jde o velmi prostý binární formát, ve kterém se ukládají informace spočítané simulací. Poněvadž simulace probíhá v diskrétních krocích, je přirozené, že informace se uchovají po framech (jako by po políčkách filmu). V každém framu je možné uchovávat různé typy informací.
Ty nejzajímavější typy informací jsou: polohy kapek, logovací informace, čas, stav trysek, světel, kamery, pole výchylek poloh hladiny (vlnky) atd.
Je jasné, že spočítat výslednou animaci je časově náročné (o mnoho náročnější než provést samotnou simulaci). Proto se jevilo účelné vytvořit velmi jednoduchý 3d prohlížeč mezivýsledků -- xburan.
Tento prohlížeč pracuje nad mezivýslednou formou buran. Poskytuje pouze základní nadhled na vygenerovanou simulaci. Kupříkladu kapky jsou nahrazeny body, hladina pouhým obdélníkem a podobně.
Prohlížeč umožňuje procházet scénou v prostoru i v čase, zvětšovat detaily, sledovat scénu z pohledu kamery.
Samotný výsledek simulace by nebyl nikterak zajímavý, pokud by nebyl nějakým vhodným způsobem realisticky zobrazen.
Celý systém simulace kapaliny je koncipován tzv. ``offline''. To znamená, že autor zadá scénář a nějakou dobu si počká, než se mu ukáže výsledek. Proto je možné použít rekurzivní sledování paprsku (raytracing), které umožňuje velmi věrné zobrazení, nicméně je časově náročné.
Jednou možností by bylo imlementovat vlastní raytracer. Aby scéna nebyla sestavena pouze z tryskající vody, světel a hladiny, bylo by jistě nutné do tohoto zobrazovače implementovat alespoň základní typy objektů, textur a popřípadě i nějaké urychlovací metody a spoustu dalších vlastností.
Ačkoliv imlementace jednoduchého raytraceru není složitá,
byl zvolen jiný přístup. Pro závěrečné zobrazení byl vybrán již hotový
raytracer povray
.
To mělo několik důvodů. Jednak v povray je implementována velká spousta vlastností. Jejich výčet by byl dlouhý, ale stručně by se dalo říci, že to jsou všechny základní metody rekurzivního sledování paprsku. Implementace v takovém rozsahu by zřemě nebyla jednotlivcem zvládnutelná.
Dále povray je ``živý'' program a to zvláště díky tomu, že je vyvíjen podle Open Source Society modelu. To znamená, že zdrojové texty jsou volně šiřitelné a v podstatě každý, kdo má chuť něco přidat či vylepšit, může ``přispět svou troškou do mlýna''.
Jinak celý systém není však tak závislý na povray.
Pro výstup do jiného zobrazovacího programu
by se dal zařídit drobnou upravou programu job
.
Viz kapitola
prog_job
.
Celý systém je rozložen do mnoha menších utilitek podle unixového hesla: každý program ať dělá pouze málo, ale ať to dělá dobře.
Cesta od scénáře scény k výsledné animaci vypadá zhruba takto: Nejprve je
jazyk popisu scény (JA) převeden do ``nižšího jazyku'' A.
Následuje zpracování simulace (jmv
) jejímž výsledkem je
soubor formátu buran. Ten je možné prohlížet xburan
'em
nebo job
'em převést do popisu povraye.
Pro paralelní spočítání výsledné animace poslouží soustava scriptů
dish-*
. Jednotlivé obrázky se pak dají dohromady nějakým
standardním konverzním programem (zde se osvěčil mpeg_encode
).
Blíže k popisu utilitek viz kapitola uziv .
Pro reprezentaci kapaliny byl použit částicový systém. Jde vlastně o to, že kapalina je nahrazena jakýmisi makromolekulami. V následujícím textu jim budeme říkat familiárně kapky.
U každé kapky se uchovávají tyto informace: poloha, rychlost a hmotnost.
Makromolekuly se pak pohybují v diskrétních krocích. Pravidla, podle nichž se určí následující stav celého systému, jsou dána použitým matematickým modelem.
Částicový systém se jeví přirozený pro reprezentaci tryskající kapaliny, i když jsou možné i jiné přístupy.
Každá kapka v průběhu simulace projde jistým vývojem. Nejprve je vytvořena v místě ústí trysky.
Poté je s ní pohybováno na základě použitého matematického modelu. Při použití některých modelů se kapka může rozpadnout na více kapek, či více kapek se může slít v jednu větší.
Pokud kapka narazí na vodní hladinu, její ``život'' tímto končí.
Gravitační model je prostým vyjádřením faktu, že celá simulace probíhá v gravitačním poli. K určení polohy a rychlosti je použita Eulerova metoda:
kde
s je nová poloha,
v je nová rychlost,
s0 je původní poloha,
v0 je původní rychlost,
g je tíhové zrychlení (0, -9.8, 0)
,
t časový krok.
V každém kroku simulace se tyto vztahy aplikují na každou kapku.
V předcházejícím modelu se nebralo do úvahy žádné vzájemné působení ani vzájemná poloha kapek. Kapalina je za normálních okolností těžko stlačitelná.
Tento model se to snaží řešit velice jednoduchým způsobem.
Řekněme, že kapky vyžadují určitý odstup. Pokud jsou
blíže než daný odstup, snaží se druhou kapku ``odstrčit''
do příslušné vzdálenosti. Získáme tedy ``odstrčenou'' polohu kapky.
Pokud je konfliktních kapek více, výslednou polohu kapky dostaneme
aritmetickým průměrem všech ``odstrčených'' poloh.
kde
Dk,od je minimální distance(odstup) kapek k a od ,
sod je nová poloha kapky,
s0 je původní poloha kapky,
k jsou kapky které jsou blíže než Dk,od
n je jejich počet,
sk je původní poloha kapky,
norm() normalizace vektoru na jednotkový vektor.
Distance Di,j se určí dle vztahu:
kde
ri, rj jsou poloměry kapek i a j.
K je konstanta určená experimentálně a dá se zadat
jako parametr scénáře. Dobré výsledky jsou s hodnotami něco pod
1. Vyjadřuje vlastně, jak moc dovolíme kapkám se překrývat.
Předcházející přístup má několik modifikací a pokusných vylepšení. Jedním z nich je model silového odstrkování.
Myšlenka je taková, že nezměníme přímo polohu kapky, ale udělíme jí zrychlení, které ji ``odstrčí'' do příslušných mezí.
Jelikož simulace je dosti hrubá, nevyplatí se fyzikální molekulový přístup, neboť kapky se mohou celkem ``beztrestně'' dostat do velké blízkosti, kam by se ve spojitém případě nikdy nedostaly, protože odpudivá síla blízkých molekul je velmi velká. Pokud se kapky dostanou skokem takto blízko, jsou pak v souladu s fyzikálními zákony ``katapultovány'' velkou silou od sebe.
Proto bylo vhodné použít nějaký umírněnější model než fyzikální. Tento vychází z modelu hrubého odstrkování kapek. Místo odsunutí kapky na vzdálenější místo je kapce uděleno zrychlení, které by ji na toto místo dostalo z klidu za časovou délku kroku simulace.
Z uděleného zrychlení se v následujícím kroku spočítá nová poloha a nová rychlost.
Zrychlení udělené kapce je tedy:
kde
aod je udělené zrychlení,
sod je ``odstrčená'' poloha dle modelu hrubého odstrkování,
Δtod je časová délka kroku.
Dalším přístupem poněkud z jiné strany je dynamické zjemňování počtu částic.
Zde je použito velmi jednoduché pravidlo: pokud dosti daleko od kapky není žádná jiná kapka, rozděl ji na několik menších.
Toto jednoduché pravidlo má několik nejasných míst. Jedno z nich je ``dosti daleko''. Tedy jak určit vzálenost, kdy je kapka ``vhodná'' pro rozdělení. V našem případě bylo použito toto pravidlo: kapka se rozdělí, pokud žádná z ostatních kapek není blíže než volitelná konstanta. Tato konstanta je řádově centimetry.
Další problém je, jakým způsobem kapku rozdělit. Je zde opět mnoho možností.
Jednou z nich je kapku nahradit čtyřmi kapkami stejného poloměru, jejichž středy jsou ve vrcholech pravidelného čtyřstěnu. Základna je ve směru rychlosti kapky. Poloměry kapek jsou voleny tak, aby souhrnný objem kapek byl zhruba stejný jako objem původní kapky. Tj: r= &sqrt;(4)R, kde r je nový poloměr kapky, R je poloměr původní kapky.
Polohy středů jsou:
kde
dist je vzdálenost středů nových kapek.
Vztažná soustava je volena tak, aby osa x splývala s vektorem rychlosti původní kapky.
dist= vnor (r/2). Kde vnor je konstanta, vyjadřující jak moc chceme, aby se nové kapky překrývaly. Pro hodnotu vnor=1 se kapky budou dotýkat. Pro hodnoty nižší se budou kapky prolínat.
Další možnost dělení kapky je motivována tím, aby tvar kapek po dělení alespoň vzdáleně přípomínal letící kapku. Vzniknou čtyři větší kapky s poloměrem r=(1/2)(&sqrt;(3) - 1) 3 R) a dvě menší kapky s poloměry r4=(1/6)(&sqrt;(3) + 1)r a r6=(1/2). Středy budou mít následující souřadnice:
Malé kapky pak:
Proč byly polměry a polohy středů zvoleny takto, je snad celkem zřejmé když se podíváme na obrázek č. 3.
Také je vhodné zajistit, aby příliš malé kapky nebyly dále děleny. Přičemž ``příliš malé'' bylo zvoleno řádově desetiny milimetru. I když obavy z ``přemnožení'' kapek jsou celkem neopodstatněné, protože kapky vzniklé touto metodou se drží celkem pospolu.
Kapky vznikají v trysce. Generují se na počátku každého kroku simulace.
Tryska v určitém časovém okamžiku je zadaná těmito parametry:
Poloha s,
směr p (normalizovaný),
rychlost vytékání kapaliny na počátku kroku v0,
rychlost vytékání kapaliny na konci kroku vT,
poloměr R,
rozptyl ro,
tření trysky.
Mimo parametry trysky záleží generování kapek na hustotě kapek ρ.
Navíc si označme průřez trysky P (= π r2). A pro přehlednost Δv=vT - v0.
Objem kapaliny, která proteče v časovém intervalu <0,t> tryskou: Rychlost kapaliny byla lineárně interpolovaná mezi krajními hodnotami tedy v(t)=v0 + Δv (t/T).
Pozn: správně bychom měli počítat s S(t), ale přepokládejme, že se průměr trysky nemění.
Speciálně pro t = T:
Počet vygenerovaných kapek, pak bude
Poloha kapky je zvolena přirozeným způsobem: náhodný bod z průměru trysky.
Rychlost kapky
kde ro rozptyl trysky, randk() je náhodný vektor z jednotkové koule, vs je střední rychlost kapaliny ve vzdálenost r od osy trysky.
Závislost střední rychlosti na vzdálenosti od osy trysky je:
kde v je střední rychlost v ose trysky. R je poloměr průřezu trysky.
Každé kapce přiřadíme čas, po který se od počátku kroku ``jako'' pohybovala. Je třeba vygenerovat náhodný čas t (tk v intervalu <0,T> s distribuční funkcí V(t). (funkce randf() vrací náhodné číslo <0, 1> s normální distribucí).
Pro tento čas musí platit:
a dosazením a zkrácením S:
vyjádříme t:
Toto je čas, po který se právě vygenerovaná kapka bude pohybovat jaksi mimo simulaci (za použití pouze gravitačního modelu).
Kapky zanikají, pokud dopadnou na tzv. pohlcovací plochu. Přičemž kapka ``dopadne'' právě když spojnice polohy kapky s polohou v předcházejícím kroku simulace se s pohlcovací plochou protíná.
Kapka pak bez jakéhokoli následku pro okolí zmizí.
Po dopadu vody na vodní hladinu se tvoří vlny. To lze reprezentovat tak, že každá kapka vytvoří zdroj vlnění, který pak přispívá k výchylce v každém bodě hladiny.
Výchylka v konkrétním bodě hladiny pak je:
kde z jsou všechny zdroje,
t je současný čas,
tz je čas vzniku zdroje z,
dz je vzdálenost od zdroje z,
yz je amplituda zdroje.
f frekvence vlnění.
λ vlnová délka,
u vzdálenostní útlumová konstanta
(kolikanásobně se zmenší amplituda na 1m),
v časová útlumová konstanta
(kolikanásobně se zmenší amplituda za 1s),
δ(dz,tz)
je 0 pokud dz < (λ tz/p)
1 jinak.
Tato rovnice je pouhým optickým přiblížením skutečnosti a nedělá si právo na nějakou hlubší fyzikální opodstatněnost.
Konstanty byly určeny odhadem a pozorováním skutečné hladiny.
Jednoduchou úpravou tohoto výrazu můžeme separovat od sebe složku závislou na čase a složku závislou na zdroji. V jednom místě hladiny si stačí pamatovat dvě čísla (``sinovou'' a ``cosinovou'' složku). Přidání dalšího zdroje pouze tyto složky upraví. Nemusíme tedy v každém časovém okamžiku procházet všechny zdoje vlnění.
kde
αz = 2 π t f je závislé pouze na čase t,
βz = 2 π ( (dz/λ) + tz f) je závislé pouze na zdroji z.
Pro pevně zvolený bod vodní plochy pak je suma konstantou, což výrazně urychlí počítání výchylky v tomto bodě.
Nové zdroje pouze přispějí každému bodu do jeho ``sinové'' a ``cosinové'' složky. Je třeba však mít na paměti, že vlnění se šíří od zdoje konečnou rychlostí, a proto není možné započítat tyto složky okamžitě při vzniku vlnění pro všechny body plochy, ale započítávat je tak, jak se vlnění postupně šíří.
Toto je další vlastnost (podobně jako vlnky na vodní hladině), která se samotným zobrazením tryskající kapaliny nemá mnoho společného a byla přidaná pouze pro ``přirozenější'' vzezření výsledku.
U skutečné fontány (pokud je vyšší vlhkost vzduchu) dochází k vytvoření vodní mlhy. V našem případě je mlha reprezentována následovně.
Celý prostor je rozdělen na malé ``buňky'' (krychličky o velikosti řádově mm3). Každá buňka má určitou vlhkost. Pokud buňkou proletí kapka, její vlhkost se zvýší na maximum. Pak tato vlhkost s postupem času klesá.
Díky jednoduchosti implementace byl přidán i vliv větru. Jde vlastně pouze o to, že vlhkost se distribuuje do okolních buněk v poměrech daných rychlostí a směrem větru.
Na pověstné prohledávání slepých uliček byla tato práce poměrně bohatá. Někdy však ono ``tudy cesta nevede, přátelé'' bylo vyřčeno příliš brzy a později se ukázalo, že navržený postup není zas až tak nepoužitelný.
Nejprve se zdálo, že bude výhodné použít molekulový fyzikální model. Molekuly na sebe působí přitažlivou silou klesající se čtvrtou mocninou vzdálenosti a odpudivou silou klesající s šestou mocninou vzdálenosti.
Vynořily se však následující problémy. Simulace vykazovala vysokou míru numerické nestability. Zvláště v situacích, kdy mělo dojít ke kolizím kapek.
Ač byl časový krok simulace dosti malý, molekuly, které se ``chystaly'' do sebe narazit, se dostaly ``skokem'' do míst, kde byla odpudivá síla velmi velká. V následujícím kroku pak byly odmrštěny doslova první kosmickou rychlostí.
To se dalo řešit dostatečným zkrácením délky časového kroku. Čas simulace se pak pochopitelně prodloužil.
Tento model (bez gravitačního modelu) byl testován na vzorku zhruba 100 částic, původně rozmístěných v pravidelné čtvercové mřížce. Výsledkem byly částice zhruba uskupené do tvaru koule Částice vykonávaly chaoticky pohyb. Výsledek byl jako vystřižený z učebnice o Brownově pohybu (ovšem bez pylových zrnek).
Vypadalo to tedy, že oněch 100 částic je na reprezentaci kapky přiměřené množství, při řádově menším počtu by kapka působila poněkud neklidným dojmem.
Vzhledem k tomu, že pro simulaci tryskající vody fontány by bylo třeba řádově tisíce takovýchto ``kapek'', ukázal se tento přístup jako neúnosný. A to nejen paměťově, ale i časově (díky velkému počtu kroků způsobených numerickou nestabilitou).
Poměrně dlouhou dobu jsem se zabýval vývojem jazyka pro popis scénáře animace, který se nakonec ukázal jako velmi nevhodný.
Tento jazyk ovládal práci s objekty, garbage collector, mnohonásobnou dědičnost. Měl také upravenou syntaxi pro práci s časem.
Možná právě tyto vlastnosti způsobily, že jazyk se stával čím dál více složitý a čím dál méně přehledný a pochopitelný. Případného nového uživatele by zřejmě stálo dosti úsilí tento jazyk si osvojit.
Proto byl jazyk popisu scénáře nahrazen prostším, vcelku jasným jazykem JA.
Motto: Přechod je možný pouze jedním směrem -- programátorský žargon.
Na začátku byly utilitky diplomové práce vyvíjeny na MSDOSu. Jelikož utilitky nevyužívaly žádnou knihovnu specifickou pro tento systém, pokusil jsem se o přenos na platformu Linux. Když jsem zjistil, jak pohodlné pro programátora je na první pohled nehostinné prostředí Unixů, už jsem u tohoto platfomy zůstal a na MSDOS jsem zcela zanevřel.
Zdá se, že toto téma je v počítačové grafice poměrně opomíjené. Teprve na konferenci WSCG99 v Plzni byl referován příspěvek Jamese Millera ``Modelling Liquids using Particle Systems and Implicit Surfaces'' (Modelování kapalin s použitím částicového systému a implicitních ploch).
Pro modelování kapaliny byl použit částicový systém. Jako matematický model byl použit jednoduchý gravitační model v kombinaci s molekulovým silovým modelem. Jelikož tento model dával očekávané výsledky, nebyl již nijak dále vylepšován.
Hlavní důraz je v práci kladen na zobrazení kapaliny. Pro výsledný tvar kapaliny jsou použity implicitní plochy.
Implicitní funkce ma tvar:
kde
δ(R,di)
je 1 pokud di menší než R a 0 jinak.
di je vzdálenost od částice i ``vážená'' jeho hmotností.
Implicitní plocha daná touto funkcí, je pak množina bodů splňující f(s)=c.
Je vidět, že například jedna izolovaná částice vytvoří kouli.
Blízké částice (tedy ty, které přispívají do jedné plochy) jsou spojeny do tzv. shluků. Je snahou udržet objem těchto shluků alespoň přibližně stejný, jaký by byl, kdybychom sečetli objemy všech izolovaných částic.
Tento požadavek je splňován modifikací konstanty c. Prostor okolo shluku se adaptivně rozdělí na malé krychličky, které se rozdělí na tři skupiny:uvnitř kapaliny, vně a na hranici. Pokud je objem příliš malý (či příliš nepřesný), modifikuje se konstanta c a pokračuje se v dělení krychliček. Takovýmto způsobem lze dosáhnout předem stanovené přesnosti zachování objemu.
Systém taktéž umožňuje modelování více druhu kapalin. Kolize povrchů kapalin různých druhů je vyřešena ``vyvážením'' obou ploch kapalin.
Upravená implicitní funkce pak má tvar (fA, fB jsou původní implicitní funkce shluku A a B, FA je upravená implicitní funkce shluku A):
FA(z)=fA(z) v případě, že fB(z) <c
FA(z)= fA(z) + c - fB(z) jinak.
Dalším problémem, kterým se J. Miller zabýval, je kontakt kapky s pevnou podložkou. Tedy jak upravit plochu částice, který by se měl dotýkat podložky.
Částice, jejíž plocha má průnik s podložkou(a), je nejprve zvětšena tak, aby byl zachován objem(b). Poté je plocha částice dvakrát zkosena. Nejprve ve směru kolmém k ploše(c) a podruhé ve směru tečném(d). Velikosti deformace jsou dány příslušnými složkami gravitace (tedy složkou kolmou a tečnou k podložce).
Práce J. S. Millera předkládá tyto nové přístupy k modelování kapaliny: slévání a štěpení částic, interakce různých druhů kapalin, jednoduchá pravidla pro zachování objemu kapaliny a reakci s podložkou. Výsledný tvar kapaliny je dán implicitními plochami.
Počítání tvarů implicitních ploch (s vyvažováním objemu) je časově náročné a proto tento přístup lze použít pouze pro relativně malé soubory částic.
Pro popis scénáře byl vyvinut jednoduchý jazyka JA (Jazyk animace).
V této kapitole je tento jazyk podrobně popsán spolu s uvedením příkladů.
Je zde i velmi stručný popis preprocesoru cpp
, který se
používá k předpracování jazyka.
Popis scénáře je realizován jazykem A. Jazyk A umožňuje popis objektů scény v závislosti na čase. Objekty scény jsou trysky (poloha, směr, rychlost proudu, poloměr průřezu, rozptyl), světla (poloha, směr, barva, úhel kužele), pohlcovací plocha (poloha, rozměry), kamera (poloha, look_at, up), externí proměnné scény (vektor).
Chování např. trysky je zadáno (volně řečeno) po časových blocích. U každého bloku je určen počátek, délka trvání a co se v průběhu bloku děje. Tento děj může být popsán v podstatě libovolnou (rozumnou) funkcí. Tato funkce bude mít jako proměnnou čas. Při následné simulaci pak bude pro každý okamžik tato funkce vyhodnocena.
Omezením jazyka je, že počátky bloků musí být ve scénáři vzestupně uspořádány. Toto je velmi výhodné pro simulační program, neboť si nemusí udržovat v paměti celý scénář, ale pouze aktuální část. Nicméně pro autora tohoto scénáře je to naopak velmi nevýhodné, zvláště při ladění pohybů několika nezávislých objektů. Například pokud máme celý scénář fontány již hotový, jen bychom rádi upravili pohyb kamery.
Proto zde vznikl velmi prostý jednoduchý ``přeuspořádávač'', jazyk JA. Ten umožňuje nesekvenční popis scény a jeho úkolem je pouze tyto bloky uspořádat. Výstupem je pak korektní kód jazyka A.
Pro větší pohodlí autora scénáře přibyla ještě preprocesová fáze.
Díky preprocesoru cpp
je možné rozdělit nezávislé části
scénaře do více souborů. S tím se ``svezly'' i všechny ostatní
vymoženosti cpp
jako jsou definování maker, podmíněný překlad atd.
Jde o zásobníkový jazyk a může velmi vzdáleně připomínat Postscript. Všechny příkazy jsou jako by v obrácené polské notaci (postfixové formě). To znamená, že nejprve je výčet argumentů a až po nich následuje funkce (operátor).
fce : argumenty fce_id ; argumenty : seznam | fce | proměnná | číslo | vektor | řetězec ; seznam : '[' příkaz ']' ;
číslo
double
vektor
ve tvaru (x y z)
např.: (1.2 0 -15)
.
proměnná
$p, $q, $t, $e, $1, $2, ... $9 vestavěné proměnné, bližší popis viz níže.
řetězec
něco v uvozovkách např: "fce2"
.
Jak je vidět, syntaxe je opravdu jednoduchá.
Číslo, vektor, řetězec anebo proměnná uloží svou hodnotu na zásobník,
fceN
vezme posledních N položek zásobníku a výsledek opět uloží
na zásobník. Mnemotechnika názvu funkcí je taková, že poslední znak názvu
vyjadřuje árnost funkce.
add2, sub2
sčítání, odčítání. Příklad: 1 3 add2
.
smul2, div2
násobení, dělení vektoru skalárem.
Příklad (1 2 3) 4 div2
.
norm1, abs1
normalizace, velikost vektoru.
(1 0 1) (0 1 0) add1 2 div2 norm1 abs1
sečte (1 0 1)+(0 1 0)
, výsledek vydělí dvěma, to celé znormalizuje
a velikost tohoto vektoru bude výsledek.
číslo je chápáno jako vektor s ostatními složkami nulovými nebo nedefinovanými.
Takže 1 (0 1 1) add1
má stejný význam jako (1 0 0) (0 1 1) add2
.
a b t umer3
vážený průměr bodu a
a b
tj. t * b + (1 - t ) * a
a pa d pd t spline5
bod určený řídícími body a
a b
vektory pa
a pd
v čase t
.
a pa d pd t vspline5
tečna v tomto bodě.
[ tělo_fce ] "mojefceN" def2
definuje N-ární funkci mojefceN
.
K argumentům funkce lze přistupovat pomocí proměnných $0, $1, $2, ... $9
.
Proměnné se číslují podle hloubky v zásobníku.
[ $0 1 add2 ] "divny_inc1" def2
definuje funkci my_inc1
, která přičte k argumentu 1.
5 my_inc1
vrátí tedy 6.
[ $2 $1 add2 $0 sub3 ] "divny_prumer3" def2
definuje funkci my_prumer3
, který sečte dva vektory a podělí číslem.
Tedy:
(6 3 3) (6 0 0) 3 divny_prumer3
vrátí (4 1 1)
.
"id" p0 p1 r v ro vl try7
id
název trysky,
p0
pozice trysky,
p1
směr,
r
poloměr,
v
rychlost vytékání,
vl
vlastnost trysky (pro budoucí využití)
"id" p0 p1 rgb rad sve5
id
název,
p0
pozice,
p1
směr,
rgb
barva světla,
rad
kužel.
"id" eye lookat up kam3
id
název,
eye
pozice kamery,
lookat
kam kouká,
up
kde je ``nahoře''.
"id" vec "dfn2"
umožňuje měnit nějaký parametr scény.
výhodné je přidefinovat si stručnější funkci:
[ "hlavnitryska" (0 0 0) (0 1 0) .01 $0 0.1 0 try7 ] "hlavnitryska1" def2
TT čas1
posune čas na TT
, čas se musí posouvat pouze vpřed.
[ tělo intervalu ] LEN int2
tělo intervalu se provede vždy, když se bude chtít zjistit
vzhled scény v časovém intervalu <TT, TT + LEN)
. S výhodou lze použít
vestavěné proměnné $t
, $p
, $q
.
TT end1
čas konce.
$t
čas od začátku intervalu;
$e
délka intervalu
$p
normalizovaný čas ( $t / $e )
$q
( 1 - $p )
kncm1
počet kapek na centimetr krychlový.
Zde je jednoduchý příklad jazyka A. Definuje scénu s jednou kamerou a jednou tryskou.
Tryska začíná tryskat. V čase 0.5 chvíli tryská stejně, v čase 1.5 proud začne zesilovat a v čase 4.5 dosáhne vrcholu. Pak tryska přestane tryskat.
Kamera nejprve trysku sleduje z jednoho místa a v čase 1.0 ji začne objíždět.
; tryska "T" ; ; id p0 p1 r v ro vl [ "T" (0 0 0) (0 1 0) 0.01 $0 0.01 0 try7 ] "T1" def2 ; camera "ocko" ; id eye look up [ "ocko" $1 $0 (0 1 0) kam4 ] "ocko2" def2 ;začneme časem 0.0 0.0 cas1 ; tryska T začíná tryskat ; na začátku tryská rychlostí 0 na konci rychlostí 1 ; interval končí v čase .3 [ $p T1 ] .5 int2 ;kamera "ocko" to z povzdálí sleduje [ (-1 0 0) (0 0 0) ocko2 ] 1 int2 ;posuň čas 0.5 cas1 ;tryska chvilku tryská stejně a to rychlostí 1 [ 1 T1 ] 1 int2 ;kamera objíždí trysku, ale stále sleduje T ; přesune se z místa (-1 0 0) na (0 0 1) za 3s 1.0 cas2 [ ; nejprve spočítáme polohu kamery (-1 0 0) (0 0 1) (0 0 1) (1 0 0) $p spline5 ;kouká se stále na T (0 0 0) ocko2 ] 4 int2 ;další bod scénáře: tryska zesílí z 1 na 5 za 3 sec 1.5 cas1 [ 1 5 $p umera3 T ] 3 int2 4.5 cas1 ;a pak rychle přestane tryskat úplně [ $q 5 mul2 T1 ] 0.1 int2 ;v čase 5s již skončíme 5.0 end2
Jak bylo řečeno výše, omezením jazyka A je, že počátky bloků musí být v souboru uspořádany vzestupně.
Proto byl navrhnut jazyk JA, který je jakýmsi přerovnávačem bloků jazyka A.
Základní stavební jednotkou je časový blok (dále jen blok). Bloky je možné vnořovat. Blok má počátek a délku trvání.
t + dt TĚLO_BLOKU
/
Blok má počátek v čase t od počátku nadbloku a délka jeho trvání je dt. Pokud není uvedené t, blok začíná v čase, kdy skončil předcházející blok, pokud není uvedeno dt, má blok délku 0 .
TĚLO_BLOKU
může mít jeden z těchto dvou tvarů:
{ seznam_podbloků }
[ fce_jazyka_a ]
Přerovnávač spočítá počátky všech atomických bloků, setřídí je.
Mimo bloky lze v přerovnáváči mít úseky, které se beze změn vloží do výsledného .a souboru. To je výhodné pro definice a deklarace objektů scény. Zkrátka pro ``věci'' globální, nezávislé na čase.
Zkopírovano bude to co bude uzavřeno mezi %{
a `%}
.
(podobně jako ve flexu či bisonu).
Příklad:
%{ ; kód v A, který bude beze změn zkopírován do .a %}
příklad z popisu jazyka A bychom mohli přepsat takto:
%{ ; toto bude beze změn zkopírováno do .a ; pro pohodlnější použití si přidefinuji funkci T1, které bude ; stačit jak rychle má tryska tryskat ; id p0 p1 r v ro vl [ "T" (0 0 0) (0 1 0) 0.01 $0 0.01 0 try7 ] "T1" def2 ; camera "ocko" ; id eye look up [ "ocko" $1 $0 (0 1 0) kam4 ] "K2" def2 %} ;nejprve trysku { ; tryska T začíná tryskat ; na začátku tryská rychlostí 0 na končí rychlostí 1 ; interval konci v čase .3 + 0.5 [ $p T1 ] ;tryska chvilku tryská stejně a to rychlostí 1 + 1.0 [ 1 T1 ] ;další bod scénáře: tryska zesílí z 1 na 5 za 3 sec + 3.0 [ 1 5 $p umera3 T ] ;a pak rychle přestane tryskat úplně + 0.1 [ $q 5 mul2 T1 ] } ; konec trysky T1 ; a nyní kamera { ;kamera K to z povzdálí sleduje + 1 [ (-1 0 0) (0 0 0) K2 ] ;kamera objíždí trysku, ale stále sleduje T ; přesune se z místa (-1 0 0) na (0 0 1) za 3s + 4 [ ; nejprve spočítáme polohu kamery (-1 0 0) (0 0 1) (0 0 1) (1 0 0) $p spline5 ;kouká se stále na T (0 0 0) K2 ] } ; konec se vygeneruje automaticky ; 5.0 end2
Oč přehlednější, že?
cpp
Preprocesor cpp
byl použit z několika důvodů.
Hlavním důvodem je možnost vkládat soubory. Tím je umožněno rozdělit scénář na nezávislé části. Díky této modularitě jde lehce psát více různých scénářů pro stejnou fontánu, různé ``střihy'' téhož scénáře, nebo např. různá osvětlení téže ``choreografie''.
Dalším důvodem je možnost vytváření maker. To by se dalo využít například při spojení s hudbou tak, že bychom si ve zvláštním souboru definovali počátky zajímavých míst. Pro různé scénáře na stejnou hudbu bychom si pak nemuseli pamatovat čísla.
V neposlední řadě je to podmíněný překlad. To je vhodné například při ladění pouze částí scénáře. Zbytek scénáře může být dočasně vynechán.
Mimo jiné to byla i ``konstruktivní lenost'', neboli: ``Proč něco implementovat po tisícáté prvé, když je to již tisíckrát napsáno a lépe.''
Jazyk makroprocesoru zajisté ovládá každý programátor
v jazyce C
či C++
. Nicméně zajisté neuškodí stručný přehled.
Makroprocesor projíždí vstup a v podstatě jej kopíruje (trochu upravený) na výstup. Úprava spočívá v expadnování maker.
Chod makroprocesoru se dá řídit pomocí tzv. direktiv. Direktiva začíná vždy na novém řádku znakem `#'.
Popis jednotlivých direktiv
#define MAKRO HODNOTA_MAKRA
po tomto každý výskyt slova MAKRO
bude nahrazen řetězcem
HODNOTA_MAKRA
. Například pokud definujeme
#define pes nejlepší přítel člověka
pak text
pes štěkal na pestrý koberec.
bude vyexpandováno následovně:
nejlepší přítel člověka štěkal na pestrý koberec.
Všiměte si, že pes
ve slově pestrý
nebylo expandováno.
#define pes(x) nejlepší přítel x
definování makra s parametrem. Text
pes(člověka) není pes(kočky).
bude vyexpandováno na
nejlepší přítel člověka není nejlepší přítel kočky.
#include "soubor"
na tomto místě bude vložen soubor soubor
. Tento soubor taktéž projde
makroprocesorem.
#ifdef PROMĚNNÁ
#endif
Pokud je deklarované makro PROMĚNNÁ
, provede se makroproces
i na blok končící endif
. V opačném případě bude tento blok vynechán.
Tyto direktivy lze vnořovat.
Makro procesor cpp
má daleko více vlastností.
Pro náš účel tento popis však stačí. Zájemci nechť se obrátí
na info cpp
, kde je velmi podrobný popis.
V této kapitole jsou popsány utility potřebné k vytvoření animace ze scénáře scény.
Než se ze scénaře v jazyku JA stane výsledná animace, musí projít poměrně dlouhou cestou. Následuje velmi stručný přehled této cesty.
Popis scénáře animace. Jeho výhodou je, že události ve scénáři nemusí
být popsány v tom pořadí, jak jdou ve skutečnosti za sebou. Další
výhodou je možnost použití preprocesoru jazyka C
, který umožní
rozložit scénář na několik nezávislých částí (např. popis poloh trysek,
popis choreografie fontány, pohyb kamer). Utilitou ja
se program
převede do jazyka A.
Toto je rovněž jazyk pro popis scénáře, jenže na nižší úrovni. Počátky událostí musí být ve scénáři tak, jak jdou časově za sebou. To je výhodné pro následující simulační část, která nemusí uchovávat v paměti celý scénář, ale stačí jí aktuální kousek.
jmv
Hlavní hybný program. Podle nastavených parametrů provede simulaci. Výstup je ve formátu buran.
Jde o binární formát pro ukládání informací o animaci, která je již rozdělena na jednotlivé framy. V každém framu jsou pak informace o poloze kapek, vlnění hladiny či stavu světel nebo poloze některých pohyblivých předmětů. Tedy informace o věcech, které se ve scéně mění.
jj
Všechny tyto výše zmíněné utilitky zastřešuje jj
. Nedělá nic jiného,
než že je ve správném pořadí spustí. Takže přímo z .ja
souboru vznikne
.bur
.
xburan
Jednoduchý prohlížeč formátu buran. Jak název napovídá program je pro Xwindows.
job
Pokud máme spočítanou animaci, je třeba ji nějak zobrazit. Tento program z formátu buran vezme konkrétní frame a vytvoří popis scény ve formátu povray'e. Tedy pouze jejích proměnlivých částí, statická část scény musí být připravena zvlášť.
dish
Pro urychlení počítání výsledné animace byla vytvořena soustava scriptů
dish-*
. Umožňuje současné počítání jedné animace na více
počítačích. Nejde o žádný hlubokomyslný distribuovaný systém.
Každý počítač vždy spočítá jeden obrázek a poté požádá o další.
Obrázek č. 7 znázorňuje cestu cestu, kterou projde scenář
a.ja
než se zně jstane výsledná animace.
a.jap
-- scénář prošel preprocesorem cpp
.
a.a
-- bloky jsou přerovnány tak, aby jejich začátky šly vzestupně
za sebou.
a.bur
-- simulace je provedena, jsou známy polohy všech kapek,
kamer, trysek atd. v každém framu,
a-vl.bur
z informací o kapkách dopadnuvších na hladinu jsou
spočítany výchylky hladiny,
a001.d.pov
-- defince proměnných scény ve formátu povray,
scéna.pov
-- popis statické části scény,
a001.t.pov
-- popis kapek, kamer a světel ve formátu povray.
a001.v.pov
-- popis vodní hladiny.
a001.t.tga
-- bump textura vodní hladiny.
a001.o.tga
-- výsledný obrazek,
a.mpg
-- celá animace.
Čárkovaným obdélníkem je naznačeno pokrytí zastřešující utilitkou
jj
resp. soustavou scriptu dish-*
.
Tento program řetězí za sebe vyvolání programů cpp
, jja
a jmv
.
Usnadní tedy průchod od popisu scény až ke spočítání každého
jeho framu (ne raytracing).
jj [-h] [-pa] -oOutput.a input.ja [ -- jmv-options ]
Programem jj
se nejprve se input.ja
provede
přezpracování preprocesorem jazyka C -- cpp
(ve skutečnosti se volá gcc -E -xc
). Díky tomu může být scénář
rozložen na několik nezávislých částí. Vytvořený soubor se jmenuje
Input.ja.p
. Poté se spustí jja
,
který vytvoří uspořádanou podobu scénáře. Název bude Output.a
.
Nakonec přijde na řadu jmv
, které zadaný scénář odsimuluje.
Mezivýsledky se nemažou.
Input.ja
Vstup. Implicitně a.ja
.
-oOutput.a
Název uspořádaného scénáře. Implicitně a.a
.
-p
Vytvoří soubor a.ja.p
a skončí. Tedy provede pouze makro preproces.
-a
Vytvoří soubor a.a
a skončí.
-h
Krátký přehled voleb.
Jednoduchý přerovnávač jazyka A
jja [-i input.ja ] [-o output.a ]
-i input.ja
Soubor vstupu . Implicitně a.ja
.
-o output.a
Výstup v jazyce A. Implicitně a.a
.
Jazyk A má tu vlastnost, že události se musí popisovat tak, jak jdou za sebou (přesněji: jak jdou za sebou jejich počátky), a to i když jsou na sobě třeba nezávislé. To je sice výhodné pro pohybový program jmv, který nemusí udržovat v paměti celý scénář, ale nevýhodné pro autora scénáře.
Tuto nevýhodu odstraňuje právě tento přerovnávač. Zvlášť si můžeme definovat tryskání jednotlivých trysek, zvlášť pohyb kamery, osvětlení atd. Viz též kapitola jaz_ja .
Hlavní hybatelský program. Simuluje pohyb vody dle nastaveného modelu. Výsledky ukládá ve formátu .bur.
jmv [-h] [-ofmvsn] [ -i infile.a ] [-o outfile.bur]
-i intput
vstup ve formátu jazyka a. Implicitně a.a
.
-o output
výstup ve formátu bur. Implicitně a.bur
.
-m
Mlha. Použij model, který počítá i s tzv. mlhou.
-p
Kapky budou udržovat minimální vzájemnou vzdálenost.
-f
Silou. Minimální vzdálenost se bude udržovat dle modelu silového odstrkování.
-r
Vítr. mlha se posunuje v závislosti na vítru.
-v
Vlnky. Použij vlnění hladiny.
-s
Štěpení. Použij model, který použivá štěpení osamělých kapek na menší.
-d
Použij definice. Do výstupu zapisuj i informace typu define.
-n
Následující volba ne.
-h
Krátký přehled voleb včetně jejich implicitních hodnot.
Pro více informací o významu voleb viz. kap teo .
prohlížeč formátu buran, formátu pro uchování informací
o animaci na úrovni framů. Vhodné pro ladění programů, které formát
buran mají produkovat (jchrl
, jmv
).
bured -fN -bBEG -eEND file.bur
file.bur
soubor formátu buran, který má být znázorněn
-fN
Frame N. zobrazí se N'tý frame.
-bBEG
První zobrazovaný frame bude BEG.
-eEND
Poslední zobrazovaný frame bude END.
Formát buran slouží k uchování informací o animaci rozložené na framy. U každého framu animace je třeba si uchovat různé informace o scéně, např. polohy kapek, polohu a směr trysek, barvy světel. Proto jsou v každém framu bloky těchto informací, ve kterých se uchovává více informací stejného typu.
POINTS
3d body.
POLYGON
Vrcholy polygonu.
LOG
Logovací informace.
CASTICE
Zrod kapky, poloha, rychlost. Pro rychlejší preview je výhodnější nepočítat polohy všech kapek, ale zapamatovat si pouze jejich zrod.
KOULE
3d bod a poloměr. Používá se pro pozici kapky.
CAMERA
Pozice ``look_at'' a ``up'' kamery.
VLNKY
Pole výchylek vodní hladiny.
DEFINE
Propašování hodnoty vektoru a identifikátoru do scény povray'e. V popisu scény se pak objeví něco jako ``#declare ID = <1, 2, 0>''.
Pro každý blok ve framu se vytiskne nejprve hlavička s těmito informacemi: název typu bloku, číselně typ bloku, délka jedné datové položky, offset ve framu, počet datových položek, velikost v bytech.
Následuje výpis každé datové položky, na jeden řádek jedna. Výjimku
tvoří bloky typu LOG
a DEFINE
, kde se vlastně celý blok vypíše
na jeden řádek.
[tryska (8, 32) 242 2(64)] `(0.00 0.10 0.00) (0.00 1.00 0.00)0.01 `(0.10 0.10 0.10) (0.00 1.00 0.00)0.01
Program pro rychlé zhlédnutí spočítané animace. Umožňuje procházení scény, zobrazení jen některých komponent.
xburan [ input.bur ]
Zobrazí animaci input.bur
. Implicitně look.bur
.
Jak název napovídá, pro zobrazení jsou potřebné Xwindows.
Z formátu .bur
jsou podporovány tyto typy:
KAPKA
-- bílá tečka, druhé bloky (užívané pro mlhu), červená tečka.
POLY
-- modrý polygon,
TRYSKA
-- parabola od místa vzniku, až po průnik s rovinou xz,
DEFINE
-- žlutý ``motýl'' pro parametry scény.
h
-- otáčení
j
-- chod vzad
k
-- chod vpřed
l
-- otáčení vpravo.
H
-- úkrok vlevo
J
-- nahoru
K
-- dolů
L
-- úkrok vpravo.
u
-- nahnutí dolů
i
-- nahnutí nahoru
n
-- následující
p
-- předcházející
b
-- počáteční
+, =
-- zvětšit
-
-- zmenšit
0 - 7
-- výběr kamery
8
-- nehýbat s kamerou dle popisu animace, zůstat na místě.
9
-- vytisknout pozici kamery na stdout.
Ta se pak muže vložit do scénáře.
h
-- help. Protože program je používán k ladění, dosti často je
potřeba nová klávesa, help zobrazí aktuální funkce kláves.
q
-- konec
Na základě informací o vyřazených kapkách spočítá pro každý časový okamžik pole výchylek vodní plochy. Vstup i výstup je ve formátu buran.
jvl [-fN ] [-bBEG] [-eEND] [ -i input.bur ] [ -o outfile.pov ]
-i input.bur
Soubor vygenerovaný jmv
'em. Implicitně a.bur
output.pov
Výstupní soubor ve formátu povray.
-bBEG
Nastaví počáteční frame, od kterého se bude počítat. Předchozí
zaniklé kapky však nebudou mít na výsledek vliv
a proto dojde k chybě. Někdy je však třeba spočítat pouze část
animace a pak stačí vzít do úvahy pouze několik předchozích
framů, viz volbu -p
. Imlicitní hodnota volby -b
je 1
.
-pPRE
Bere do úvahy PRE
framů před BEG
. Tyto framy budou
odsimulovány, avšak výstup začíná až framem BEG
.
-eEND
Nastaví, po který frame se mají vlnky počítat.
Je implementována pouze statická velikost vodní hladiny, ač měnit rozměry hladiny je v jazyce JA v zásadě možné. Hladina se sice může pohybovat, avšak velikost vodní plochy bude braná z prvního zpracovaného framu.
Vytváří z popisu animace .bur
popis scény jednoho framu
job [-fN ] [-kcn] [-vName] [ input.bur [ outfile.pov ] ]
Job vezme jeden obrázek formátu popisu animace a vygeneruje zdojový text pro povray.
input.bur
Soubor vygenerovaný jmv
'em
output.pov
Výstupní soubor ve formátu povray.
-fN
Zobrazovaný frame číslo N.
-k
Kapky budou reprezentovány jako obyčejné koule.
-r
Blízké koule budou spojené válcem.
-n
Nezobrazovat kapky.
-c
Obalit barevně koule, spojující válce i trojúhelníky. Pro účely ladění obalovacích algoritmů.
-vNAME.tga
Bumb textura vlnek do NAME.tga
.
-bXXX
Bump scale, výchylka vlnek v metrech se musí převést do 256'ti stupňové škály <-128, 127>. Někdy jsou vlny tak vysoké, že přesáhnou tento rozsah. Proto je vhodné vlnky touto volbou zmírnit. Implicitní hodnota 6000.
-h
Krátký seznam voleb. Právě zakompilované implicitní hodnoty.
dish-client
,
dish-vi
,
dish-get
,
dish-put
,
dish-num
soustava shelových scriptů, pro spočítání animace ze spočítaného .bur
s pomocí více počítačů.
Na serveru musí být přítomné scripty dish-vi
, dish-get
,
dish-put
, dish-num
program job
, pro vytváření .pov z .bur. Také je zde spočítaná animace
.bur.
Na clientu musí být dish-client
a povray
. Script
dish-client
si
vytvoří adresář tmp a do něj si bude ze serveru stahovat popisy scén,
spočítané obrázky vrací serveru.
Čísla framů, která se mají spočítat, jsou
v souboru dish.todo
(na serveru). Všechna čísla jsou na jednom řádku
oddělená mezerou.
Scripty dish-num
a dish-vi
vytváří soubor(lockfile) dish.lock
,
čímž je zajištěno, že při práci s dish.todo
nedochází ke kolizím.
dish-client
Script dish-client využívá program ssh a proto je dobré umožnit clientům přístup k serveru bez autorizace (viz. man ssh).
Ve scriptu je také třeba upravit nastavení těchto proměnných,
SERVER
Uživatel a server na který se bude dish-client
logovat a získavat
z něj popisy scén jednotlivých framů. Např:
vitas@vitas.kolej.mff.cuni.cz.
RDISH
Kde je adresář s dish-* na serveru
LDISH
Kde je adresář s dish-* na clientu
Taktéž je třeba upravit proměnnou prostředí PATH
tak,
aby vedla i k povray
'ovi.
dish-get
tento script je volaný z dish-clienta
, ale je spouštěn za pomocí
ssh
na straně serveru. Požádá dish-num
o další číslo
framu, vytvoří potřebné soubory, zataruje je. Vytiskne tři řádky:
první příkaz, který se má spustit (obyčejně něco jako
povray -ifile.pov -ofile.tga
). Druhý řádek je seznam souborů,
které se mají poslat zpět. Třetí řádek jsou soubory, které se po
skončení tohoto výpočtu mohou smazat. Poté následují zatarované
vstupní soubory.
dish-num
Vždy při žádosti clienta o práci script dish-num
přečte první číslo
ze souboru dish.todo
a smaže ho.
dish-vi
Editovat soubor dish.todo
je možné i za běhu výpočtu pomocí
tohoto scriptu. Klávesa ``m'' je namapovaná na ``zapsaní čísla o jedna většího,
než jaké je na místě kurzoru'', čehož lze s úspěchem využít při psaní
dlouhého seznamu framu. Tj. stačí napsat ``1'' a pak jen přidržet ``m''.
dish-put
pouze příjme zatarovaný výsledek od dish-client
'ta. A roztaruje jej.
V následující části se budeme zabívat programátorskou dokumentací. Je zde popsáno členění na moduly jejich rozhraní jednotlivých modulů a hlavní datové struktury.
Knihovna pro uchování informací o animaci, která je rozdělena na navzájem nesouvisející framy. Každý frame může obsahovat několik bloků.
Knihovní funkce pro práci s formátem buran využívají strukturu TBuran
.
O datech struktury TBuran uživatel této knihovny nemusí nic vědět.
Další pomocnou strukturou je TBuran_dir
, která slouží (mimo jiné)
k předávání informací o právě načteném bloku dat:
typedef struct TBuran_dir { int typ, off, siz; }TBuran_dir;
V typ
je typ dat bloku a v siz
je velikost bloku v bytech.
Velikost typu dat v bloku není pro knihovnu
nikterak zajímavá.
Knihovna vlastně nic neví (ani nepotřebuje vědět) o typu dat v bloku.
Přesto je použita následující konvence: spodních
16 bitů typu má význam délky typu, horních 16 je pak již rozlišovacích.
TBuran* buran_open(char *path, char *mode)
vytvoří strukturu TBuran
. Argumenty path
a mode
jsou
obdobně jako ve fopen(3)
. Tedy path
je jméno otevíraného souboru,
mode
je jedno z "rb"
pro čtení, nebo "wb"
pro zápis.
TBuran* buran_open_by_file(FILE *f)
obdoba s tím rozdílem, že soubor f
je již otevřen v příslušném módu.
int buran_seek(TBuran *bur, int offset, int mode)
posun po buranu bur
, offset je v závislosti na mode
buď od
začátku (SEEK_SET
), nebo relativně k současné pozici (SEEK_CUR
).
TBuran_dir *buran_read(TBuran *bur, int typ, int nty)
načtení nty
'ého bloku typu typ
. Pokud bude typ==0
,
jde o nty
blok bez ohledu na typ.
V případě neúspěchu je vrácená hodnota NULL
.
Při úspěchu ukazuje na TBuran_dir
, ve které
jsou obsaženy informace o načteném bloku.
unsigned char *buran_buff(bur);
vrátí ukazatel na buffer s místem, kde jsou data bloku. Je nutné si jej přetypovat.
TBuran_dir *dir; float *points; /* chci všechny bloky s body (POINT)*/ for(i=0; NULL != (dir = buran_read(bur, BUR_TYP_POINT, i); i++) { points = (float*)buran_buff(bur); ... dělej něco s polem `points' }
int buran_write(TBuran *bur, int bur_typ, int siz, void * buff)
Zapíše blok typu bur_typ
, na který ukazuje buff
velikosti (v bytech ) siz
.
int buran_write_init(TBuran *bur, int bur_typ)
Připraví burana pro postupný zápis bloku typu typ
. Ekvivalentní
buran_write(bur, bur_typ, 0, 0);
int buran_write_add(TBuran *b, int siz, void *buff)
Přidání dat k posledně zapsanému bloku dat. Velikost dat je siz
a nacházejí se na buff
.
int buran_flush(TBuran *b)
Konec zadávání tohoto framu. Je potřeba zavolat po ukončení sypání dat do jednoho framu.
int buran_printf(TBuran *bur, char *format, ...)
Zapiš logovací hlášku (typ BUR_TYP_LOG
). Argumenty format
a ...
jsou stejné jako u printf(3)
.
Příklad ukazuje, jak je možné uložit 10 framů, v každém jsou dva bloky(logovací hláška a blok pět bodů):
TBuran *bur = buran_open("look.bur", "wb"); float point[3] = { 1, 0 , 0}; /* deset obrázků */ for(int frame = 0; frame < 10; frame++) { /* v každém pět bodů */ buran_init(bur, BUR_TYP_POINT); for(i = 5; i--;) { /* přidej jeden bod */ buran_add(bur, sizeof(float)* 3, point); point[2] += 0.5; /* ``výpočet'' bodu */ } /* zapiš poznámku k tomu framu */ buran_printf(bur, "Poznámka k framu %i\n", frame); /* ukonči zadávání tohoto framu */ buran_flush(bur); }
Podrobný popis formátu buran
Celý soubor je rozdělen na framy. Fram má následující struktru: nejprve je hlavička framu. Ta je dlouhá 3 inty. V prvním je tzv magické číslo (hodnota pro kontrolu, zda nejde o porušenou strukturu souboru). Následuje offset následujícího a předcházejícího framu.
Následuje adresář bloků ve framu. Každá položka se skládá opět ze tří intů. První je typ bloku. Druhý je offset začátku dat odpovídajícího bloku. Offset se počítá od konce adresáře. A konečně třetí položka je délka bloku dat.
Pokud jsou všechny položky nulové, jde o poslední položku adresáře.
Následují data framu.
Knihovna nepomýšlí na korekci různých typu endianit (byte-orderů).
Proto soubor není přenositelný mezi platformami různých endianů. Jinými
slovy: číst data můžeme jen na endiánech na kterých tato data vznikala.
Taktéž zřejmě budou problémy s přenosem souboru mezi platformami s
různými hodnotami sizeof(int)
.
Pro parsování je použita sehraná dvojice flex-bison. Pro více informací viz flex(1), bison(1).
Text mezi znaky tokeny %{
a %}
je bezezměn zkopírován do výstupního
souboru již při parsování.
Během parsování se postaví derivační strom. Jeho průchodem získáme jeho všechny atomické bloky i s přesnými počátky. Po setřízení dle počátku bloku je vše připraveno k výstupu do korektního kódu jazyka A.
V každém kroku simulace je třeba vědět o poloze a směru a síle trysky.
I když popis jazyka A umožňuje (v čase) spojité definování těchto hodnot, pro simulaci je nutné toto spojité chování zdiskrétnit. Právě k tomu slouží modul Zenon. Zjednodušeně řečeno, chceme se ptát Zenona, jak vypadá scéna v určitém okamžiku. Za odměnu mu slíbíme, že se budeme ptát pouze na čím dál tím větší čas.
Pro lexikální analýzu je použit nástroj flex(1). Tomu stačí pouze
popsat jednotlivé lexémy regulárnímy výrazy a co se má dělat poku je najde.
Z toho popisu flex vytvoří zdrojový text v jazyce C
.
Tak můžeme přehledným a snadno upravitelným způsobem získat velmi efektvivní
lexikální analyzátor. Více informací o flexu viz např. info flex
.
Parser bere od lexikálního analyzátoru jeden token za druhým a podle typu s nimi nakládá takto:
[
-- tokeny se
začnou ukládat do spojového seznamu, který bude jako jedna položka zásobníku,
až po vyvážený počet hranatých závorek.
Některé funkce speciálně:
[] "idfce" def2
Definice nové funkce. Na zásobníku musí být dva argumenty: tělo definice (spoják tokenů), a id názvu této nové funkce.
TT cas1
Posunutí času na TT
. Pokud je tento čas větší než požaduje modul Zenon
(viz níže), parsování je dočasně přerušeno.
[] dT int2
Definice intervalu. Na zásobníku je tělo intervalu (spoják tokenů)
a délka intervalu dT
.
Tělo bude provedeno pokaždé, když bude modul Zenon požádán
o zjištění stavu trysek v čase, který bude mezi TT
a TT + dT
.
Pracuje vlastně ve dvou fázích. Nejprve načte všechny další intervaly,
které by mohly potenciálně obsahovat čas TT
.
Zároveň se vyřadí ty intervaly, které již do děje nezasáhnou.
Druhá fáze spočívá v tom, že se provedou těla všech intervalů obsahujících
čas TT
.
V polích zn_trysky
, zn_svetla
, zn_kamery
, zn_define
,
jsou informace o tryskách, o světlech, o kamerách, o proměnných scény.
Informace o tryskách jsou využity modulem Chrlič, ostatní jsou
jednoduše přeneseny do výstupu.
Modul Zenon má následující interface:
zn_init()
inicializace.
zn_set_time_zone(double tz)
nastavení kroku.
zn_shift_time()
posuň čas krok vpřed.
zn_set_time_lo(double tm)
nejmenší možný čas bude tm
.
zn_at_time(double tm)
spočítej pozici trysek, světel ... v čas tm
.
Z programu jmv
vypadnou (zjednodušeně řečeno) souřadnice a poloměry kapek.
Úkolem job
je tyto informace převést do popisu scény povray
'e.
Tato metoda je velmi jednoduchá: kapky budou nahrazeny skleněnými koulemi o daném poloměru.
O něco málo složitější metoda. Blízké kapky jsou navíc spojeny komolými jehlany.
Název zůstal historicky z doby, kdy měly všechny kapky stejný poloměr.
Pojem ``blízké kapky'' je definován tak, že středy kapky nejsou dále než
DISTANCE
(konstanta definovaná v job.c
).
Pokud jsou tři koule dostatečně blízko, vytvoří se mezi nimi navíc ještě trojúhelník, který zacelí díru použítím předchozí metody.
Protože by vznikla velká spousta trojúhelníků, což by jistě zdržovalo následný raytracing, je použita ještě drobná optimalizace. Vytvoří se pouze ty trojúhelníky, které jsou viditelné. Přičemž viditelnost chápeme následovně: Pokud 4 kapky tvoří (v relaci ``býti blízko'') čtyřstěn, potom o vnitřních stranách stěn tohoto čtyřstěnu řekneme, že nejsou viditelné. A nakonec -- trojúhelník je viditelný, pokud je viditelný alespoň z jedné strany.
Následujícím prostředkům se již nedá říkat obalování, ale myšlenka je podobná:
převést spočítané prvky scény do popisu scény povray
'e.
Bude vytvořen obrázek ve formátu TGA s bumb texturou výchylek vodní hladiny, zároveň se vytvoří objekt scény (obdélník), která se na tuto texturu odkazuje.
Do popisu scény se přidá definice hodnoty. Tedy například:
#declare poloha_autobusu = <1,0,1>
v hlavním popisu scény se potom může autobus posunout do místa
poloha_autobusu
.
Modul Star uchovává kapky ve vnitřní datové struktuře. Požadavky na tuto strukturu jsou: rychlé vyhledávání blízkých kapek, možnost projít všechny kapky. Dále potřebujeme do struktury přidávat nové a ubírat staré kapky. Na druhou stranu můžeme slíbit, že přidávání a ubírání bude probíhat najednou (v kvantech). Jinými slovy budou se střídat dvě fáze. První: časté vyhledávání. A druhá: mazání a přidávání. To odpovídá tomu, že simulace probíhá diskrétně.
Prostor je rozdělen na 2 48 pomyslných buněk. Buňka je vlastně malá krychlička, která případně obsahuje kapky. Každá buňka má své vlastní číslo, které koresponduje s jejím umístěním v prostoru. Číslo je rozděleno na významnější (hi) a méně významnou část (lo). Významná část má 32 bitů, méně významná 16.
Základem je pole st_hvezdy
, ve kterém jsou informace o kapkách
(trochu zavádějící název zůstal ze starších verzí). Tj. poloha,
rychlost a různé další, které využívá převážně modul Move.
Nad tímto polem je ona vnitřní struktura,
tvořena polemi st_bunky_ht
hašovací tabulka buněk, st_bunky
pole obsazených buněk, st_hvezdy_idx
prvky buněk.
Dejme tomu, že máme najít kapku, která patří do buňky s číslem B
.
Méně významnou část čísla B
nazveme lo
, více významnou pak hi
.
st_bunky_ht
hashtable buněk, na místě st_bunky_ht[lo]
se nalézá index do pole st_bunky
, jde o první buňku s tímto
lo
. Zatímco st_bunky_ht[lo+1]
je index na první buňku,
která má již jiné lo
. Mezi těmito dvěma čísly se nacházejí
všechny buňky s hledaným lo
.
st_bunky
bloky buněk se stejným lo
.
Jak jsou tyto bloky určeny je patrné z předcházejícího odstavce.
V bloku jsou buňky uspořádány dle hodnoty hi
. Hledaná buňka
se najde poměrně snadno půlením intervalu. V buňce jsou
kromě informace o hodnotě hi
tyto informace:
fst
první hvězda této buňky (odkaz do pole st_hvezdy_idx
),
len
jejich počet.
st_hvezdy_idx
bloky indexu do pole st_hvezdy
, v každém
bloku jsou hvězdy ze stejné buňky. Jak jsou určeny počátky a délky
těchto bloků, je opět jasné z předchozího odstavce.
Z výše uvedeného je zároveň i vidět, jak se v takové struktuře vyhledávají blízké kapky: prostě prohledáme blízké buňky. Čísla hledaných buněk jsou snadno spočítatelná z jejich souřadnic.
První průchodem pole st_hvezdy
zjistíme počty kapek se stejnými lo
.
Tyto hodnoty jsou dočasně uchovány v poli st_bunky_ht
.
Poté průchodem pole st_bunky_ht
připravíme bloky v
st_bunky
. Pro každou, kapku zde bude zatím připravena jedna buňka.
Druhým průchodem st_hvezdy
zaplníme pole st_bunky
.
Následuje komprese a setřídění v každém z bloků buněk.
Bloky buněk budou setřízeny dle hodnoty hi
.
Komprese spočívá v tom, že buňky se stejnou hodnotou hi
budou
spojeny v jednu.
Posledním průchodem st_hvezdy
konečně zařadíme hvězdy do buněk.
A to tak, že vyplníme bloky pole st_hvezdy_idx
indexy příslušných kapek.
Označíme h_siz
počet kapek,
rozsah hodnot lo
označíme lo_max
.
ro
maximální počet kapek na buňku ro
.
Z výše uvedeného je vidět, že časová složitost
vybudování struktury je O(h_siz + lo_max)
.
Časová složitost vyhledávání buňky je pak O(ln(kol))
,
kde kol
je maximální počet kolizí v st_hvezdy_ht
.
Tyto úvahy však vedou k zamyšlení, zda má smysl počítat asymptotickou složitost datové struktůry, jejíž velikost je omezena konstantou (viz následující odstavec).
Velikost této struktury záleží poněkud netriviálním způsobem na
maximálním počtu kapek (h_siz
).
V ``malé'' variantě je počet kapek omezen číslem
65535(= 2^16 - 1)
.
Ve ``velké'' variantě je počet kapek
v podstatě neomezen (přesněji: omezen číslem 2^32
). V hranatých
závorkách je uváděna hodnota pro velkou variantu.
Počet položek pole st_bunky_ht
je vždy 64k.
Každá položka (index do pole st_bunky
) má velikost 2B [4B].
Počet položek pole st_bunky_ht
je h_siz
.
Velikost položek hi
4B [4B],
fst
index do st_hvezdy_idx
2B [4B],
len
délka bloku 2B [4B], celkem tedy 8B [12B].
Počet položek pole st_index
je také h_siz
.
Položky mají velikost 2B [4B].
Celkově je tedy v malé variantě potřeba (horní odhad) 12 * 64kB = 768kB.
Kódování polohy prostoru do čísla buňky je voleno tak, aby blízké buňky měly rozdílné číslo.
lo y5 y4 y3 y2 y1 y0 z4 z3 z2 z1 z0 x4 x3 x2 x1 x0 `--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--' 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 hi y15 ... y6 z15 ... z5 x15 ... x5 `---+---+---+---+---+---+---+---+---' 31 ... 22 21 ... 11 10 ... 0
st_init()
Inicializace polí st_hvezdy
, st_hvezdy_idx
, st_bunky_ht
,
t_star *st_hvezda_alloc()
Alokace nové hvězdy v poli st_hvezdy
.
hvezda->flag &= ~ST_FLAG_IN
Zneplatnění hvězdy. Po zavolaní st_pack()
bude hvězda uvolněna.
void st_pack()
modul Star vymaže všechny označené hvězdy a přejde do ``přidávacího'' stavu. Tj. mohou se přidávat nové kapky.
void st_build()
Vystavěné struktury. Od této chvíle se bude pouze vyhledávat.
t_long_bid vec2bid(t_vec v)
t_long_bid bid2vec(t_bid b)
t_long_bid bid2lbid(t_bid b)
Funkce na převod mezi identifikací, bodem prostoru a číslem buliňky, do které patří.
Hlavním cílem této diplmové práce bylo vymyslet, vyzkoušet a porovnat možné přístupy k dané problematice. Nyní jsou implementovány základní modely chování vody, štěpení kapek, vlnění hladiny.
Zda bylo dosaženo onoho ``realistického zobrazení'', samořejmě dosti závisí na subjektivním pohledu. Nicnéně je nutné konstatovat, že nebylo dosaženo takové věrnosti, aby si výsledek ``necvičený laik'' spletl se skutečností, jak by se od realistického zobrazení mohlo očekávat.
Práce přinesla také nějaké vedlejší produkty, které mohou být aplikovány i mimo téma práce.
Jazyk JA vyvinutý v rámci této práce umožňuje spojité popsání chování oběktů ve scéně. Proto je možné jej s úspěchem použít i na zobrazení ``netryskajících'' animací.
Počítání animace je, díky prostému distribuovanému systému dish-*
,
možné v rozumném čase, pokud je k dispozici dostatečná výpočetní síla.
Například spočítání průměrné desetisekundové animace (100 framů za sec)
trvalo za použití 18 počítačů PC, 4 počítače silicon graphic, a 5
počítačů sun zhruba dvacet minut.
Vzhledem k umírněnému používání systémově závislých knihoven, je celý systém dobře přenositelný. Přenos byl bez větších zásahů a problémů proveden na tyto platformy: PC (Linux, FreeBSD, DOS), Sparc (Solaris, Linux), Silicon Graphics (Irix).
Dalším pozitivním rysem je poměrně dobře navržená struktura celého projektu, která umožňuje snadné zásahy a tím je dobře připravena pro případný další vývoj.
Tento vyvoj by se mohl zaměřit na sofistikovanější přístup při závěrečném zobrazení, či více propracovat použité matematické modely.
Na přiloženém cd jsou vytvořené programy, jejich zdrojové texty a ukázky animací. Struktura adresářů je následujicí:
/doc
Text diplomové práce v mnoha textových fomátech (tex, dvi, ps, html, txt, doc, sgml).
/src/di
Zdrojové texty diplomové práce.
/src/cvsroot/
Archív zdrojáků cvs.
/bin/i386
Zkompilované programy pro platformu Linux/x86
/bin/sparc
Zkompilované programy pro platformu sparc (sun).
/sup
Podpůrné programy (pro platformu Linux/x86), které se ne vždy dodávají s distribucí.
Jsou to
povray
, ssh
, knihovna Mesa
, Togl
, mpeg_encode
.
/obr
Ukázky výsledků. V souboru /obr/index.html
je přehled a komentáře k obrázům.
Jeden snímek z animace. Pro simulaci byl použit základní gravitační model spolu s modelem hrubého odstrkování. Kapky jsou nahrazeny koulemi.
Ten samý snímek, jen k obalování je zvolena válcová metoda, tj blízké kapky jsou návíc spojeny válcem.
Tento snímek se od prvního liší pouze změnou použitého materiálu pro vodu. Zatímco v předcházejících případech šlo o zrcadlo, v tomto případě je použito čisté čiré sklo.
Prohlížeč xburan
. Tímto programem, lze snadno a rychle prohlížet
právě spočítanou simulaci, aniž by se musela počítat výsledná animace.
V prohlížeči je zhruba stejný záběr jako v předchozích ukázkách.
Ukázka detailu obalování kapek. V místě kapky je koule a blízké kapky jsou spojeny válci. Barvy válců a koulí byly záměrně obarveny odlišnými barvami, aby vynikla struktura obalování.
Jiný přístup k obalování. Blízké kapky jsou oproti předchozímu příkladu navíc pokryty trojúhelníčky. Jednotlivé útvary jsou opět obarveny nerealistickými barvami.
vedoucímu této diplomové práce, za dobré vedení, spoustu inspirativních připomínek.
za opravdu velkou inspiraci.
autoru editoru vim
, ve kterém tato diplomová práce byla napsána.
za hudbu při které tato práce vznikala.
za morální podporu při tvorbě diplomové práce. Zvláště pak za cenné konzultace v oblasti pravopisu.
[1] Orfeus: Křižíkova fontána http://www.radio.cz/orfeus/
[2] Gerlová, J.: Životopis Františka Křižíka http://www.trafika.cz/pr/1997/9707/pr182701.HTML
[3]
Miller, J. S.:
Modelling Liquids using particle Systems and Impicit Surfaces.
Ph.D. Thesis University of Manchester Department of Computer Science, 1998
[4] Murta A., Miller, J. S.: Modelling and Rendering Liquits in Motion. WSCG99
[5] Svěrák Z., Smoljak L., Cimrman J.: Posel z Liptákova.
[6] Horák, Z. a kol: Technická fysika. SNTL, 1961
[7] Rapaport, D. C.: The art of molecular dynamics simulation. Cambridge University Press, 1995
[8] GNU info cpp
[9] Povray, Povray documentation. http://www.povray.org/