Ray-tracing v knihovně GrCis
Knihovna GrCis
Základní definice interface a tříd pro paprskové zobrazovací metody najdete v adresáři
common. Abstraktnější definice (interface, default třídy) jsou obsaženy
ve zdrojovém souboru RayCastingCommon.cs, jednoduché varianty implementací
pak v souboru RayCastingBasic.cs (Váš kód bude některé tyto třídy doplňovat,
tyto jsou zatím označeny partial).
Některé části architektury ray-traceru v GrCis jsou popsány v
této prezentaci
(existuje i ve video podobě zde).
Sdílený dokument pokrývající mnohé návody, rady a doporučení najdete
zde.
Spuštění, projekty
Pro experimenty s ray-tracingem jsou nejlepší projekty 048rtmontecarlo nebo
048rtmontecarlo-script. Ten druhý je rozšířen o možnost definovat scénu externě,
pomocí scriptu se syntaxí C# CSharpScript.
Vzorové scripty (všechny běžné vestavěné scény) najdete v adresáři data/rtscenes.
Doporučované animační projekty jsou 062animation a
062animation-script. Oba dva umožňují předávat definiční funkci scény textový
parametr Param: z formuláře, druhý z nich je opět rozšířen o možnost definovat scénu
(animační scénář) pomocí CS-scriptu. Ukázkový script je v souboru data/rtscenes/AnimatedScene.cs.
Spuštění programu 048rtmontecarlo-script je o trochu složitější, je třeba při
startu zadat některý z command-line příkazů (doporučujeme před spuštěním změnit adresář, ve kterém
má program běžet project / Properties / Debug / Working directory: – nastavte tam
adresář, ve kterém leží projekt 048rtmontecarlo-script!):
- -nodefault zruší implicitní vestavěné scény zkompilované v .EXE souboru
- -dir <directory> (například "-dir ..\data\rtscenes\")
načte jména scriptů z daného adresáře. Vlastní scripty se vždy dynamicky načítají do paměti
před spuštěním příkazu "Render", můžete je tedy průběžně editovat externím
textovým editorem a program 048rtmontecarlo-script nechat stále spuštěný
- -scene <scene-script> je opakovatelný a načte zadaný script
- -mask <file-mask> mění implicitní masku scriptů scény (normálně je to
*.cs)
Jak bylo uvedeno výše, je vhodné nastavit startovací adresář EXE souboru tak, aby realativní
cestky k souborům/adresářům v command-line argumentech fungovaly.
Kontext CSharpScript definic scény
Kód CS-scriptu je formálně zapsán jako posloupnost příkazů jazyka C# bez nutnosti obalit
je do nějakého bloku nebo funkce. Příkazy dostanou před spuštěním k dispozici všechny běžné
"name-spaces" (jakoby direktivy using) a navíc v globálních proměnných
několik užitečných hodnot:
- IRayScene scene – tento objekt bude předem zkonstruován a do něj musíte scénu vyplnit
- string param – textový parametr z formuláře (příkazové řádky), byl-li zadán, jinak prázdný řetězec
- Dictionary context – zde se předávají další parametry, dovnitř
scriptu i ze scriptu ven. Přehled parametrů a jejich typů ("in" znamená hodnotu předávanou do scriptu,
"out" hodnotu, kterou může script nastavit a ovlivnit tak běh frameworku), hodnoty předané ze scriptu
tímto způsobem mají větší prioritu než hodnoty nastavené v GUI/formuláři:
- bool PropertyName.CTX_PREPROCESSING (in) – když je přítomen (a true), znamená to, že
je script volán poprvé a můžete v něm spočítat "pre-processing" – typicky nějakou simulaci
společnou pro všechny snímky animace. Všechny nové hodnoty, které do context zapíšete, budou předány
i do následujících běhů tohoto scriptu...
- string PropertyName.CTX_SCENE_NAME (in) – jméno scény
- string PropertyName.CTX_SCRIPT_PATH (in) – úplná cesta k souboru se scriptem (dá se použít např. pro
adresování dalších pomocných souborů ve stejném adresáři)
- int PropertyName.CTX_WIDTH (in, out) – šířka renderovaného obrázku/snímku v pixelech
- int PropertyName.CTX_HEIGHT (in, out) – výška renderovaného obrázku/snímku v pixelech
- int PropertyName.CTX_SUPERSAMPLING (in, out) – supersampling koeficient (počet vzorků na pixel,
1 ... bez antialiasingu)
- IImageFunction PropertyName.CTX_ALGORITHM (out) – zde můžete přenastavit algoritmus renderingu
(default je RayTracing)
- IRenderer PropertyName.CTX_SYNTHESIZER (out) – zde můžete nastavit vlastní vzorkovač bitmapy
(konvertor z IImageFunction do rastrového obrázku, default je SupersamplingImageSynthesizer = jittering)
- string PropertyName.CTX_TOOLTIP (out) – můžete definovat obláčkovou nápovědu ("tooltip") pro
textové pole Parameters: na formuláři. Použijte znak '\r' pro oddělování řádek
- double PropertyName.CTX_START_ANIM (in, out) – počáteční čas animace v sekundách
- double PropertyName.CTX_END_ANIM (in, out) – koncový čas animace v sekundách
- double PropertyName.CTX_FPS (in, out) – FPS (počet snímků za sekundu) pro výpočet animace
- double PropertyName.CTX_TIME (in) – když je nastavený, znamená to, že je renderován jednotlivý
obrázek a ne celá animace. Můžete tedy např. zredukovat množství práce v simulaci/pre-processing.
Vlastní hodnota tohoto atributu udává čas renderovaného snímku
Důležité obecné interface
- IIntersectable – jakýkoli objekt, který umí být protnut paprskem
(metoda Intersect() vracející seznam objektů Intersection)
- ICamera – generátor primárních paprsků (např. StaticCamera)
- ILightSource – světelný zdroj
- IReflectanceModel – lokální model odrazu světla na povrchu tělesa
- IMaterial – parametry konkrétního materiálu (musí být v souladu s použitým modelem odrazu světla)
- ITexture – obecný prototyp textury jako objektu schopného měnit (modulovat) libovolné
hodnoty uložené v objektu Intersection
- ITimeDependent – objekt schopný reagovat na změny v čase (tj. animovat se v čase).
Musí mít schopnost se naklonovat (pro výpočty ve více vláknech)
- IRayScene – objekt sdružující všechny komponenty zobrazované CSG scény:
vlastní geometrii scény Intersectable, barvu pozadí BackgroundColor,
generátor primárních paprsků Camera a seznam světelných zdrojů Sources.
Jednotlivé komponenty mohou být animovatelné (viz interface ITimeDependent)
Datová struktura scény (geometrie)
- ISceneNode – obecný uzel stromu hierarchie scény, může mít potomky (hierarchie)
a přiřazené atributy (GetAttribute(), SetAttribute()...), umí se protnout paprskem
(interface IIntersectable)
- ISolid – list CSG stromu obsahující těleso (hlavně musí implementovat
interface IIntersectable). Příklady těles:
Plane,
Sphere nebo
BezierSurface
- DefaultSceneNode – implicitní implementace základních funkcí interface ISceneNode,
slouží jako předek všech dalších uzlů, mj. i listů (ISolid)
- CSGInnerNode – implementace vnitřního uzlu CSG stromu
Další důležité objekty
- Intersection – datový objekt obsahující všechny údaje o nalezeném průsečíku paprsku
s povrchem tělesa/scény. Dvoufázový výpočet, primárně vznikne instance objektu obsahující pouze
nejnutnější údaje, k doplnění slouží metoda CompleteIntersection()..
Algoritmy paprskového zobrazování
- IImageFunction – abstraktní koncept obrazové funkce se spojitou průmětnou – zobrazení
ze spojitého obdélníka do prostoru barev (double[])
- IRenderer – z připojeného objektu typu IImageFunction umí vyrábět výstupní rastrový obrázek
- SimpleImageSynthesizer – implementuje IRenderer, nechává počítat jenom jeden vzorek na každý pixel
- SupersamplingImageSynthesizer – potomek SimpleImageSynthesizer,
v každém pixelu umí spočítat a zprůměrovat více vzorků
- RayCasting – implementuje IImageFunction, obsahuje základ algoritmu ray-casting
- RayTracing – potomek RayCasting, implementace rekurzivního sledování paprsku
Detaily
Objekt Intersection
Instance třídy Intersection slouží k uchování informací o konkrétním
průsečíku paprsku se scénou (s povrchem tělesa). Je implementován dvoufázový výpočet:
- V první fázi jsou spočítány jenom ty nejdůležitější údaje (např. souřadnice průsečíku
v 1D systému paprsku) a objekt scény, který průsečík vytvořil (uzel grafu scény), si do něj uloží případné
mezivýsledky pro druhou fázi
- Druhá fáze znamená, že průsečík bude skutečně použit,
a že tedy potřebujeme všechny detailní informace o něm. Zavolá se funkce CompleteIntersection(),
která dopočítá vše potřebné. Mnohé pomocné výpočty, např. transformační matice, akumulace atributů,..
jsou již vestavěny do systému a není třeba je explicitně implementovat
Výčet důležitých položek datového objektu Intersection, nejdříve ty z první fáze (povinné):
- bool Enter [povinná] – příznak, že paprsek v tomto bodě vstupuje dovnitř tělesa (rozhraní "vzduch-těleso")
- bool Front [povinná] – příznak, že paprsek vstoupil dovnitř elementárního tělesa (ISolid):
při výpočtu průsečíku je to zatím hodnota totožná s Enter, později se však může začít lišit
(negativní CSG operace)
- double T [povinná] – parametrická souřadnice průsečíku na přímce paprsku. Pro pozitivní směr od počítku paprsku
je T > 0.0 (výpočet průsečíku paprsku se scénou však musí udržovat i průsečíky na negativní polopřímce)
- ISolid Solid [povinná] – odkaz na těleso, které průsečík vytvořilo (je potřeba pro druhou fázi -
bude se volat jeho matoda Solid.CompleteIntersection(this))
- object SolidData [povinná, může být null] – pomocná data, která si potřebovalo těleso
Solid odložit pro pozdější kompletaci průsečíku
Datové položky počítané typicky až při kompletaci průsečíku:
- Vector3d CoordWorld [odložená] – světové souřadnice průsečíku
- Vector3d CoordObject [odložená] – relativní souřadnice v rámci objektu (animační jednotka, objekt se
ve scéně hýbe jako jeden celek)
- Vector3d CoordLocal [odložená] – lokální souřadnice průsečíku (v rámci elementárního tělesa)
- Vector2d TextureCoord [odložená] – 2D texturové souřadnice na poverchu tělesa
- Vector3d Normal [odložená] – normálový vektor (ve světových souřadnicích)
- Matrix4d LocalToWorld, WorldToLocal [odložená] – transformační matice mezi lokálním (Solid) a světovým
souřadným systémem
- Matrix4d LocalToObject [odložená] – transformační matice z lokálního do objektového (animačního) prostoru
- double [] SurfaceColor [odložená] – pracovní kopie barvy povrchu tělesa (může se mnohokrát přepisovat,
proto se musí okopírovat ze sdílených datových struktur)
- IReflectanceModel ReflectanceModel [odložená] – platný model odrazu světla (= světelný model)
- IMaterial Material [odložená] – materiálové konstanty pro aktuální světelný model
- LinkedList<ITexture> Textures [odložená] – seznam textur v pořadí, ve kterém se mají
aplikovat (tj. od nižších priorit k vyšším)
Interface IIntersectable
Každá třída, která se umí protnout daným paprskem.
- LinkedList<Intersection> Intersect (Vector3d p0, Vector3d p1) – výpočet průsečíků daného
paprsku s objektem. Vrací jen průsečíky s vyplněnými povinnými položkami (první fáze). Souřadnice paprsku
jsou lokální = přizpůsobené elementárnímu tělesu!
- CompleteIntersection (Intersection inter) – kompletace daného průsečíku (druhá fáze), je třeba
dopočítat všechny odložené položky
Interface ICamera
Ve skutečnosti se v zobrazovači používá jako generátor primárních paprsků, to je jediná funkce
objektů tohoto typu. Vstupem je souřadnice bodu v průmětně, výstupem paprsek zadaný počátečním bodem a
směrovým vektorem:
- bool GetRay (double x, double y, out Vector3d p0, out Vector3d p1) – vrací true,
pokud zadaná poloha bodu v průmětně ([x,y]) byla přípustná (výjimky např. objektiv typu
"rybí oko")
- double Width, Height, AspectRatio – parametry oblasti průmětny, která se má používat pro
zobrazení. Tato tři čísla jsou na sobě závislá: AspectRatio = Width / Height
Interface ITimeDependent
Každá třída implementující tento interface se umí měnit v čase (animovat). Obsahuje vlastnosti:
- double Start – počáteční čas ve sekundách, spíše informativní údaj říkající,
v jakém intervalu je akceptován čas (Time).
- double End – koncový čas ve sekundách (délka cyklu animace), spíše informativní údaj
říkající, v jakém intervalu (periodě) je akceptován čas (Time).
- double Time – vlastní nastavení vnitřního času instance. Pokud objekt obsahuje
odkazy na další podobjekty (komponenty), musí se do nich čas propagovat!
Interface ITimeDependent je potomkem ICloneable. Význam je následující: pokud
je výpočet obrázku prováděn ve více vláknech, musí se naklonovat všechny datové struktury, které
by se případně v jednotlivých vláknech mohly individuálně modifikovat. Například při výpočtu
animace má každé vlákno za úkol počítat jiný snímek animační sekvence. K tomu se používá
vlastnost Time, při jejím nastavování by tudíž docházelo ke konfliktu.
Implementace metody Clone() musí být přítomna u všech objektů, které se v průběhu
výpočtu mění a do kterých se ukládají nějaké mezivýsledky (stavové API). Musí se jednat
o tzv. "deep-copy", to znamená, že se objekt musí při klonování postarat o to, aby všechny
jeho komponenty implementující interface ITimeDependent byly rovněž naklonovány!
Konkrétní třídy
Nejdříve několik elementárních těles, všechna implementují ISolid (tj. nepřímo i
interface IIntersectable) a jsou potomky DefaultSceneNode,
to znamená, že mají implicitně implementované všechny funkce potřebné pro začlenění do grafu scény.
Sphere
Jednotlová koule se středem v pořátku. Prototyp elementárního tělesa, obsahuje všechny potřebné implementace
v metodách Intersect() i CompleteIntersection().
Plane, Cube, Cylinder, Torus
Další jednoduchá tělesa, jejichž průsečíky lze spočítat analyticky.
TriangleMesh
Trojúhelníková síť uložená v datovém objektu SceneBrep (Corner Table, lze ji načíst z OBJ formátu, ...).
Výpočet průsečíků je naivní, bez urychlovacích metod, není to tedy prakticky použitelný objekt.
BezierSurface
Síť bikubických Bézierových plátů. Ukázka optimalizovaného výpočtu průsečíků – pláty se dělí (subdivision) a výsledné
menší pláty jsou ukládány do R-tree pro větší efektivitu při hledání průsečíku.
Následuje několik základních implementačních tříd, většinou realizují nejjednodušší variantu daného interface.
Při rozšiřování ray-traceru je vhodné z jejich implementace vycházet, protože je přímočará a ověřená.
StaticCamera : ICamera
Nejjednodušší implementace generátoru primárních paprsků (interface ICamera).
Simulace ideálního objektivu pro středové promítání do rovinné průmětny (lineární perspektiva).
Geometrie je zadána středem projekce, směrem pohledu a Up vektorem. Dále je definován zorný úhel
pomocí třech na sobě závislých parametrů: Width, Height a AspectRatio.
Copyright (C) 2011-2020 J.Pelikán,
last change: 2024-02-26 01:42:34 +0100 (Mon, 26 Feb 2024)