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:

  1. 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
  2. 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: 2021-06-10 00:45:26 +0200 (Thu, 10 Jun 2021)