MASARYKOVA UNIVERZITA FAKULTA INFORMATIKY
Komponenty OpenGL pro vývojové prostředí Delphi Bakalářská práce Ondřej Sochora
Brno, 2010
Prohlášení Prohlašuji, že tato bakalářská práce je mým původním autorským dílem, které jsem vypracoval samostatně. Všechny zdroje, prameny a literaturu, které jsem při vypracování používal nebo z nich čerpal, v práci řádně cituji s uvedením úplného odkazu na příslušný zdroj.
Vedoucí práce: RNDr. Jaroslav Pelikán, Ph.D.
Shrnutí Tato práce pojednává o návrhu a tvorbě vizuálních komponent ve vývojovém prostředí Borland Developer Studio 2006, které implementují grafické rozhraní OpenGL a umožňují tak pracovat s trojrozměrnou grafikou. Součástí práce je přiložený CD disk, na kterém se nachází zdrojové kódy komponent a ukázkové aplikace, jež tyto komonenty používají. Příklady jsou napsány v jazyce Object Pascal a využívají grafické rozhraní OpenGL 1.5.
Klíčová slova: Delphi, tvorba komponent, OpenGL, Object Pascal
Obsah 1
Úvod......................................................................1 1.1 Přínos práce...............................................................1 1.2 Integrace s prostředím Delphi...................................2 1.2.1 Úvod do komponentní technologie ............2 1.2.2 Kroky k vytvoření komponenty...................2 1.2.3 Vlastnosti komponenty..................................3 1.2.4 Editory vlastností...........................................5 1.2.5 Editory komponent........................................7 1.2.6 Distribuce komponent...................................9
2
3
Kontext zařízení a rendering conext..............10 2.1 Funkcionalita na pozadí .........................................10 2.2 Kontext zařízení......................................................10 2.3 Rendering context...................................................11 2.4 Implementace kontextů v komponentě...................12
Kamera................................................................13 3.1 Integrace kamery do komponenty...........................13 3.2 Pohledové transformace..........................................13 3.3 Pohyb kamery ve scéně...........................................14
4 5
Objekty...............................................................15 4.1 Grafická primitiva..................................................16 4.2 Mesh objekty a jejich zobrazení..............................16
Světla a osvětlení...............................................19 5.1 Základní světelný model.........................................19 5.2 Typy světel..............................................................20
6
7
Materiály a textury............................................21 6.1 Specifikace materiálů..............................................21 6.2 Implementace materiálů v komponentě..................22 6.3 Specifikace textur....................................................22 6.4 Implementace textur v komponentě.......................23
Grafické efekty..................................................27 7.1 Mlha.......................................................................27 7.2 Antialiasing............................................................27 7.2.1 Objektový supersampling..........................27 7.2.2 Supersampling scény..................................28 7.2.3 Multisampling.............................................28 7.3 Míchání barev.........................................................28
8
Závěr.....................................................................30
Použitá literatura a zdroje.....................................31 Příloha A. ObsahCD...............................................33
Kapitola 1
Úvod 1.1 Přínos práce Cílem této práce je navrhnout a vytvořit komponenty pro vývojové prostředí Borland Developer Studio 2006, které pracují s grafickým rozhraním OpenGL[1]. Úkolem práce je přinést uživateli sadu ukázkových komponent, s jejichž pomocí bude schopen vytvářet jednoduché objekty, umísťovat je do scény a celou scénu vykreslit. Tyto komponenty abstrahují složitost a komplexitu rozhraní OpenGL a poskytují jednoduchý a intuitivní přístup k tvorbě aplikací zobrazujicích trojrozměrnou grafiku. Od uživatele se předpokládá základní znalost jazyka Object Pascal a jistá znalost rysů, které OpenGL nabízí. Nemusí však vědět detaily implementace OpenGL jako parametry a názvy funkcí, ale postačí si s takovými vědomostmi jako např. které parametry ovlivňují výsledný vzhled mlhy, jaký je rozdíl mezi bodovým, směrovým a reflektorovým světlem apod. Text zahrnuje vývoj dvou komponent, a to GLViewer a ObjectScene. GLViewer simuluje kameru a práci s ní, přičemž do sebe integruje různé funkcionality (kosoúhlé projekce, pohyb, rotace) a efekty (mlha, antialiasing). ObjectScene je komponenta zapouzdřující mechanismus, který umožňuje tvorbu grafických prvků (primitiv, meshů, skyboxu), jejich následnou editaci (definice materiálů a textur) a definuje scénu a její nastavení (face culling, stínování, zapnutí osvětlení aj.). Každému objektu (výjimku tvoří dvojrozměrné elementy jako body, čáry a světla) lze posléze přiřadit materiál a texturu, objektům bez materiálů lze nastavit barvu. Výše zmíněné komponenty uživateli nabízí:
tvorbu scény a umístění objektů do scény
možnost objektům nadefinovat materiály a textury
přiřazení kamery scéně a následný pohyb kamery
grafické efekty (mlha, antialiasing)
načítání externích 3D souborů (3DS, OBJ)
celoobrazovkový režim (fullscreen) a ukládání obrazovky do bitmapy
tvorbu a nastavení světel
generování terénu a venkovního prostředí (terrain, skybox)
spolu s dalšími komponentami, které vývojové prostředí obsahuje (především TTimer), vytvořit jednoduché animace
-1-
1. ÚVOD
1.2 Integrace s prostředím Delphi V prostředí Delphi se komponentou rozumí základní stavební prvek každé vizuální aplikace (nikoliv konzolové aplikace1). Komponenty mohou být dvojího typu – vizuální a nevizuální. Vizuální komponenty jsou například tlačítka, přepínače, formuláře (známé windowsovské okno), menu, popisky, textová pole, dialogy a mnoho dalších a jsou v době návrhu (designtime) i v době běhu (runtime) aplikace viditelné a uživatel může s nimi přímo interagovat. Nevizuální komponenty jsou například klienti FTP připojení, správci různých databázových nebo síťových připojení. Tyto komponenty jsou viditelné pouze v době návrhu aplikace a v době běhu s nimi uživatel může interagovat pouze nepřímo (pomocí ovládacích prvků, které vyvolají činnost dané komponenty).
1.2.1 Úvod do komponentní technologie Samotný návrh aplikace je tvořen metodou umístěním konkrétní komponenty na formulář a nastavením jejích vlastností, tzv. properties v Object Inspectoru. Object Inspector je okno, respektive správce komponent, ve kterém se vlastnosti komponenty nastavují. Vlastnosti definují vzhled a chování komponenty, typicky velikost, barvu, typ písma, styl, okraje, viditelnost, atributy spojené s dědičností apod. Změny vlastností mohou být na komponentě okamžitě vidět, nebo se mohou projevit až v době běhu. V Object Inspectoru jsou viditelné pouze ty vlastnosti, které komponenta obsahuje ve svém zdrojovém kódu v sekci published. Vlastnosti komponenty mohou být i v sekci public, avšak ty jsou dostupné pouze programově v době běhu. Kromě vlastností mohou být komponentě nastaveny i události, tzv. events, pokud komponenta nějaké má. Pro nějakou událost (třeba přejetí kurzoru myši přes komponentu) se může přiřadit obslužná metoda, která se při vyvolání události provede. Prostředí Delphi používá vlastní komponentní technolgii VCL (visual component library), která má za úkol značně zjednodušit volání složitých funkcí Windows API (programové rozhraní, umožňující používat funkce systému MS-Windows a vytvářet uživatelské rozhraní). Tato knihovna v sobě nese standardní vizuální komponenty, pomocí kterých lze vytvářet grafické uživatelské rozhraní aplikace (GUI – graphical user interface) a dále pak přináší mnoho nevizuálních komponent pro vývoj a návrh databází, síťových aplikací a jejich rozhraní. Knihovna VCL též umožňuje vytvářet nové komponenty nebo modifikovat stávající.
1.2.2 Kroky k vytvoření komponenty Při vytváření nové komponenty (nebo modifikaci stávající) je nutné dobře znát objektové koncepty Delphi, mít alespoň základní povědomí o struktuře a hierarchii knihovny VCL a dodržovat zaběhnuté konvence (to samozřejmě platí nejen při návrhu komponent, ale pro psaní jakéhokoliv kódu). Dále je vhodné mít určitou představu, co by komponenta měla a neměla nabízet, které vlastnosti komponenta mít nebude. Tento aspekt je důležitý zejména z hlediska vhodného výběru rodičovské třídy. 1 Konzolová aplikace je aplikace podobná programu Příkazový řádek systému MS-Windows. Ačkoli na první pohled se může jevit jako vizuální (není neviditelná, uživatel ji vidí), za vizuální aplikaci se považuje typická okenní aplikace s tlačítky, přepínači, menu apod.
-2-
1. ÚVOD
Je důležité mít na paměti, že komponenty se tvoří děděním od zvolené rodičovské třídy. Komponenta tedy specializuje rodičovskou třídu, avšak zároveň ji rozšiřuje. Při odvození nové třídy není možné některou vlastnost či metodu předka zrušit, vyjmout z deklarace nebo nadále zapomenout. Dědičnost pouze umožňuje přidat k atributům a metodám předka nové atributy a metody a je tedy důležité vybrat takovou rodičovskou třídu, která obsahuje pouze nezbytné funkcionality. V opačném případě bude nová komponenta obsahovat zbytečné funkce, které nikdy nevyužije a celková velikost kódu bude zbytečně narůstat. Ke zvolení rodičovské třídy a vytvoření jednotky (souboru .PAS) je možné použít správce komponent, který vytvoří základní kód nové komponenty. Další nezbytný krok je vytvoření tzv. balíku. Balík (package) je určitý druh projektu Delphi; podobně jako při návrhu aplikace je vytvořen zdrojový soubor projektu (soubor .DPR – delphi project), tak i při návrhu balíku je vytvořen podobný soubor (.DPK – delphi package). Pro každý balík je vytvořen práve jeden tento soubor. Balíky jsou rozděleny na dvě části – contains a requires. V části contains jsou přítomny zdrojové soubory komponenty (soubory .PAS), případně ikony a zdroje (soubory .DCR). Část requires obsahuje reference na další externí balíky, které komponenta pro svoji funkčnost vyžaduje. Pokud bude vytvářená komponenta velice jednoduchá, balík sekci requires nemusí obsahovat, nicméně ve většině případů komponenty bývají robustní a vizuálního charakteru a balík sekci requires obsahuje. V případě popisovaných komponent (GLViewer a ObjectScene) balík DelphiOpenGL.dpk sekci requires vyžaduje a ta obsahuje reference na balíky vcl.dcp, rtl.dcp, vcljpg.dcp a designide.dcp. Přítomnost balíků vcl.dcp a rtl.dcp je nezbytná, protože komponenta bude vizuální a bude určena pro dobu návrhu i dobu běhu (balík DelphiOpenGL bude návrhový i běhový). Balík vcljpg.dcp umožňuje pracovat s rastrovými soubory JPG (práce s nimi je možná i bez tohoto balíku, nicméně balík vcljpg.dcp práci s nimi velice usnadňuje). Protože komponenta ObjectScene obsahuje několik editorů vlastností a editorů komponent (jsou popsány v podkapitole 1.2.4 a 1.2.5), musí zde být zahrnuta i reference na balík designide.dcp.
1.2.3 Vlastnosti komponenty Podle definice vlastnosti (properties) představují rozhraní k interním datovým polím komponenty[2]. Znamená to tedy, že k soukromým atributům lze přistupovat pomocí tohoto rozraní (myslí se tím pouze technický termín, nezaměňovat s rozhraním, jaké používá např. jazyk Java) a třída má tak plně pod kontrolou, jakým způsobem lze data modifikovat a může ošetřit neplatné hodnoty a vyhnout se tak nekonzistentnímu stavu, do kterého by se třída (resp. její instance) mohla dostat. Pokud mají být vlastnosti viditelné v Object Inspectoru, musí být deklarovány v sekci published, jinak se mohou nacházet i v sekci public, ale Object Inspector je nezobrazí. Obecný tvar deklarace vlastnosti je property propertyName[indexes]: type index integerConstant specifiers;
-3-
1. ÚVOD
Vlastnost se deklaruje klíčovým slovem property. Za ním následuje jméno vlastnosti, tak jak bude zobrazeno v Object Inspectoru. Pokud bude vlastnost typu pole, pak následují indexy prvků oddělené čárkou, podle kterých bude komponenta číst nebo zapisovat příslušné hodnoty. V případě, že vlastnost pole není, indexy nejsou přítomny. Další povinný atribut vlastnosti je její datový typ. Typ vlastnosti může být číselný, řetězcový, enumerační (výčtový), možina, jiná komponenta nebo typ TPersistent. Specifikátor index integerConstant umožňuje několika dalším vlastnostem sdílet tutéž přístupovou metodu. Specifikátory jsou sekvence klíčových slov read, write, stored, default (popř. nodefault) a implements. Specifikátory read, resp. write stanovují, co nastane, je-li hodnota vlastnosti čtena, resp. zapisována. Za specifikátorem read může následovat privátní atribut třídy nebo název funkce, která vrací hodnotu privátního atributu, za specifikátorem write může být uveden opět privátní atribut nebo procedura, která nastavuje hodnotu tohoto atributu. Obě metody musí být kompatibilní s typem dané vlastnosti, zápisová procedura bere pouze jeden argument (pokud se jedná o sdílenou proceduru, pak má argumenty dva – další argument je index do pole, do kterého má být hodnota zapsána). Každá definice vlastnosti musí obsahovat alespoň jeden z těchto dvou specifikátorů. Zbytek specifikátorů je nepovinný, pouze stručně můžeme některé zmínit, jakou hrají roli při vývoji komponenty. Specifikátor default (nodefault) – při ukládání projektu prostředí Delphi zapisuje do souboru .DFM hodnoty vlastností komponenty, ale jen v tom případě, pokud se hodnota vlastnosti liší od implicitní. Pokud se neliší, Delphi nic neukládá a při následném načtení souboru .DFM se použije právě hodnota za specifikátorem default. Direktiva nodefault ruší ukládání hodnot zděděných vlastností. Pokud komponenta vyžaduje, aby se konkrétní zděděná vlastnost neukládala, použije se tento specifikátor a vlastnost tedy nebude mít při zpětném načtení projektu implicitní hodnotu. Specifikátor stored – za ním následuje logická hodnota (mohou to být i číslice 0 a 1) nebo funkce, jejíž návratová hodnota je typu Boolean. Implicitně je nastaven na hodnotu true (i když není uveden). V případě, že je nastaven na hodnotu false, hodnota vlastnosti se nikdy do souboru .DFM neuloží. Všechny nové vlastnosti komponenty (ty, které nejsou zděděny od rodičovské třídy) se v Object Inspectoru nachází v kategorii Miscellaneous (česky různé). Lze ale zajistit, aby se pro nové vlastnosti vytvořily v Object Inspectoru nové kategorie nebo vlastnost přesunout do jiné, která už v Object Inspectoru je. Object Inspector je pak více přehledný a vlastnosti jsou umístěny v takových kategoriích, kde je uživatel může snáze najít. K tomu slouží procedura RegisterPropertyInCategory(categoryName: string; propertyName: string);, kde první parametr metody představuje název nové kategorie (nebo nějaké stávající, pokud vlastnost pouze přesunujeme) a druhý parametr je název vlastnosti, pro kterou je nová kategorie generována. Obdobně existuje i metoda pro registraci několika vlastností současně.
-4-
1. ÚVOD
1.2.4 Editory vlastností Některé komponenty mohou mít takové speciální vlastnosti, jež neodpovídají žádným datovým typům (čísla, řetězce, persistentní třídy), které je schopen Object Inspector zobrazit. V takovém případě je dobré a někdy i nutné vytvořit speciální editor vlastnosti, který bude umožňovat danou složitější vlastnost modifikovat. Editory vlastností jsou rozšířením Object Inspectoru a v podstatě oznamují Object Inspectoru, jak má zacházet s danou vlastností. Příkladem takového editoru může být vlastnost barvy pozadí formuláře (Color). Jedná se sice o výčtový typ, který Object Inspector normálně zobrazuje, avšak na rozdíl od běžného výčtového typu zobrazuje i danou barvu a to už běžný výčtový typ neumí. Ve většině případů není nutné psát editor vlastnosti úplně od základů – Delphi nabízí velké množství předdefinovaných editorů a stačí vybrat ten, který umí pracovat s daným datovým typem vlastnosti, pro kterou je editor vytvářen a přepsat některé z jeho metod. Všechny editory vlastností jsou potomky třídy TPropertyEditor, jež se nachází v modulu DesignIntf.pas. Protože Borland Developer Studio 2006 neobsahuje binární soubor tohoto modulu (důvody nejsou známé, ačkoli starší verze Delphi tyto soubory obsahují), je zapotřebí v balíku, ve kterém je komponenta vyvíjena, do sekce requires přidat referenci na balík designide.dcp. Všechny vlastnosti, které se v Object Inspectoru nachází, jsou spravovány patřičným editorem – např. výčtové typy jsou spravovány editorem TEnumProperty, vlastnost typu množina má přiřazen editor TSetProperty, číselné hodnoty s plovoucí desetinnou čárkou jsou zase pod správou editoru TFloatProperty apod. Nyní si krátce popíšeme vývoj editoru vlastnosti Model třídy TDGLMesh, pomocí které uživatel vybere soubor s objektem. Samotná vlastnost je řetězcového typu, ale pro pohodlný výběr v adresářové struktuře disku je pro ni vytvořen editor v podobě dialogového okna. Třída editoru je nazvána T3DFileProperty a je potomkem editoru TStringProperty. Pro potřeby našeho editoru stačí přepsat implenetaci dvou metod – funkce GetAttributes: TPropertyAttributes a procedury Edit. Kód třídy je následující: T3DFileProperty = class(TStringProperty) public function GetAttributes: TPropertyAttributes; override; procedure Edit; override; end; Funkce GetAttributes: TPropertyAttributes; určuje druh vlastnosti a její chování. Existuje několik možností, jak bude hodnota ve vlastnosti zobrazena. Implicitně je zobrazována jako editační pole (např. vlastnost Caption), ale může být i stylu paReadOnly (je zakázáno obsah vlastnosti měnit), paValueList (zobrazí se jako výčtový typ (např. vlastnost FormStyle) a další. Pro účely popisovaného editoru je zvolen styl paDialog.
-5-
1. ÚVOD
Procedura Edit nastane tehdy, když uživatel v Object Inspectoru bude chtít vybrat nějaký soubor. Podobně jako u vlastnosti Font, i u vlastnosti Model je při jejím zaměření zobrazeno tlačítko se třemi tečkami ('…'). Po kliknutí na něj se otevře požadované dialogové okno a uživatel může zvolit konkrétní soubor. Po zvolení souboru je ve vlastnosti uložena cesta k souboru. Implementace obou metod: function T3DFileProperty.GetAttributes: TPropertyAttributes; begin //paDialog určuje, že vlastnost bude používat dialogové okno Result := [paDialog]; end; procedure T3DFileProperty.Edit; begin with TOpenDialog.Create(Application) do try //zobrazí název vlastnosti v titulku dialogu Title := GetName; Filename := GetValue; //maska filtru, podle které se budou v dialogu zobrazovat //pouze soubory s danou příponou Filter := 'Mesh files (*.obj, *.3ds)|*.OBJ;*.3DS|LightWave OBJ (*.obj)|Autodesk 3DS (*.3ds)|*.3DS'; HelpContext := 0; //pokud soubor nebo cesta k němu nebude existovat, zobrazí se //chybová hláška Options := Options+[ofShowHelp,ofPathMustExist,ofFileMustExist]; if Execute then SetValue(Filename); finally free end end; Metody GetName: string;, GetValue: string; a SetValue(const Value: string); jsou definované ve třídě TPropertyEditor. Aby byl nový editor vlastnosti připraven k použití, musí se zaregistrovat do vývojového prostředí v registrační proceduře Register. To se provede metodou RegisterPropertyEditor(PropertyType: PTypeInfo; ComponentClass :TClass; PropertyName :string; EditorClass :TPropertyEditorClass);. První argument procedury je ukazatel na typ třídy vlastnosti, pro kterou je editor navržen. Druhý argument je třída komponenty, jejíž vlastnost bude tento editor používat. Další argument je název této vlastnosti a poslední argument je třída editoru. -6-
1. ÚVOD
Tedy v případě popisovaného editoru bude funkce vypadat takto: RegisterPropertyEditor(TypeInfo(TModelName),TDGLMesh, 'Model', T3DFileProperty);
1.2.5 Editory komponent Editory komponent jsou podobné editorům vlastností – rozšířují a obohacují vývojové prostředí. Stejně jako editory vlastností jsou editory komponent odvozeny od jediné mateřské třídy, které je potřeba přepsat a předefinovat některé abstraktní a virtuální metody k dosažení požadovaného chování editoru. Jsou přiřazeny konkrétnímu typu komponenty a jsou většinou vyvolány kliknutím na komponentu pravým tlačítkem myši (když se nachází na formuláři). Způsob aktivace editoru komponenty je mírně odlišný od způsobu aktivace editoru vlastnosti, ale návrh takového editoru je víceméně stejný. Editor komponenty je vytvořen (na základě typu komponenty) vždy, když je komponenta vybrána z palety komponent. Při dvojitém kliknutí je zavolána metoda Edit. Když je vyvoláno kontextové menu komponenty, jsou zavolány metody GetVerbCount a GetVerb, které způsobí přidání položek do kontextového menu. Pokud je vybrána nějaká položka v menu (která patří dané komponentě), zavolá se metoda ExecuteVerb. Editory komponent je dobré vytvářet tehdy, když je potřeba přidat položky do kontextového menu nebo změnit chování při poklikání na komponentu. Příklad editoru lze spatřit po kliknutí pravým tlačítkem myši na komponentu TPopupMenu. Rodičovská třída všech editorů komponent se jmenuje TComponentEditor a je nezbytné přidat do balíku, ve kterém se komponenta nachází referenci na balík designide.dcp. Komponenta ObjectScene má také svůj editor a nyní si jej stručně popíšeme. Třída editoru se jmenuje TSceneEditor a je potomkem třídy TComponentEditor. Třída samotná redefinuje 4 metody, zmíněné v předchozím odstavci. Kód třídy vypadá takto: TSceneEditor = class (TComponentEditor) public procedure Edit; override; procedure ExecuteVerb(Index: Integer); override; function GetVerb(Index: Integer): String; override; function GetVerbCount: Integer; override; end; procedure TSceneEditor.Edit; begin {po provedení metody Edit se k přidávání komponent grafických with ObjectSceneEditorForm do SetScene(Self.Component as Show; end; end;
zobrazí formulář, který slouží objektů a jejich správě } begin TObjectScene, Self.Designer);
-7-
1. ÚVOD
Procedura Edit je zavolána prostředím Delphi vždy, když uživatel dvakrát klikne na komponentu. Mnoho komponent používá tuto metodu k namapování události OnClick, zatímco například třída TForm používá tuto metodu k namapování události OnCreate. procedure TSceneEditor.ExecuteVerb(Index: Integer); begin case Index of 0 : Edit; end; end; Procedura ExecuteVerb je vyvolána vždy, když uživatel klikne na jednu z položek v kontextovém menu. function TSceneEditor.GetVerb(Index: Integer): String; begin case Index of 0 : Result:='Show Scene Editor'; end; end; Funkce GetVerb se provede pro každou položku menu komponenty. Význam této funkce spočívá v oznámení Delphi, které položky se v kontextovém menu mají zobrazit. function TSceneEditor.GetVerbCount: Integer; begin Result:=1; end; GetVerbCount je provedeno prostředím, kdy je zapotřebí zjistit, kolik položek má být přidáno do kontextového menu komponenty. Stejně jako editor vlastností je nutné editor komponenty zaregistrovat do prostředí funkcí RegisterComponentEditor(ComponentClass: TComponentClass; ComponentEditor: TComponenEditorClass);, kde první argument je třída, pro kterou je editor registrován, druhý argument je třída editoru. Pro rozebíraný editor bude mít funkce argumenty RegisterComponentEditor(TObjectScene, TSceneEditor);. Komponenty se sdružují do skupin a jsou dostupné v paletě komponent. Kupříkladu nabídka Standard obsahuje nejzákladnější a nejpoužívanější, jako třeba tlačítko (button), textové pole (edit), popisek (label), rozbalovací seznam (combobox) nebo přepínač (radiobutton), nabídka Dialogs pro změnu nabízí dialogy pro výběr barvy, stylu písma, dialog pro otevření souboru či jeho uložení, nebo dialog pro nastavení tiskárny a tisku. Pro to, aby se komponenta mohla zobrazit v paletě komponent, je zapotřebí ji za-8-
1.ÚVOD
registrovat do vývojového prostředí. To má na starost procedura RegisterComponent(const Page: string; ComponentClass: TComponentClass);, jejíž první argument značí název skupiny v paletě a druhý argument je třída komponenty, kterou registrujeme. Všechny třídy editorů vlastností a komponent, funkce pro registraci jednotlivých komponent, jejich vlastností a funkce pro roztřídění vlastností do kategorií jsou definovány v modulu IDEIntegration.pas.
1.2.6 Distribuce komponent Soubor .DPK je pouze textový popis projektu balíku. Aby se komponenty v něm obsažené mohly používat a přenášet od autora k uživateli, musí se balík přeložit do binární podoby. Delphi kompilací vytvoří dva binární soubory, konkrétně .BPL a .DCP. Soubor .DCP obsahuje hlavičku balíku a spojení všech .DCU souborů v balíku (DCU je binární forma .PAS souboru). K tomuto souboru musí mít prostředí Delphi přístup, aby později mohlo přeložit aplikace využívající komponenty z daného balíku. Soubor .BPL je velice podobný dynamicky linkovaným knihovnám (.DLL), avšak se speciálními vlastnostmi, které jej předurčují pro použití v aplikacích Delphi (nebo Borland C++ Builderu). Je to v podstatě vlastní běhový i návrhový balík a Delphi musí k němu mít přístup. Soubor .BPL i .DCP je pro každý balík vytvořen právě jeden a má shodný název s projektem balíku (souborem .DPK). Pokud autor komponent má v úmyslu své komponenty nabízet za účelem zisku a nechce zveřejňovat zdrojové kódy, stačí koncovým uživatelům dát k dispozici pouze tyto dva binární soubory. V případě, že se jedná např. o projekt s otevřeným kódem, stačí nabídnout textorvý soubor .DPK se zdrojovými .PAS soubory a koncový uživatel je schopen si už balík sám přeložit a nainstalovat.
-9-
Kapitola 2
Kontext zařízení a rendering context Každá vizuální komponenta je zároveň okenním prvkem. Nejedná se o klasické okno s titulkovým pruhem a ikonami pro minimalizaci, maximalizaci a uzavření okna, ale má stejné nebo podobné atributy a charakteristiky. Typické okno, známé prakticky ze všech desktopových aplikací, je pouze speciální případ okenní komponenty. Prvotní návrh komponenty, která zobrazuje výstup OpenGL, by mohl souviset s použitím knihovny GLUT, která obsahuje funkce pro vytvoření plnohodnotného okna s už nastavenými kontexty (viz podkapitola 2.2). Vzhledem k faktu, že vytvářená komponenta GLViewer nebude ve formě takového okna, musí se vykreslování OpenGL nastavit ručně.
2.1 Funkcionalita na pozadí Všechny vizuální komponenty mají tzv. popisovač [3] (handle) – jednoznačný identifikátor, pomocí kterého můžeme s komponentou manipulovat, odkazovat se na ni apod. Delphi při běžné práci uživatele od složitého odkazování pomocí popisovače chrání, nicméně tvorba nových komponent znalost práce s popisovačem vyžaduje. Další významnou část vizuálních komponent tvoří vykreslovací plocha (canvas). Je to abstrakce malířského plátna, plochy, kam komponenta vykresluje svůj obsah. Samotné vykreslení různých grafických primitiv, jako například čar, elips, n-úhelníků, neprovádí komponenty přímo, nemají tedy přímý přístup k hardwaru. Toto vše zajišťuje GDI (Graphics Device Interface), což je aplikační programové rozhraní (zkratka API) platformy MS-Windows, kterému je přístup k hardwaru umožněn. V našem případě bude GDI z velké části nahrazeno OpenGL, protože GDI není uzpůsobeno pro vykreslování trojrozměrné grafiky.
2.2 Kontext zařízení Aby komponenta mohla něco vykreslit, musí se jí nastavit kontext zařízení (Device Context, zkráceně DC). Pomocí kontextu zařízení se nasměruje výstup z GDI do vykreslovací plochy komponenty (canvasu). Kontext zařízení mimo jiné také definuje různé atributy textů a obrázků, které jsou zobrazovány dále na obrazovce nebo tisknuty tiskárnou. Komponenta GLViewer je potomek třídy TCustomControl[4], ze které dědí některé důležité rysy. Jeden z významnějších je, že se nemusíme starat o samotné vytvoření kontextu zařízení. V opačném případě bychom jej museli vytvořit sami. Před výstupem do komponenty (nebo jiného výstupního zařízení) musíme získat od GDI právě aktuální kontext. To provede funkce
function GetDC(handle: HWND): HDC, kde se jí jako parametr předá výše zmíněný popisovač okna, resp. komponenty.
-10-
2. KONTEXT ZAŘÍZENÍ A RENDERING CONTEXT
V době, kdy už kontext zařízení není potřeba, jej dealokujeme funkcí
function
ReleaseDC(handle:
HWND;
DC:
HDC):
integer,
kde se v argumentu handle funkci předá popisovač okna a v argumentu DC identifikátor kontextu zařízení, který byl před tím obdržen funkcí GetDC(handle: HWND). Uvolnění kontextu není vždy nezbytné, odvíjí se od typu kontextu. Je ale vhodné jej implicitně uvolňovat. Pokud by později došlo ke změně typu kontextu, který by se nedealokoval, mohlo by dojít ke značné spotřebě paměti nebo k jiným, závažnějším chybám.
2.3 Rendering context Další fáze návrhu komponenty spočívá v propojení OpenGL s operačním systémem a přesměrování výstupu OpenGL, tzv. rendering contextu, na dříve získaný kontext zařízení. Toto propojení umožní zachytit výstup OpenGL a v případě vícevláknové aplikace můžeme odlišit výstup pro jednotlivá vlákna. Ačkoli je knihovna OpenGL platformově nezávislá, je přesměrování jejího výstupu platformově závislé. Každý operační systém obsahuje nadstavbovou knihovnu, která propojení umožňuje. V MS-Windows je to knihovna WGL, v Linuxu knihovna GLX nebo v operačním systému Mac OS X je to knihovna AGL či NSOpenGL. Protože tato práce pojednává o vývoji v operačním systému MS-Windows, je použita knihovna WGL. Nejdůležitější funkce: function wglCreateContext(hDC: HDC): HGLRC Tato funkce nad kontextem zařízení vytvoří OpenGL rendering context. Jako parametr bere kontext zařízení, který vrátila funkce GetDC(handle: HWND). function wglDeleteContext(hRC: HGLRC): boolean Podobně jako kontext zařízení je dobré i rendering context uvolnit, jakmile s ním není potřeba dále pracovat. Parametr funkce je konkrétní rendering context připravený k dealokaci, který funkce wglCreateContext(hDC: HDC): HGLRC předtím vrátila. function wglMakeCurrent(hDC: HDC; hRC: HGLRC): boolean Funkce nastavuje kontextu zařízení aktuální rendering context. První parametr funkce je kontext zařízení (získaný funkcí GetDC), na který je třeba napojit dříve vytvořený rendering context (druhý parametr funkce). V případě, že aktuální rendering context už není potřeba, stejná funkce oddělí rendering context od kontextu zařízení. To se provede tak, že oba argumenty funkce budou nula – wglMakeCurrent(0,0). -11-
2. KONTEXT ZAŘÍZENÍ A RENDERING CONTEXT
Nastavení obou kontextů nesmí být provedeno v konstruktoru komponenty, protože by vznikla chyba (kontexty by se vytvořily pro neexistující objekt). Z tohoto důvodu je vytvoření kontextů provedeno až po inicializaci v proceduře CreateWnd, která se automaticky volá až po vytvoření instance komponenty GLViewer. Podobným mechanismem je zpracována i následná dealokace obou kontextů. Při zániku objektu (komponenty), přesněji těsně po jeho odstranění systémem z obrazovky, objekt pošle zprávu WM_DESTROY [5], kterou zachytí metoda WMDestroy, ve které se oba kontexty uvolní. Mimo jiné, kontextu zařízení se mohou nastavit různé parametry, jako například barevná hloubka, hloubka z-bufferu, počet bitů akumulačního nebo stencil bufferu, pak to, zda bude podporován stereo rendering nebo dvojitý framebuffer. Tato nastavení poté ovlivní i chování rendering contextu.
2.4 Implementace kontextů v komponentě Tvorba a správa obou kontextů je plně v režii komponenty GLViewer a uživatel se tedy nemusí o nic starat. Samotný mechanismus vytvoření a nastavení rendering contextu je zapouzdřen ve třídě TContextBuffer, která má na starosti nastavení palety, nastavení parametrů pixelformátu (struktura, která v sobě uchovává specifická nastavení rendering contextu jako například počet bitů barevné hloubky, počet bitů hloubkového, akumulačního nebo stencil-bufferu a mnohé další). Vytvořená instance třídy TContextBuffer se následně musí předat komponentě ObjectScene, která jej vyžaduje pro správné zobrazení a nastavení několika parametrů objektů ve scéně (pokud se ve scéně už nachází). Mimo jiné, třída TContextBuffer umožňuje nastavení několika vlastností scény (nemyslí se teď vlastnosti jako property, které jsou viditelné v Object Inspectoru, ale parametry, které ovlivňují celkový vzhled scény). Tyto vlastnosti se povětšinou týkají modelu osvětlení (ambientní barva scény, osvětlení odvrácených stran polygonů apod., viz kapitola 5). Dále umožňuje nastavit typ stínování (konstantní a Gouraudovo), zapnutí nebo vypnutí osvětlování scény (zapne nebo vypne všechna světla ve scéně v jednom okamžiku) nebo face culling (technika, kdy polygony objektu, které jsou překryty jinými polygony téhož objektu se nevykreslí – například zadní plošky koule, které jsou překryty ploškami předními. To umožňuje snížit náročnost scény na vykreslování).
-12-
Kapitola 3
Kamera Kamera je velice důležitým prvkem v 3D grafice, protože poskytuje pohled na scénu. Jinými slovy, převádí trojrozměrný pohled na dvojrozměrný, který lze dále už jednoduše zobrazit na nějakém výstupním zařízení. V OpenGL objekt podobný kameře neexistuje, existuje pouze kolekce projekcí a transformací, vhodně uspořádaných za sebou, která simuluje pohled kamerou.
3.1 Integrace kamery do komponenty Protože OpenGL žádnou kameru neobsahuje, byla v rámci komponenty GLViewer vytvořena abstrakce kamery, která má zjednodušit uživateli práci s projekcemi a transformacemi, které kamera používá. Kamera není samostatná komponenta, ale je to vlastnost komponenty GLViewer (třída TCamera). Kamera má několik vlastností, které umožňují:
nastavení pozice a orientace kamery
nastavení záběru (field of view) pro perspektivní projekci
nastavení hodnot přední a zadní ořezové roviny
nastavení antialiasingu (2×, 4×, 8×, 16×; více v kapitole 7)
výběr z mnoha typů projekcí (perspektivní, ortografická, tři axonometrické)
zapnutí/vypnutí mlhy, nastavení jejích parametrů
funkce pro posun a rotaci kamery
zafixovat směr pohledu na nějaký objekt, který se nachází ve scéně
3.2 Pohledové transformace Třída TCamera dává uživateli na výběr z pěti různých typů projekcí. Mimo běžnou perspektivní a ortografickou projekci, které OpenGL má již v sobě implementované, si uživatel může zvolit ze tří axonometrických, a to izometrickou, kavalírní a kabinetní. Izometrická projekce je vytvořena běžnou ortografickou projekcí a umístěním kamery do pozice [x,y,z] = [1,1,1] s pohledem do počátku soustavy souřadnic. Kavalírní a kabinetní projekce je zajištěna přinásobením vhodné matice o rozměru 4×4 funkcí glMultMatrixf(const m: PGLfloat) k ortografické projekci. Obecná matice kosoúhlé projekce promítání do roviny xy vypadá takto:
1 0 0 0
0 Kcos 1 Ksin 0 1 0 0
0 0 0 1
, kde K je koeficient zkrácení a α je úhel průmětu osy z
-13-
3. KAMERA
obr. 1 - kabinetní projekce
obr. 2 - kavalírská projekce
Pro obě kosoúhlé projekce je úhel α roven 30°, v případě kabinetní projekce je K rovno jedné polovině.
3.3 Pohyb kamery ve scéně Komponenta GLViewer nabízí uživateli metody, které umožňují pohybovat s kamerou ve scéně. Tyto metody aplikují na kameru základní lineární a afinní transformace jako např. pohyb vpřed, vzad a stranou, rotace se středem v pozici pozorovatele a v pozici cíle, kam se kamera dívá. Navíc poskytuje funkci pro přiblížení/oddálení scény (zoom). Protože výpočty transformací v pohyblivé řádové čárce jsou náročné, je drtivá většina metod napsána v jazyku symbolických adres (JSA, angl. assembly language), který maximalizuje rychlost výpočtů. Tento přístup lze jednoduše realizovat, protože překladač Delphi obsahuje vestavěný překladač tohoto jazyka (JSA) a assemblerovské instrukce lze psát přímo do těla pascalovské funkce a navíc lze přímo použít argumenty funkce či globální proměnné, vyskytující se v jednotce. Jednotka s těmito funkcemi je volně k dispozici v rámci projektu JEDI [6]. Stručný popis nejdůležitějších funkcí pro práci s kamerou: procedure Move(x,y,z:single); Metoda posune kameru na zadané souřadnice. procedure Move(distance: single); Metoda posune kameru ve směru pohledu. procedure Strafe(distance: single); Metoda posune kameru do stran. procedure Zoom(amount :single); Metoda přiblíží pohled kamery. procedure Pitch(angle: single); Metoda otočí kameru kolem osy x. procedure Roll(angle :single); Metoda otočí kameru kolem osy z. procedure Turn(angle: single);
Metoda otočí kameru kolem osy y.
-14-
Kapitola 4
Objekty Komponenta ObjectScene je navržena tak, aby uživatel mohl jednoduše vytvářet objekty, které se budou následně zobrazovat v komponentě GLViewer. Nadřazená třída všech objektů scény se nazývá TBasicObject. Obsahuje metody a funkce, které poskytují možnost manipulace s objekty v prostoru (posun, rotace, změna měřítka, generování matice transformací a jají inverze). Tato třída dále definuje základní atributy, jako souřadnice pozice, hodnoty rotace a směr up vektoru (vektor, který určuje, jaká rovina znamená pro objekt směr „nahoru“) a směr vzhledem k ose z. Tato třída je fundamentální pro všechny objekty, z ní je přímo odvozena třída světel. Objekty v komponentě ObjectScene se dále dělí na ty, které mohou mít materiál a na ty, které materiál mít nemohou (více o materiálech v kapitole 6). Převážná většina objektů nacházející se v komponentě ObjectScene materiál má. Jedná se o běžná prostorová primitiva jako rovina, krychle, koule, válec, torus apod. Na druhé straně světla, body a přímky definovaný materiál nemají. Za tímto účelem jsou z třídy TBasicObject odvozeny třídy TMaterialObject a TImmaterialObject. Třída TMaterialObject definuje a popisuje ty objekty, které materiál mít mohou. V podstatě přidává vlastnost Material a zviditeňuje některé vlastnosti pro Object Inspector, které v nadřazené třídě byly typu public a tudíž by se v Object Inspectoru nezobrazovaly. Analogicky třída TImmaterialObject je mateřská třída pro objekty, které materiál mít nemohou. Těmto objektům lze nastavit alespoň barvu. Všem objektům je možno zobrazit jejich lokální osy, které mají za úkol usnadnit orietnaci uživateli v prostoru. Mateřská třída objektů (TBasicObject) je potomkem třídy TComponent a tedy s každým objektem je možné pracovat jako s běžnou vizuální komponentou – lze jí nastavovat vlastnosti v Object Inspectoru. Díky faktu, že třída TComponent je potomkem třídy TPersistent, prostředí Delphi automaticky pomocí své serializační technologie ukládá vlastnosti každého objektu (komponenty) do souboru .DFM. Soubory .DFM slouží k popisu vzhledu formuláře (jeho vlastností), k popisu všech komponent, které se na něm nachází a k definici vztahů mezi komponentami a formulářem. Pro každý formulář je vytvořen jeden .DFM soubor. Prostředí Delphi používá svou vlastní serializační technologii k ukládání stavu komponent právě do tohoto souboru. Serializace je technika, kdy je instance objektu, tak jak je uložena v paměti, převedena na posloupnost bitů, kterou je dále možné uložit na nějaké pevné datové úložiště (na pevný disk nebo jiné médium) a z tohoto úložiště ji zpětně zrekonstruovat do původní podoby. V tomto případě jakákoli instatnce třídy TPersistent a jejích potomků, tedy i komponent, má schopnost být uložena serializační technologií Delphi a zpětně z tohoto souboru opět načtena a vytvořena. Tímto způsobem je velice jednoduše a elegantně dosaženo toho, že po otevření daného projektu aplikace jsou všechny komponenty instanciovány a ihned použitelné a uživatel (programátor) nemusí vytvářet vlastní serializační funkce (samozřejmě mu v tom nic nebrání). Ze stejného důvodu jsou i grafické objekty komponenty ObjectScene navrženy jako vizuální komponenta a tedy po znovuotevření projektu jsou tyto objekty vytvořeny a je možné s nimi dále manipulovat. -15-
4. OBJEKTY
4.1 Grafická primitiva Komponenta ObjectScene nabízí k zobrazení různá geometrická primitiva. Jednak taková, která nabízí OpenGL (bod, přímka, čajová konvička, čtyřúhelník), dále ta, která lze z těchto jednodušších elementů vytvořit (krychle, torus) a kvadriky (koule, válec, kužel, disk, popř. částečný disk). Protože komponenty nepoužívají knihovnu GLUT, je vytváření objektů o něco málo složitější, nicméně jako plus je fakt, že všechny objekty mají definované texturovací souřadnice, které GLUT pro své objekty negeneruje (vyjma čajové konvičky).Vedle těchto jednoduchých primitiv je uživatel schopen vytvořit venkovní prostředí. K tomuto účelu komponenta ObjectScene nabízí dva speciální objekty, a to skybox a terén. Skybox není nic jiného než krychle a používá se k vytvoření dojmu oblohy a okolní krajiny. Avšak oproti krychli ze sady základních geometrických primitiv skybox umožňuje na každou stěnu namapovat jinou texturu. S dostatečnou velikosí skyboxu a vhodně zvolenými texturami lze vytvořit vcelku reálné prostředí, do kterého mohou být zasazeny další objekty. Vhodnými texturami se rozumí sada 6 textur, které na sebe hranami lícují a není tedy vidět, v jakých místech tyto textury na sebe navazují a také je eliminován dojem toho, že se scéna nachází uvnitř krychle. Druhý ze speciálních objektů je terén, konkrétně třída TDGLTerrain. Tato třída umožňuje vytvořit terén na základě výškové mapy. Tato výšková mapa (height map) je obyčejný šedotónní obrázek, kde jednotlivé intenzity bodů rastru definují výšku na ose y. Mezi vlastnosti této komponenty patří možnost nastavit hodnotu ofsetu (vlastnost StepSize), díky kterému lze regulovat hustotu bodů a tím zvyšovat nebo snižovat úroveň detailů. Další vlastnost, HeightRatio, umožňuje usměrňovat hodnoty na ose y a dává tak vzniknout rozlehlým pláním nebo naopak vysokým pohořím. Poslední a neméně důležitou vlastností je atribut ScaleValue, který aplikuje celkovou změnu měřítka na terén. Je zde z toho důvodu, že intentzity jasu šedotónního obrázku se pohybují v rozmezí od 0 do 255. Pokud by se tyto hodnoty aplikovaly rovnou bez změny měřítka, objekt terénu by byl úplně mimo záběr kamery a prakticky by nic nebylo vidět (už pouze jedna jednotka délky v souřadnicovém systému OpenGL je poměrně veliká). Implicitně je tento atribut nastaven na 0,004.
4.2 Mesh objekty a jejich zobrazení Ve většině aplikací, ale i světě kolem, se člověk nesetkává pouze s výše popsanými primitivními objekty. Převážná část objektů jsou tzv. mesh2 objekty. Mesh objekty jsou tvořeny množinou bodů (nebo hran, facet či povrchů), které reprezentují geometrii objektu. Tímto způsobem může být zobrazen jakýkoli reálný (i nereálný) objekt či před mět. Podobně jako textury (viz kapitola 6), také mesh objekty bývají ukládány a načítány do aplikace z externích souborů.
2 Mesh v angličtině znamená spleť, síťka, síťovina
-16-
4. OBJEKTY
obr. 3 – ukázka mesh objektů Komponenta ObjectScene umožňuje zobrazovat objekty z externích souborů a dále s těmito objekty pracovat jako s běžnými objekty scény (lze s nimi pohybovat, rotovat, měnit měřítko apod.). Formáty souborů, které ObjectScene umí načítat jsou stručně popsány a shrnuty v následujícím přehledu: Formát Autodesk 3DS Jedná se o binární formát a tedy data v něm uložená lze rychleji zpracovávat (zapsat na disk nebo nahrát do paměti). Jedná se o de facto3 standard 3D formátu, mnoho aplikací s ním pracuje a firma Autodesk nikdy nezveřejnila jeho specifikaci. I přes toto omezení se podařilo zhruba zjistit jeho strukturu a tedy je možné s ním pracovat. Jeho interní struktura je reprezentována v podobě stromové hierarchie, jejíž listy a uzly jsou tvořeny speciálními bloky dat, tzv. chunks. Každý takový blok obsahuje hlavičku, která jej popisuje a vlastní data. Struktura bloků vypadá takto:
identifikátor: dvoubytový úsek, podle něhož lze poznat, jaká data blok reprezentuje – zda materiál, světlo, vrcholy aj.
velikost: čtyřbytový údaj, který udává velikost bloku v bytech (v této hodnotě je zahrnuta i velikost všech dat, které blok obsahuje včetně velikostí podbloků, pokud nějaké má)
data: vlastní data bloku. Některé bloky data mít nemusí, slouží pouze jako kontejner zapouzdřující nižší podbloky
podbloky: další úrovně bloků ve stromové struktuře
Formát 3DS obsahuje mimo vlastní data geometrie objektů i popis světel, kamer, materiálů a názvy textur přiřazené jednotlivým materiálům a je schopen v sobě nést i informace o animaci objektů, které obsahuje. Následkem toho, že tento formát nemá zveřejněnou specifikaci, některé aplikace jej špatně interpretují (nemluvě o zápisu) a tedy jej mohou zobrazovat chybně. Ve většině případů jsou zapotřebí pouze údaje o vrcholech, normálách a texturovacích souřadnicích a zbytek informací se může bezpečně ignorovat. 3 De facto standard je vyvinut na základě dohody v rámci jisté komunity. Ta před jeho vydáním odsouhlasí, že odpovídá stanoveným cílům. Příklad jsou dokumenty RFC vydávané organizací IETF.[reference]
-17-
4. OBJEKTY
Formát Wavefront OBJ Formát OBJ je textový a je tedy velice jednoduché z něj získat veškerá data. Mimo běžných vrcholových dat umožňuje ukládat také NURBS plochy, křivky aj. Vedle OBJ souboru může existovat i soubor s příponou .MTL, který definuje vlastnosti materiálů objektů, nalézajících se v souboru .OBJ. V ideálním případě vypadá struktura souboru .OBJ takto:
prvních několik řádků může a nemusí být komentář. Tyto řádky mohou být ignorovány. Komentář je uvozen znakem dvojitý křížek (#)
následují řádky se souřadnicemi vrcholů v zápisu x,y,z. Jsou uvozeny znakem malé v. Například v 0.123 0.234 0.345
po skupině vrcholových dat následují řádky texturovacích souřadnic. Jsou uvozeny znaky vt a zapsány ve tvaru u,v takto: vt 0.500 -1.352
dále mohou být přítomny hodnoty normál (které nemusí být jednotkové). Řádky jsou uvozeny znaky vn a formát zápisu je vypadá takto : vn 0.707 0.000 0.707, kde tři číselné hodnoty reprezentují složky vektoru normály
pokud v souboru jsou přítomny souřadnice textur nebo normál (nejlépe oboje), jako poslední množina údajů jsou informace o ploškách. Řádky jsou uvozeny znakem f a jsou to indexy vrcholů, texturovacích souřadnic a normál odkazující do polí, které jsou vytvořeny v předchozích krocích z příslušných hodnot. Tedy zápis jednoho trojúhelníku plošky vypadá následovně: f 3187/20/4080 3185/22/4078 3175/24/4068, kde hodnota před prvním lomítkem je index do pole vrcholů, hodnota před druhým lomítkem je index do pole texturovacích souřadnic a hodnota za druhým lomítkem je index do pole normálových souřadnic. Pokud soubor obsahuje např. pouze vrcholová data, zápis plošky je ve tvaru f v// v// v//, kde v je index do pole vrcholů.
-18-
Kapitola 5
Světla a osvětlení V případě zobrazení složitější scény, tvořené z prostorových objektů (primitiv jako např. krychle, válce, koule apod.), musí scéna obsahovat minimálně jedno světlo. V opačném případě bude scéna zobrazena chybně nebo některé objekty nebudou vidět. Uvažujme jednoduchou scénu s nějakým objektem, například krychlí. I bez světla se tato krychle vykreslí, nicméně bude vidět pouze její obrys. Až po přidání světla (nebo i více světel) krychle nabude trojrozměrný dojem. Komponenta ObjectScene nabízí stejný počet světel jako OpenGL, tedy minimálně 8 (různé implementace OpenGL nabízí různý počet světel, tedy maximální počet světel, který komponenta může nabídnout závisí na konkrétní implementaci). Je třeba mít na paměti, že s každým přidáním světla jsou výpočty náročnější a pokud má být scéna kreslena v reálném čase s mnoha světelnými zdroji, je lepší použít jiné techniky jako například vypínat vzdálená světla nebo lightmapping4.
5.1 Základní světelný model Komponenta GLViewer umožňuje uživateli volit tři atributy modelu osvětlení OpenGL, a to barvu a intenzitu globálního ambientního světla, zda mají být výpočty osvětlení prováděny zvlášť pro přední a zadní stranu objektů (polygonů) a zda bod pohledu je nekonečně vzdálený nebo umístěný do scény. Globální ambientní světlo je světlo, které nevydává žádný zdroj. Je to světlo, které vznikne rozptylem okolního světla do prostoru a ustálí se na nějaké hodnotě. Takto může celá scéna získat barevný nádech – může se tak nasimulovat třeba západ slunce, kdy celý prostor, včetně objektů, je zabarven do barvy červánků. Tento atribut osvětlení se nastavuje vlastností AmbientColor, kde lze zvolit hodnoty jednotlivých složek světla (červené, zelené, modré) a složky alfa5. Oboustranné osvětlení určuje, jak bude prováděn výpočet na odvrácených stranách polygonů. Ve většině případech není nutné nastavení měnit, pokud se ale ve scéně nachází například rozříznutá koule, byl by její vnitřek špatně osvětlen vlivem otočených normál. Avšak po zapnutí oboustranného osvětlení se odvrácené strany polygonů osvětlí správně. Atribut oboustranného osvětlení se nastavuje ve vlastnosti ContextOptions. Poslední volitelný atribut světelného modelu je umístění bodu pohledu ve scéně. Povolení tohoto atributu má za následek zlepšení realističnosti osvětlených objektů. Implicitně je bod pohledu nastaven na nekonečno, povolením tohoto atributu se bod pohledu přesune do počátku soustavy souřadnic. Pozici kamery toto nijak neovlivní. Atribut se nastavuje vlastností LocalViewer, a to na hodnotu true nebo false. 4 Lightmapping je technika sloužící k simulaci osvětlení objektů ve scéně. Vyžaduje multitexturing a spočívá v namapování dvou textur na objekt. První textura je klasický obrázek nějakého povrchu, druhá textura je mapa intenzity světla (šedotonní obrázek, většinou nějaký gradient). Tím se vytvoří polygon, který je jakoby vystínovaný. Touto technikou lze rapidně vylepšit vzhled scény za cenu prakticky mizivého nárůstu výpočtů 5 Alfa složka nebo také alfa kanál je 4. komponenta barev v počítačové grafice. Nese v sobě informaci o průhlednosti barvy. Takto lze třeba na sebe překládat několik obrázků s průhledným pozadím a výsledný obázek bude spojení všech těchto vrstev.
-19-
5. SVĚTLA A OSVĚTLENÍ
5.2 Typy světel Objekt světla je tvořen třídou TLigtSource, která je potomkem třídy TBasicObject. To znamená, že se světlem se může zacházet jako s jiným objektem scény, tedy umožňuje běžné lineární i afinní transformace. Za zmínku stojí fakt, že aplikace změny měřítka (scale) nebo rotace nemá na světlo nijaký vliv – rotace pouze v případě, že se jedná o reflektorové světlo, kterým se může simulovat např. reflektor nebo rozsvícená baterka. Stejně jako OpenGL, tak i ObjectScene nabízí tři typy světel, které jsou v nabídce v Object Inspectoru ve vlastnosti LightStyle:
bodové – bodové světlo lze chápat jako žárovku umístěnou do prostoru, která vyzařuje světlo na všechny strany se stejnou intenzitou. Bodovému světlu odpovídá položka lsOmni. Žádná z transformací, kromě translace, jej neovlivňuje.
směrové – světlo se šíří z jednoho směru nezávisle na vzájemné poloze a vzdálenosti objektů v zobrazované scéně. Za zdroj směrového světla lze považovat například slunce, neboť světelné paprsky, které dopadají na povrch těles ze slunce, jsou víceméně paralelní (vzhledem k poměru vzdáleností slunce a těles od sebe). U směrového světla pozice zdroje nehraje roli, protože zdroj světla je zadán jako vektor, jehož 4. složka v homogenních souřadnicích je nula, tedy zdroj je jakoby v nekonečnu. Směrové světlo lze zvolit volbou lsParallel.
reflektorové – tento typ světelného zdroje se chová podobně jako klasický reflektor nebo kapesní svítilna, tj. světlo se šíří (stejně jako u bodového světla) z jednoho bodu, ale ne do všech směrů, nýbrž pouze v předem zadaném kuželu. Toto světlo ovlivní téměř všechny parametry, které třída LightSource v Object Inspectoru nabízí: pozice, kde je světlo umístěno (Position), směr, kam světlo svítí (Spot direction), šířka světelného kuželu (Spot cutoff) a míra intenzity světla podél osy směru kužele (Spot exponent). Reflektorové světlo se nachází pod položkou lsSpot.
Pro názornost jsou všechny typy světel zobrazeny na následucjích obrázcích:
obr. 4 – bodové světlo
obr. 5 – směrové světlo
-20-
obr. 6 – reflektorové světlo
Kapitola 6
Materiály a textury Aby byla výsledná scéna co nejvěrohodnější, je dobré objektům ve scéně přiřadit různý vzhled. Samotná světla sice scénu jistě dobře nasvítí, nicméně opravdu hezky vypadajících objektů můžeme docílit pouze nadefinováním a přiřazením materiálových vlastností a textur. Stejně jako v reálném světě mají předměty různou barvu, vzhled, lesklost, parametry, které různě lámou, odráží nebo ohýbají světelné paprsky, může se něčeho podobného dosáhnout i v počítačové grafice. Tyto charakteristiky lze obecně označit jako materiál nebo vlastnosti materiálu. Různým nastavením difuzní, ambientní barvy, barvy spekulárních odlesků a dalších případných parametrů lze dosáhnout docela přesvědčivé reprezentace různých kovových nebo plastových povrchů, různě matných, lesklých nebo zářivých. Ale ani materilály nedokáží věrně simulovat jiné, například organické nebo průmyslové povrchy. Tohoto lze dosáhonout až po aplikování textur dohromady s výše zmíněnými materilály. Textura je v podstatě vzor, který připomíná nějaký povrch – dřevo, textil, trávu, cihly, beton, koberec nebo některé z mnoha dalších. Teprve po kombinaci materiálů a textur je možné opravdu věrohodně napodobit takřka každý předmět či povrch, se kterým se můžeme setkat v reálném životě.
6.1 Specifikace materiálů Materiál objektu definuje jeho optické vlastnosti, tj. jakým způsobem povrch objektu odráží světelné paprsky zdrojů světla, nacházejících se ve scéně a také definuje, jaké barevné složky světla pohlcuje – toto je v podstatě fyzikální princip, který určuje barvu předmětu a jenž platí i v reálném světě. Výsledná barva předmětu, na který dopadá světlo je rovna barevné složce světla (resp. její vlnové délce), kterou předmět odráží. Pokud například máme míč, jenž pohlcuje všechny vlnové délky zelené a modré barvy a vlnové délky červené barvy odráží, pak jeho výsledná barva je červená (v bílém světle). Vzhled materiálu je výsledkem kombinací jednotlivých složek, ze kterých se materiál skládá. Podobně jako barva světla, skládá se i výsledná barva materiálu z difuzní, ambientní a spekulární barvy (zrcadlového odrazu), ale navíc se materiál skládá z dalších dvou komponent, a to emisní barvy a konstanty shininess. Následující přehled vysvětluje význam všech materiálových složek:
ambientní barva: je nejvíce patrná tam, kam žádné přímé osvětlení na objekt nedopadá a ovlivňujě tak celkovou barvu materiálu objektu. Celková ambientní barva objektu je ovlivněna globálním ambientním světlem scény a ambientními složkami všech světelných zdrojů nacházejících se ve scéně.
difuzní barva: nejdůležitější složka materiálu – určuje, jakou výslednou barvu objektu vnímáme. Nejintenzivnější je v místech, kde světlo dopadá kolmo na povrch objektu. Ve většině případech je ambientní a difuzní barva stejná.
spekulární barva: spekulární barva neboli zrcadlový odraz vytváří odlesky na objektu. Na rozdíl od ambientní a difuzní barvy závisí množství odraženého světla na úhlu mezi světelným zdrojem a místem pozorovatele. -21-
6. MATERIÁLY A TEXTURY
Aby odlesky vůbec mohly být vidět, je důležitá také hustota geometrie objektu. Pokud bude hustota sítě polygonů objektu nízká, spekulárních odrazů se lze jen těžko dočkat. Čím ale bude hustota polygonů vyšší, tím věrnější odlesky lze očekávat.
emisní barva: určením emisní barvy lze získat objekt, který zdánlivě vyzařuje danou barvu. Ve skutečnosti se z něj světelný zdroj nestane, pouze vypadá, jakoby svítil danou barvou. Lze tímto simulovat mnoho světelných zdrojů (ač žádné světlo nevyzařují) a pomocí lightmappingu můžeme dosáhnout přesvědčivé iluze svítivosti tohoto objektu.
konstatnta shininess: určuje lesklost materiálu. Její hodnoty jsou v rozsahu od 0 do 128. Nízké hodnoty definují matný povrch, zatímco vysokými hodnotami lze docílit vysoce lesklých a jakoby vyleštěných povrchů.
6.2 Implementace materiálů v komponentě Materiál objektů, které ObjectScene nabízí, je implementován jako třída TMaterial a je to vlastnost (property) každého objektu, pro který má smysl materiál definovat. Jedná se tedy o všechny objekty vyjma světel, bodů a čar. Třída TMaterial obsahuje 4 vlastnosti: BackProperties, FrontProperties, BlendingMode a MaterialOptions. MaterialOptions je množina dvou hodnot moIgnoreFog a moIgnoreLighting. První hodnota způsobí, že na objekt nebude aplikována mlha, je-li ve scéně povolena, druhá má za následek, že na objekt se nebude vztahovat osvětlení, je-li osvětlení ve scéně zapnuto. Vlastnost BlendingMode je podrobně rozebrána v kapitole 7.3, avšak je dobré ji stručně popsat i zde. Tato vlastnost souvisí s mícháním barev a pomocí této vlastnosti lze materiál učinit poloprůhledným. Může se tímto imitovat například tabule skla nebo ledový krystal. BackProperties a FrontProperties jsou vlastnosti téhož typu (třída TFaceProperties) a nastavují se v nich všechny materiálové složky (rep. jejich barvy). Protože každý polygon má 2 strany (přivrácenou a odvrácenou), je možné pomocí těchto vlastností nastavit jiné parametry pro každou stranu polygonu zvlášť. Přivrácené straně polygonu odpovídá vlastnost FrontProperties a odvrácené BackProperties.
6.3 Specifikace textur Textura je, na nejnižší hardwarové úrovni, pouhé pole hodnot v paměti počítače. Většina lidí si pod pojmem textura vybaví běžný dvojrozměrný obrázek, ať už šedotónní nebo barevný. Ale textury mohou být i jednorozměrné nebo dokonce i trojrozměrné. Jednorozměrné textury se nepoužívají často, například takovou texturu lze aplikovat na body nebo úsečky. Trojrozměrné textury se používají převážně v medicíně nebo materiálovém inženýrství. Trojrozměrnou texturu lze vytvořit i složením několika dvojrozměrných textur. Například v lékařství se tento přístup používá k prostorovému zobrazení vnitřních struktur a tkání těla, kdy se jednotlivé snímky získají postupným snímáním pacientova -22-
6. MATERIÁLY A TEXTURY
těla buď pomocí magnetické rezonance (MR) nebo počítačové tomografie (CT). Tělo pacienta se v přeneseném významu jakoby „nařeže“ podél jedné z os a výsledné obrázky se složí. Následně lze snímky libovoně upravovat – lze je různě analyzovat nebo se podle nich vytvoří plnohodnotný trojrozměrný model. Jak už bylo zmíněno v prvním odstavci, textura je pole hodnot v paměti počítače. To tedy znamená, že texturu lze vytvořit i programově pouhým vytvořením vhodné datové struktury s odpovídajícími hodnotami. Odpovídajícími hodnotami se rozumí data reprezentující například barvy (RGB), barvy s alfa hodnotou (RGBA) nebo jas (luminance). Osvědčený způsob získávání textur je nahravání dat z externích souborů, tzn. rastrových obrázků. Protože je ale rozhraní OpenGL primárně navrženo pouze pro vykreslování trojrozměrné grafiky, navíc co nejvíce platformově nezávislé, z tohoto důvodu nenabízí žádné funkce, které by toto obstarávaly (na rozdíl od rozhraní DirectX, které několik formátů umožňuje načítat). Je tedy nezbytné požít nějakou jinou knihovnu, která z mnoha různých grafických formátů umožňuje získat obrazová data[7] nebo se pokusit o napsání vlastních metod, které by nahrávání obrázků prováděly.
6.4 Implementace textur v komponentě Implementace textur v komponentě ObjectScene je zapouzdřena jako vlastnost téměř všech grafických objektů, kromě světel, bodů a čar. Tato vlastnost s názvem Texture je instancí persistentní třídy TTexture, která se nachází v jednotce DGLTexture a sama nabízí další atributy, které umožňují s texturou manipulovat. Co je ale neméně důležité je skutečnost, že k tomu, aby se textura na konkrétním objektu zobrazila, musí objekt mimo svou geometrii definovat i texturové souřadnice. Texturové souřadnice lze připodobnit k nějakým styčným bodům, podle kterých se textura na objekt uchytí (namapuje) a mezi těmito body se interpoluje. U jednoduchých grafických primitiv se texturové souřadnice určí poměrně snadno, ale s narůstající složitostí geometrie je velice obtížné alespoň dostatečně přesně texturu namapovat. Mapování textur (resp. určování jejich souřadnic) lze považovat v jistém slova smyslu za vědní disciplínu v oboru počítačové grafiky. K tomu, aby se textura řádně namapovala na objekt, je někdy potřeba použít specializovaný software6. Pokud má navíc aplikace umožňovat třeba načítání a zobrazování 3D souborů, pak tyto soubory spolu s geometrií objektu či objektů většinou obsahují i souřadnice textur jim určené. Asi nejdůležitější atributy třídy TTexture jsou parametry Image a Enabled. Vlastnost Image je řetězcový typ, který odkazuje na umístění souboru textury na disku. Aby vyhledání patřičného souboru bylo co nejjednodušší, po kliknutí na tuto vlastnost v Object Inspectoru se otevře dialogové okno, které umožňuje pohodlné procházení adresářové struktury disku. Třída TTexture umožňuje načítat několik rastrových formátů, a to Windows Bitmap (BMP), soubory JFIF (JPG, JPEG), Truevision TGA (TGA) a bezhlavičkové soubory RAW (RAW). Třída, která načítání těchto souborů umožňuje se nachází v jednotce DGLBitmaps a je to převzatý a upravený kód z tutoriálu na stránkách věnujících se grafice a OpenGL [8]. 6 Nemusíme to nutně být programy typu Cinema 4D nebo 3D Studio Max, lze si poměrně dobře vystačit s méně robustními a ne tak finančně náročnými programy jako např. Blender nebo Poser.
-23-
6. MATERIÁLY A TEXTURY
Druhá nejdůležitější vlastnost třídy TTexture má název Enabled a je to logická hodnota, která zapíná a vypíná zobrazování textury na konkrétním objektu. Implicitně je nastavena na false, tzn. pokud sice uživatel vybere ve vlastnosti Image soubor s texturou, nebude na objektu zobrazena do té chvíle, dokud nebude v Enabled povoleno zobrazení textury a uživatel může tak nabýt dojmu, že mapování textur nefunguje správně. V drtivé většině případů málokdy texely7 textury korespondují s celočíselnými souřadnicemi pixelů na obrazovce. V tom případě je nutné použít filtrování textur, které zajistí výpočet správné barvy pro daný pixel. V praxi mohou nastat dva případy, kdy je potřeba tyto filtry použít, a to, když se textura na objektu zmenší (minification – objekt je od kamery vzdálen) nebo zvětší (magnification – kamera je v těsné blízkosti objektu). Existují dva základní filtry, jak pro zmenšení, tak i pro zvětšení textury na objektu a tyto dva filtry se nazývají GL_NEAREST a GL_LINEAR. Filtr GL_NEAREST odpovídá bodovému vzorkování (point sampling), filtr GL_LINEAR je označován jako bilineární vzorkování (bilinear sampling). V případě GL_NEAREST se pro zvětšení a pro zmenšení použije takový texel, který se nachází nejblíže středu pixelu. Tento filtr poskytuje rychlý výpočet, avšak jeho nevýhoda spočívá v tvorbě nehezkých artefaktů. V případě volby GL_LINEAR se pro zvětšení a zmenšení použije vážený lineární průměr z pole 2×2 texelů8, které se opět, jako v předchozím případě, nachází nejblíže středu konkrétního pixelu. Tento filtr poskytuje lepší výsledky než filtr GL_NEAREST, je ale výpočetně náročnější. Třída TTexture samozřejmě tyto filtry nabízí v rámci vlastností MagFilter a MinFilter jako tmgNearest, tmgLinear ( MagFilter) a tmiNearest, tmiLinear ( MinFilter). Pro srovnání jsou zde uvedeny obrázky, na kterých jsou vidět výsledky obou filtrů:
obr. 7– bodové vzorkování (GL_NEAREST)
obr. 8 – bilineární vzorkování (GL_LINEAR)
Velice často je potřeba namapovat texturu na objekt opakovaně až do velké vzdálenosti od pozorovatele. Pokud by se použila textura jen s jedním jediným rozlišením, ve vzdálenějších místech objektu by se zobrazovala s chybami a nepěknými artefakty. Řešením je vytvořit několik úrovní rozlišení textury a s rostoucí vzdáleností mapovat tuto texturu 7 Texel je základní jednotkou textury, podobně jako pixel je základní jednotkou rastrového obrazu. Texel je většinou definován barvou, intenzitou jasu nebo hodnotou alfa. 8 Pole o rozměrech 2×2 se použije v případě dvojrozměrných textur. Pokud se ale pracuje s jednorozměrnými texturami, spočítá se průměr dvou vedle sebe ležících texelů, v případě trojrozměrných textur, uvažuje se pole o rozměrech 2×2×2.
-24-
6. MATERIÁLY A TEXTURY
se stále nižším rozlišením. Tato technika se nazývá mipmapping (MIP je zkratka latinského multum in parvo – mnoho v malém). Mipmapping spočívá v tom, že se vytvoří několik úrovní velikosti textury a v závislosti na vzdálenosti kamery od objektu se použije nejvhodnější velikost. Mějmě například texturu o velikosti 256×256 px. Mipmapping z dané textury vytvoří sadu textur vždy s poloviční velikostí, tedy 128×128 px, 64×64 px atd. až do velikosti jednoho pixelu (úroveň velikostí lze řídit, ne vždy jsou potřeba všechna rozlišení). Poté se na vzdálenější místa objektu aplikuje ne textura s velikostí 256×256 px, ale třeba jen s velikostí 64×64 pixelů. Tak se odstraní artefakty a chyby vzniklé použitím textury bez mipmappingu. Tímto se rozrůstá sada minifikačních filtrů o další čtyři, a to GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST a GL_LINEAR_MIPMAP_LINEAR. Těmito dalšími filtry lze řídit přechody mezi jednotlivými úrovněmi mipmap, protože v místě, kde jedna mipmapa přechází v druhou se může vyskytnout hrubý přechod, šev. Tyto filtry pomáhají tento přechod zjemnit nebo dokonce úplně eliminovat. Pakliže je textura na objektu úspěšně namapována, může být ještě dále upravována. OpenGL nabízí 4 základní módy, jak má být textura smíchána s materiálem objektu. Podobně i třída TTexture tyto 4 módy poskytuje a ty jsou následující:
tmReplace: barva textury plně nahradí barvu materiálu
tmDecal: je podobný módu tmReplace, ale pokud materiál je poloprůhledný, je i textura poloprůhledná, ale barva textury se nemíchá s žádnou složkou materiálu
tmModulate: umožňuje průhlednost textury a navíc smíchá barvu textury s barvou emisní složky materiálu
tmBlend: umožňuje průhlednost textury a barva textury je smícháná s barvou ve vlastnosti EnvColor. Vlastnost EnvColor je brána v potaz pouze v tomto módu a lze jí dodatečně míchat výslednou barvu textury. V jiných případech se barva EnvColor používá při programování shaderů9. Shadery ale popisované komponenty nepoužívají.
Posledním důležitějším atributem textur je styl mapování, jak se textura na objekt namapauje. Každému vrcholu geometrie by měla odpovídat texturovací souřadnice, nicméně existují další způsoby mapování. Atribut, který se o mapování textury stará se nazývá MappingMode a může nabývat těchto hodnot: tmmUser: textura je namapována podle texturovací souřadnice dle geometrie objektu. Objekty v komponentě ObjectScene mají texturovací souřadnice již přiřazeny a uživatel do texturovacího procesu nijak nezasahuje. tmmObjectLinear: textura je jakoby fixně promítnuta na objekt a tedy s pohybem objektu se pohybuje i textura, ale s celkovým dojmem, že textura je napevno přichycena k objektu 9 Shader je malý program, určený výhradně pro grafickou kartu, který určitým způsobem řídí pořadí zpracování vrcholů a fragmentů a může toto pořadí měnit. Výsledkem jsou takové grafické efekty, kterých by se běžným způsobem jen těžko nebo vůbec podařilo dosáhnout.
-25-
6. MATERIÁLY A TEXTURY
tmmEyeLinear: textura je fixně umístěna v prostoru a pokud se skrze ni pohybuje nějaký objekt, je otexturována pouze ta část, na kterou promítaný obraz tmmSphere: namapuje texturu na objekt jako kouli a tímto způsobem lze relativně věrně imitovat odrazy okolí. Ve skutečnosti objekt okolí neodráží, pouze textura je na objektu deformovaná tak, že odrazivost vcelku hezky napodobuje. Jediná vada tohoto typu mapování je, že na pólech pomyslné koule je textura značně zdeformovaná, což se projeví i na objektu. Všem objektům, které texturu mít mohou lze přiřadit právě a pouze jedna textura. Pokud je zapotřebí vytvořit objekt s více texturami, pak musí být složen z několika částí (podobjektů) a každé části přiřadit texturu. Výjimku tvoří pouze objekt skybox, kterému lze přiřadit 6 textur (na každou stěnu krychle jednu) a mesh objekty, které více textur většinou mají. Textury jsou ale s mesh objekty pevně svázány10 a nelze je měnit.
10 Samozřejmě profesionální modelovací programy umožňují textury objektů měnit, ale to je poněkud jiná záležitost, mimo rámec této práce.
-26-
Kapitola 7
Grafické efekty 7.1 Mlha Mlha (fog) je efekt, který má připomínat mlhu z reálného světa, kouř, znečištění nebo prach v ovzduší. Aby byla mlha viditelná, musí se jednak povolit, nastavit míra hustoty (0 až 1) a co je neméně důležité – musí mít stejnou barvu jako color-buffer, v opačném případě by se mohla chybně zobrazit. Protože se výpočty mlhy provádí většinou na speciálním hardwaru grafické karty, používání mlhy se na náročnosti scény téměř neprojeví a dříve se často mlha používala k urychlení vykreslování scény ve videohrách, protože skryla podstatnou část geometrie scény a tato geometrie se poté nemusela vůbec vykreslovat. Komponenta GLViewer obsahuje v sobě mlhu již integrovanou jako vlastnost. Obsahuje také vlastnost EnableFog, což je logická hodnota, podle které se mlha zapíná a vypíná. Mlha jako taková je vlastnost typu TFog, která má klasické atributy, známé z OpenGL: hustotu mlhy (density) a typ mlhy (lineární, exponenciální, kvadraticky exponenciální). V případě lineární mlhy lze nastavit počátek a konec mlhy. Barva mlhy se automaticky nastavuje podle barvy pozadí komponenty GLViewer a tento mechanizmus zabraňuje chybnému zobrazení mlhy důsledkem rozdílné hodnoty barvy mlhy a color-bufferu.
7.2 Antialiasing Antialiasing je nástroj, technika k odstranění nebo alespoň výraznému potlačení nežádoucích jevů (artefaktů) v obrazu. Alias vzniká nepravidelným nebo nižším vzorkováním signálu, než je frekvence signálu. Právě převodem spojitého signálu na diskrétní často dochází k chybám (moaré nebo typický rotační pohyb kol). V grafice, ať už dvoj nebo trojrozměrné, se s aliasem setkáváme při kreslení kulatých objektů, křivek nebo čar, kdy objekty jsou kresleny se „zuby“ (jaggies). V 3D grafice navíc může alias způsobit i to, že na pohybujícím se objektu textura jakoby bliká (flickering). Existují 3 osvědčené způsoby antialiasingu:
objektový super sampling
super sampling celé scény pomocí akumulačního bufferu
multisampling v rozšíření knihovny OpenGL
7.2.1 Objektový supersampling Jedná se sice o výkonnou techniku, ale je nejsložitější na provedení. Je to v podstatě alfa blending polygonů a čar v RGBA módu (viz následující podkapitola 7.3), kdy se počítá pokrytí fragmentu. Musí se vyřešit překrytí hran objektů, což je problematické. Provádí se řazením polygonů, kdy musíme (nejčastěji) vykreslovat polygony zepředu dozadu s vypnutým depth-bufferem.
-27-
7. GRAFICKÉ EFEKTY
7.2.2 Supersampling scény Tato technika je mnohem jednodušší na implementaci, avšak výkonově náročnější. Provádí se tzv. jitteringem, kdy se viewport (záběr) kamery mírně posune do všech směrů (obvykle v 2, 4, 8 nebo 16sousedství pixelu), a to tak, že posun musí být menší, než je velikost pixelu. Tyto pozice se následně zprůměrují v accumulation-bufferu a výsledek se poté vykreslí. Tato metoda změní artefakty na šum, který je mnohem lepší pro lidské oko než pravidelné chyby. Komponenta GLViewer používá tento způsob antialiasingu, protože lze snadno volit úroveň jeho intenzity a uživatel tak může zvolit optimální poměr mezi kvalitou a výkonem.
7.2.3 Multisampling Další metoda antialiasingu spočívá ve využití rozšíření knihovny OpenGL (konkrétně soubor glext.h, resp. glext.pas). Jedná se o podobný princip, jako v případě s accumulation-bufferem s tím rozdílem, že antialiasing se provádí pouze na hranách polygonů a vnitřek polygonů neovlivňuje. Veškerá implementace antialiasingu je už v OpenGL vytvořena a programátorovi stačí vytvořit odpovídající kontexty a zavolat příslušné funkce. Nevýhoda tohoto rozšíření je, že se nejedná o OpenGL standard, pouze o návrh v rámci konsorcia ARB a tedy některé grafické karty (zejména starší) nebo implenetace OpenGL tuto metodu nemusí podporovat.
7.3 Míchání barev (blending) Míchání barev neboli blending je možnost, jak dosáhnout velmi zajímavých grafických efektů. Jedná se o prolínání (míchání) barev vykreslovaného tělesa s pozadím na základě předem zadané míchací funkce. Pomocí blendingu se může stát nějaký objekt průhledným, lze jím dosáhnout antialiasingu, modulace textur na objektech (jev, kdy barva textury a barva materiálu objektu se smíchá) nebo maskování určité barvy, tj. technika, která se používá převážně u tzv. spritů, což jsou dvojrozměrné11 objekty, kterými lze vytvořit například efekt plápolajícího ohně nebo jimi lze vytvořit mnoho objektů, aniž by musely mít složitou geometrii. Jako příklad lze uvést skupinu stromů na pozadí scény, kdy každý strom je tvořen jediným polygonem, na kterém je obrázek stromu na černém pozadí. Pomocí průhlednosti lze černou barvu zamaskovat a je tedy vidět pouze obrázek stromu a kdysi černé pozadí je nyní průhledné. I objekty, které nabízí komponenta ObjectScene průhlednost umožňují, a sice jako součást materiálu, který se posléze na objeckt aplikuje. Předem je ale nutno průhlednost povolit, respektive nastavit její typy. Průhlednost se nastavuje ve vlastnosti Material pod nabídkou BlendingMode. Implicitně je průhlednost vypnuta – v BlendingMode je nastavena hodnota bmOpaque (z anglického opaque – neprůhledný, neprůsvitný). 11 OpenGL se všemi primitivy, od bodu začínaje, po polygon konče, nakládá jako s trojozměrnými objekty (jsou reprezentovány 3, resp. 4 souřadnicemi x,y,z, resp. w. Tedy i sprity jsou trojrozměrné objekty. Pro zjednodušení ale na ně lze nahlížet jako na dvojrozměrné.
-28-
7. GRAFICKÉ EFEKTY
Možné hodnoty vlastnosti Blending Mode a jejich významy jsou:
bmOpaque: průhlednost vypnuta, objekt se vykreslí normálně, tak jak jej definuje nastavený materiál
bmTransparency: objekt bude průhledný podle nastavené míry alfa kanálu difuzní složky barvy
bmAlphaTest50: objekt je plně průhledný, pokud hodnota alfa složky klesne pod 0,5. V opačném případě (v rozmezí 0,5 až 1,0) bude objekt plně neprůhledný (stejný výsledek jako v případě bmOpaque).
bmAlphaTest100: podobně, jako v předchozím případě, ale objekt bude plně neprůhledný, pokud hodnota alfa složky bude rovna 1. Naopak, pokud bude hodnota alfa složky menší než 1, bude objekt plně průhledný.
bmAdditive: aditivní blending spočívá v přičtení násobku barvy příchozího fragmentu svojí alfa složkou k barvě pixelu, nacházejícího se ve frame-bufferu. Výsledek je, že intenzita jasu výsledné barvy se značně zvýší. Míchací koeficient pro příchozí fragment se rovná GL_SRC_ALPHA a koeficient pro fragment, který se nachází v color-bufferu, se rovná GL_ONE.
bmModulate: modulační blending si lze nejlépe představit jako filtr, přes který se kamera dívá na scénu. Uvažujme, že tento filtr nepropouští 50 % červené barvy, 10 % zelené a 25 % modré. Výsledkem tedy bude scéna se zeleným nádechem, protože filtr propouští 90 % zelené složky, červené jen polovinu a z modré složky jen tři čtvrtiny.
Míchání barev nabízí mnohé možnosti, ale jeho zvládnutí vyžaduje bohaté znalosti a zkušenosti, protože je velmi důležité pořadí objektů při vykreslování. Ačkoli tato problematika je mimo rámec této práce a uživatel popisovaných komponent nemusí o těchto záležitostech nic vědět, není na škodu si ji velmi stručně nastínit. Mějmě scénu, ve které se nachází objekty, z nichž je několik průhledných. Pak nastává problém, jak je správně vykreslit. Pokud by se vykreslovaly náhodně, pak by se neprůhledné objekty za průhlednými nemusely zobrazit (průhledné objekty by se zobrazovaly stejně jako neprůhledné). První krok spočívá ve vykreslení všech neprůhledných objektů (v libovolném pořadí). Následně se musí zakázat zápis do hloubkového bufferu (depth-bufferu) a postupně, vzhledem k pozorovateli, se vykreslí všechny průhledné objekty od nejvzdálenějšího k nejbližšímu. Dále už, podle požadavků, se opět může zaponout zápis do hloubkového bufferu.
-29-
Kapitola 8
Závěr Úlohou této práce bylo popsat problematiku spojenou s tvorbou komponent ve vývojovém prostředí Borland Developer Studio 2006 a navrhnout a vytvořit ukázkové komponenty, pracující s grafickým rozhraním OpenGL, na kterých se daná problematika demonstrovala. Někdy k dokonalému pochopení určitých aspektů bylo zapotřebí vysvětlit rysy, týkající se zobrazování trojrozměrné grafiky. Text neměl sloužit jako detailní nápověda k tak rozsáhlému tématu, což programování komponent bezesporu je, ale měl pouze nastínit celkový koncept a návrhy, které by vedly k úspěšné realizaci komponent. Byl vysvětlen pojem komponenta a z jakých částí se komponenta skládá. Byly představeny editory vlastností a editory komponent, které umožňují vytěžit maximum z prostředků, které vývojové prostředí Delphi nabízí a přidávájí komponentám zajímavé a užitečné vlastnosti. Dále byly uvedeny důležité kroky, jež jsou třeba učinit v průběhu celého procesu od návrhu komponent až k jejich distribuci. Trojrozměrná počítačová grafika je velice rozsáhlá oblast, kterou není prakticky možné v této práci celou obsáhnout. Popisované komponenty nabízí pouze zlomek toho, co 3D grafika ze svých možností naskýtá. V budoucnu by komponenty mohly nabídnout například ucelený částicový systém, bump-mapping nebo detekce kolizí mezi objekty.
-30-
Použitá literatura a zdroje [1]
Shreiner, D., Woo M., Neider J., Davis T. OpenGL Průvodce programátora, Brno: Computer Press, 2006. 1. vyd., 696 str. ISBN: 80-251-1275-6
[2]
Miller,T., Powell, D. Mistrovství v Delphi 3, strana 524. Brno: Computer Press 1998. 1043 str. ISBN 80-7226-110-X.
[3]
THandle.Ayres J., Bowden D., Diehl L., Dorcas P., HarrisonK., Mathes R., Mathes O., Tobin M. The Tomes Of Delphi 3: Win32 Graphical API, Wordware Publishing 1998. 879 str. ISBN: 1556226101.
[4]
TCustomControl. Delphi 4 :podrobný průvodce programátora. Edited by Marco Cantú - Pavel Hofmann - Jiří Hynek. 1. vyd. Praha : Grada, 1999. 638 s. ISBN 80-7169-800-8.
[5]
WM_DESTROY. Win 32 API - průvodce vývojáře :kompletní reference programátora pro Windows 95 a Windows NT. 1. vyd. Brno : UNIS publishing, 1997. s. 671- 141. ISBN 80-86097-06-4
[6]
Project JEDI – The Delphi API Library [online].[citováno 24.4.2010]
[7]
SourceForge. DevIL – A full featured cross-platform image library [online]. DevIL - Developer's Image Library. 19.10.2009 [citováno 5.4.2010]
[8]
SULACO[online]. 14.11.2006 [citováno 23.4.2010]
Použitá obrazová příloha: obrázek 1 – kabinetní projekce. doc. Ing. Jiří Sochor, Csc., prezentace k předmětu PB009 Základy počítačové grafiky obrázek 2 – kavalírská projekce. doc. Ing. Jiří Sochor, Csc., prezentace k předmětu PB009 Základy počítačové grafiky obrázek 3 – ukázka mesh objektů. Dostupný online z:
-31-
obrázek 4,5,6 – typy světel. Vlastní tvorba obrázek 7 a 8 – ukázka texturovacích filtrů GL_NEAREST a GL_LINEAR. Dostupný online z: Při vývoji komponent popisovaných v této práci byly použity některé zdrojové soubory od jiných autorů nebo fragmenty kódů volně k dispozici v rámci ukázek a tutoriálů na internetu. Jedná se především o konverze hlavičkových souborů knihovny OpenGL a o mechanismus nahrávání externích souborů (rastrových obrázků a souborů s modely). Project JEDI(http://www.delphi-jedi.org): konverze hlavičkových souborů OpenGL do jazyka Pascal a matematické funkce pro 3D. (gl.pas, glu.pas, glext.pas, sdl.pas, vectorTypes.pas, vector.pas) Jan Horn (http://www.sulaco.co.za): načítání souborů OBJ a rastrových obrázků. (OBJLoader.pas, fragment kódu použitý v modulu DGLBitmapFormats.pas) dipl. Ing.Mike Lischke (http://www.soft-gems.net): načítání souborů 3DS a modifikovaný streamovací mechanismus objektů (File3DS.pas, Const3DS.pas, Utils3DS.pas, Types3DS.pas, DGLObjectList.pas, streamingSystem.pas)
-32-
Příloha A
Obsah CD
source – zdrojové soubory balíku DelphiOpenGL.dpk (.PAS a .DPK)
BDS2006_bin – binární soubory přeloženého balíku DelphiOpenGL.dpk (.DCP a .BPL)
demos – ukázkové programy v podobě Delphi projektů (.DPR) a spustitelných souborů (.EXE). demos/basic – demonstrace tvorby základních objektů, světel a kamerových efektů demos/tex – práce s materiály a texturami demos/env – ukázka tvorby venkovního prostředí (terén, skybox) demos/anim – jednoduchá animace objektů demos/meshes – program pro načítání objektů ze souborů 3DS a OBJ
docs – elektronická podoba textu
-33-