85
Rok akademicki 2010/2011 Politechnika Warszawska Wydział Elektroniki i Technik Informacyjnych Instytut Informatyki PRACA DYPLOMOWA MAGISTERSKA Stanisław Ignacy Gąsiorowski Silnik graficzny 3D Opiekun pracy dr inż. Tomasz Martyn Ocena: ..................................................... ................................................................ Podpis Przewodniczącego Komisji Egzaminu Dyplomowego

Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

  • Upload
    dangbao

  • View
    237

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

Rok akademicki 2010/2011

Politechnika Warszawska

Wydział Elektroniki i Technik Informacyjnych

Instytut Informatyki

PRACA DYPLOMOWA MAGISTERSKA

Stanisław Ignacy Gąsiorowski

Silnik graficzny 3D

Opiekun pracy

dr inż. Tomasz Martyn

Ocena: .....................................................

................................................................

Podpis Przewodniczącego

Komisji Egzaminu Dyplomowego

Page 2: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

Streszczenie

Przedmiotem niniejszej pracy jest rozbudowa silnika graficznego. W pracy przedstawiono

nową architekturę silnika, opisano jego działanie, a także przetestowano przydatność do

tworzenia gier komputerowych. Silnik napisano w językach C++, CG oraz GLSL

i przystosowano do pracy w systemach Windows oraz Linux. Do generowania grafiki

wykorzystana została biblioteka OpenGL. Z silnikiem zintegrowano silnik fizyki NVidia

PhysX. Dodano również nowe elementy: mechanizm zarządzania sceną, efekty

cząsteczkowe, mechanizm renderowania map kubicznych. Jednym z ciekawszych

elementów jest autorski system portali, którego implementacja sprawiła najwięcej

trudności. Powstała także baza materiałów, które można nakładać na obiekty.

Zastosowanie kubicznych map otoczenia pozwoliło stworzyć materiały przezroczyste

i odbijające. Opracowano szereg efektów, z których najbardziej złożone to symulacja głębi

ostrości, rozmycie ruchu i mgła wolumetryczna. Pozostałe efekty to m.in.: stereoskopia,

rozmycie, wyostrzenie, poświata, mieszanie kolorów. Wygodny w obsłudze interfejs

użytkownika pozwala na proste zarządzanie efektami.

Słowa kluczowe: silnik graficzny, grafika 3d, efekty, głębia ostrości, mgła, rozmycie

ruchu, portale, efekty cząsteczkowe, interfejs użytkownika, OpenGL, shadery.

Abstract

Title: 3D graphics engine

The subject of this thesis is development of graphics engine. Thesis describes new engine

architecture and the usability to create games. Engine was written in C++, CG and GLSL

and uses OpenGL for rendering graphics. It can work in both Windows and Linux. NVidia

PhysX physics engine was integrated with the engine. New elements were added, such as:

scene management, particle effects, environmental cube map rendering. One of the most

interesting elements is a portal system, which was created from the beginning. The engine

has set of base materials, which can be applied to objects. Transparent and reflective

materials can be created using environmental cube maps. Many effects were created, of

which the most interesting are depth of field, motion blur and volumetric fog. There are

also other effects such as: blur, sharpen, glow, color blend. The engine offers a useful

interface for managing effects.

Keywords: graphics engine, 3d graphics, effects, depth of field, fog, motion blur, portals,

particles, user interface, OpenGL, shaders.

Page 3: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

Spis treści

1 Wprowadzenie ...................................................................................................... 4

1.1 Przegląd istniejących rozwiązań ............................................................................... 5

1.1.1 Podział przestrzeni .............................................................................................. 6

1.1.2 Fizyka .................................................................................................................. 7

1.1.3 Portale ................................................................................................................. 7

1.2 Cel i zakres pracy ....................................................................................................... 8

2 Architektura ......................................................................................................... 9

2.1 Założenia sprzętowe ................................................................................................... 9

2.2 Silnik ............................................................................................................................ 9

2.3 Narzędzia ................................................................................................................... 11

3 Implementacja .................................................................................................... 14

3.1 Drzewo ósemkowe .................................................................................................... 14

3.2 Silnik fizyki NVidia PhysX ...................................................................................... 18

3.3 Portale ....................................................................................................................... 23

3.3.1 Wprowadzenie ................................................................................................... 23

3.3.2 Renderowanie sceny za portalem ...................................................................... 23

3.3.3 Integracja fizyki z mechanizmem portali .......................................................... 25

3.3.4 Fałszywe kolizje ................................................................................................ 28

3.3.5 Skalowanie portali ............................................................................................. 29

3.3.6 Kamera a portale ............................................................................................... 29

3.3.7 Oświetlenie a portale ......................................................................................... 30

3.4 Kubiczne mapy otoczenia ........................................................................................ 31

3.5 Efekty cząsteczkowe ................................................................................................. 35

4 Efekty i materiały ............................................................................................... 39

4.1 Stereoskopia .............................................................................................................. 39

4.2 Głębia ostrości .......................................................................................................... 42

4.3 Rozmycie ruchu ........................................................................................................ 57

4.4 Mgła ........................................................................................................................... 63

4.5 Inne efekty ................................................................................................................. 71

4.6 Materiały ................................................................................................................... 72

5 Przykładowa gra ................................................................................................ 81

6 Podsumowanie .................................................................................................... 83

Bibliografia ................................................................................................................ 85

Spis załączników ..................................................... Błąd! Nie zdefiniowano zakładki.

Page 4: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

~ 4 ~

1 Wprowadzenie

Grafika komputerowa to dziedzina zajmująca się tworzeniem grafiki za pomocą

komputera. Istnieje ona od czasów pierwszych komputerów zdolnych przedstawiać obrazy.

Od lat używana jest przez artystów jako ich kolejne narzędzie, obok pędzla i ołówka.

Grafikę komputerową wykorzystuje się m.in. do wzbogacania filmów o efekty specjalne,

tworzenia gier komputerowych, a także obrazowania medycznego.

Grafika komputerowa może być dwuwymiarowa lub trójwymiarowa. Pojęcie

trójwymiarowości w grafice komputerowej można interpretować na kilka sposobów. W tej

pracy będzie oznaczało przedstawianie trójwymiarowych obiektów na dwuwymiarowej

płaszczyźnie ekranu za pomocą odpowiednich rzutowań perspektywicznych.

Gałęzią grafiki komputerowej, na której skupia się niniejsza praca, jest grafika czasu

rzeczywistego. Polega ona na tym, że obrazy muszą być wygenerowane w czasie

rzeczywistym, co oznacza, że muszą zostać stworzone w skończonym, krótkim czasie.

W praktyce jest to czas rzędu kilkudziesięciu milisekund. Właśnie dlatego jednym

z kluczowych parametrów opisujących system tworzący grafikę jest liczba klatek

generowanych na sekundę (FPS – ang. frames per second). W grafice czasu rzeczywistego

jakość grafiki nie jest najważniejsza i może zostać zdegradowana w celu zwiększenia

liczby klatek generowanych na sekundę. W tradycyjnej grafice komputerowej ilość czasu,

który można poświęcić na wygenerowanie obrazu jest teoretycznie nieograniczona, dlatego

też można stworzyć obrazy w dowolnie wysokiej jakości. W grafice czasu rzeczywistego

ograniczenie narzucone na czas generowania jest bardzo ostre i dlatego grafika ta stanowi

duże wyzwanie dla programisty [1].

Grafika czasu rzeczywistego znajduje zastosowanie tam, gdzie wymagana jest

interakcja z użytkownikiem. Zdarzają się jednak wyjątki, takie jak animacje generowane

w czasie rzeczywistym, pokazujące możliwości sprzętu oraz programistów. Systemy

szkoleniowe pilotów cywilnych, jak i wojskowych, używają systemów grafiki czasu

rzeczywistego dla symulatorów lotniczych. Programy dla architektów pozwalają na

wizualizację m.in. budynków i jachtów, stwarzając możliwość odbycia spaceru przez

klienta, np. po swoim przyszłym mieszkaniu. Jednak największą dziedziną wykorzystującą

grafikę czasu rzeczywistego są gry komputerowe. To właśnie dzięki nim przemysł sprzętu

komputerowego rozwija się tak szybko. Coraz większe wymagania gier powodują bardzo

dynamiczny rozwój sprzętu.

Gry komputerowe wyznaczają kierunek rozwoju sprzętu komputerowego i najlepiej

pokazują jego możliwości. Gry dążą do fotorealizmu i bardzo szybko zbliżają się do

poziomu obrazu znanego z filmów.

Gry komputerowe tworzone są w oparciu o tzw. „silnik graficzny”. W przypadku

gier trójwymiarowych rola silnika graficznego w dzisiejszych produkcjach nie ogranicza

się tylko do generowania grafiki. Silnik graficzny pełni kluczową rolę w całym programie.

To od możliwości silnika zależy, jakie gry można na nim zrobić. Niestety, pojęcia silnika

graficznego i silnika gry są coraz częściej mylone, a najpopularniejsze staje się używanie

samego określenia „silnik”, bez podawania jakiego jest on rodzaju. Jest to silnik

„do wszystkiego”. Poprawną nazwą będzie w tym wypadku silnik gry, natomiast silnik

graficzny jest raczej modułem graficznym silnika gry. Silnik gry integruje wszystkie

Page 5: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

1.1 Przegląd istniejących rozwiązań

~ 5 ~

elementy, takie jak moduł graficzny, dźwiękowy, silnik fizyki, interfejs, mechanizmy

wejścia-wyjścia, komunikację sieciową oraz wiele innych, w jedną całość.

Silnik stanowi podstawę, na której buduje się grę komputerową. Wprowadza on

wyższy poziom abstrakcji dla projektanta gry, ukrywając szczegóły implementacyjne

poszczególnych elementów. Udostępnia tak abstrakcyjne byty jak: scena, obiekt, kamera,

światło, tekstura. Programista gry nie musi zastanawiać się nad implementacją tych

elementów. Dzięki takiemu rozdzieleniu możliwe jest stworzenie gry na bazie silnika,

którą potem można przenieść na inne platformy, zmieniając tylko implementację silnika.

Silnik stanowi również warstwę między grą a systemem operacyjnym.

1.1 Przegląd istniejących rozwiązań

Na temat grafiki komputerowej czasu rzeczywistego powstało bardzo wiele książek,

artykułów i stron internetowych. Można na nich znaleźć kursy dla początkujących,

chcących programować grafikę komputerową, jak również informacje dla osób na każdym

poziomie zaawansowania.

Pierwszym zagadnieniem jest budowa samego silnika. Na temat jego tworzenia

można znaleźć wzmianki w wielu książkach, jednak skupiających się głównie na tym

zagadnieniu jest niewiele. Warto wymienić [2], [3] i [4]. Znaleźć można również wiele

artykułów na stronach internetowych, niektóre z nich zawierają sporo użytecznych

informacji, np. [5] i [6].

Kolejnym zagadnieniem jest przeanalizowanie dostępnych bibliotek graficznych.

Dzisiaj w praktyce liczą się tylko dwie technologie: OpenGL oraz DirectX. Wybór między

nimi jest trudny. Obydwie udostępniają podobne możliwości, jednak na niekorzyść

technologii DirectX przemawia fakt, że wszystkie nowe funkcje dostępne od 10. wersji

biblioteki dostępne są tylko na systemach Windows Vista i Seven. OpenGL natomiast

udostępnia wszystkie swoje możliwości niemalże na każdym systemie operacyjnym.

OpenGL dostępny jest na wszystkie wersje systemu Windows, a także na systemach Linux

oraz Mac OS [7].

Przy tworzeniu silnika gry warto przyjrzeć się profesjonalnym silnikom i ich

możliwościom. Bardzo wartościowe wskazówki co do tworzenia silnika dostarczają kody

źródłowe trzech części gry Quake, udostępnione publicznie [8].

Silnik Unreal Engine 3.0 jest jednym z najlepszych silników dostępnych na rynku.

Warto wzorować się na możliwościach i elastyczności jakie oferuje [9].

Następnie warto wymienić kolejne wersje silnika ID Tech o numerach 4 i 5,

o których można się dowiedzieć od Johna Carmacka. Zamieszcza on swoje wypowiedzi

odnośnie problemów podczas pisania silników na wielu stronach internetowych [10, 11].

Kolejnym silnikiem wartym uwagi jest CryENGINE, aktualnie w wersji 3 [12].

Warto zwrócić również uwagę na silnik Unigine [13]. Jego autor tworzył małe

programy z dziedziny grafiki trójwymiarowej i zamieszczał je na swojej stronie.

Nabierając doświadczenia zaczął pisać swój silnik, aż w końcu postanowił rozwinąć go do

postaci komercyjnej. Warto zatem przejrzeć programy wcześniej udostępniane przez tego

autora.

Page 6: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

1.1 Przegląd istniejących rozwiązań

~ 6 ~

1.1.1 Podział przestrzeni

Podział przestrzeni i zarządzanie geometrią jest bardzo ważną składową silnika. Od

mechanizmu zarządzania geometrią wymaga się, by można było dodawać do niego

obiekty, usuwać je, a także zmieniać ich położenie i wielkość. Mechanizm ten odpowiada

również za to, które obiekty są widoczne przez kamerę lub światło.

Najpopularniejszymi metodami dzielenia przestrzeni są m.in. drzewa BSP

(ang. binary space partition), drzewa ósemkowe (ang. octree) i portale. Drzewa BSP oraz

ósemkowe dzielą przestrzeń na komórki (sektory), do których przypisują obiekty.

Zastosowanie portali polega natomiast na podzieleniu sceny na sektory, a między nimi

umieszczenie portali – przejść miedzy sektorami. Drzewa BSP opisane są w wielu

źródłach, m.in. [14], [15] i [16]. Opis portali można znaleźć w [17] oraz [18]. Drzewa

ósemkowe opisane są w [19].

Metody używające drzew BSP i ósemkowych dobrze sprawdzają się dla otwartych

przestrzeni (ang. out-door). Natomiast portale najlepiej spisują się w przestrzeniach

zamkniętych (ang. in-door), ponieważ mogą być z powodzeniem umieszczone

np. w drzwiach pomiędzy pomieszczeniami (sektorami). Jednak drzewa BSP i ósemkowe

również sprawdzają się w przestrzeniach zamkniętych.

Możliwe jest także łączenie metod. Np. dla zamkniętych przestrzeni, w których

niektóre pomieszczenia (sektory) mają duże rozmiary, można zastosować portale dzielące

pomieszczenia, a także drzewa ósemkowe dla podziału dużych pomieszczeń.

Przeważnie sceny obsługiwane przez silnik są typu zamkniętego lub otwartego.

Wtedy wybór mechanizmu podziału przestrzeni jest łatwy. Jednak czasami sceny są

otwarte, lecz zawierają w sobie przestrzenie zamknięte, np. planszą gry jest wyspa, na

której znajduje się wejście do podziemnej bazy. Wtedy przestrzenią otwartą jest teren

wyspy, a przestrzenią zamkniętą podziemna baza. Elementem łączącym te dwie

przestrzenie mogą być drzwi. Najlepszym rozwiązaniem jest wtedy łączenie metod,

np. do przestrzeni otwartej drzewa ósemkowego, a dla przestrzeni zamkniętej systemu

sektorów i łączących je portali. Drzwi do bazy mogą być zatem portalem, który łączy

przestrzeń otwartą z przestrzenią zamkniętą. Jednak podział sceny na przestrzeń otwartą

i zamkniętą musi być wykonany przez projektanta poziomu, ponieważ automatyczne

wykrycie gdzie przestrzeń jest zamknięta, a gdzie otwarta jest wyjątkowo trudnym

zadaniem.

Największym problemem przy łączeniu mechanizmu portali z np. drzewem

ósemkowym jest określenie czy kamera znajduje się w przestrzeni zamkniętej czy otwartej.

Jeśli jednak założymy, że przestrzeń zamknięta będzie opisana przez projektanta poziomu

za pomocą w miarę prostej bryły, sprawdzenie, iż znajdujemy się w przestrzeni zamkniętej

będzie wymagało tylko określenia czy jesteśmy w środku bryły. Jeśli tak, to widoczne

obiekty określamy za pomocą mechanizmu portali, a jeśli nie – to za pomocą drzewa

ósemkowego. Jeśli natomiast widoczny jest portal łączący obie przestrzenie, to

w zależności, w której przestrzeni jesteśmy, dodajemy obiekty, które są widoczne przez

portal z drugiej przestrzeni.

Page 7: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

1.1 Przegląd istniejących rozwiązań

~ 7 ~

1.1.2 Fizyka

Silnik fizyki wzbogaca silnik gry o dodatkowe cechy nadające realności rozgrywce.

Dzięki niemu możliwe jest symulowane zachowania obiektów, które jest wystarczająco

zgodne z zachowaniem obiektów w prawdziwym świecie. Umożliwia także tworzenie gier

opartych na fizyce, w których istotnym elementem jest właśnie zachowanie obiektów lub

cieczy. Można na przykład stworzyć prostą grę polegającą na rzucaniu przedmiotów do

celu, graniu w bilard lub kręgle albo balansowaniu na linie.

Silnik fizyki odpowiada za symulowanie zachowania obiektów. Należy dostarczyć

mu opis sceny, w tym obiektów i materiałów z jakich są wykonane, a także parametrów

symulacji, takich jak siła i kierunek grawitacji czy odcinek czasu jaki ma być symulowany.

Najpopularniejszymi silnikami używanymi w grach są Havok Physics [20], który jest

jednak silnikiem płatnym, oraz NVidia PhysX [21]. Używane są także mniej profesjonalne

silniki, takie jak Newton Dynamics [22], które również oferują duże możliwości.

Wiele silników gier zawiera własne implementacje silnika fizyki. Pozwala to na

dowolną kontrolę nad kodem silnika, jednak wymaga opracowania takiego silnika,

co związane jest z dużym nakładem pracy.

1.1.3 Portale

Portale opisane w niniejszej pracy nie są związane z portalami wykorzystywanymi

do podziału przestrzeni. Zbieżność nazw może być myląca, jednak w niniejszej pracy nie

będziemy się zajmować portalami w podziale przestrzeni.

Portale, opisane w niniejszej pracy, są to teleporty, przez które mogą się

przedostawać obiekty oraz światło. Portale łączą się w pary, tzn. każdy portal ma

odpowiadający mu drugi portal. Często portal, o którym mówimy, będziemy nazywać

portalem wejściowym, a sparowany z nim portal – wyjściowym. Patrząc przez portal

widzimy w nim to, co znajduje się za odpowiadającym mu portalem wyjściowym. Portal

jest płaski i ma ograniczoną powierzchnię, np. prostokąta.

Nie istnieje praktycznie żadna literatura opisująca portale, przez które może

przedostawać się światło oraz obiekty symulowane przez silnik fizyki, i które to portale

same mogą się poruszać. Portale takie pojawiły się w grze „Prey” filmy 3D Realms, jednak

nie umożliwiały przemieszczania obiektów przez portale. Dopiero gra „Portal” firmy

Valve pokazała potencjał portali. Gracze mogli przemieszczać obiekty pomiędzy

portalami. Gra zawierała zagadki logiczne, do rozwiązania których trzeba było

wykorzystać portale, np. mając przed sobą mur nie do przeskoczenia, można było stworzyć

jeden portal w podłodze, a drugi na suficie za przeszkodą i wskoczyć w portal na podłodze,

wyskakując w ten sposób za przeszkodą.

Jednym ze źródeł informacji o portalach są komentarze twórców gry „Portal”,

zawarte w samej grze. Pewne wypowiedzi twórców na temat technologii stojących za

portalami można znaleźć w [23].

Page 8: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

1.2 Cel i zakres pracy

~ 8 ~

1.2 Cel i zakres pracy

Celem pracy jest rozszerzenie silnika grafiki trójwymiarowej, opracowanego

w ramach pracy inżynierskiej, o nowe elementy, takie jak:

integracja z silnikiem fizyki NVidia PhysX,

mechanizm zarządzania geometrią sceny,

system portali (w rozumieniu teleportów wspomnianych w pkt. 1.1.3),

efekty cząsteczkowe,

mechanizm renderowania map kubicznych.

Kolejnym celem pracy jest stworzenie bazy wielu prostych efektów, a także kilku

bardziej złożonych efektów, takich jak: głębia ostrości, rozmycie ruchu, specjalna mgła

oraz mgła wolumetryczna.

Dodatkowym celem jest stworzenie bazy materiałów, takich jak: różne rodzaje

metalu, drewno, szkło, kamień.

Ponadto celem jest przystosowanie silnika do pracy w systemie Linux.

Ostatnim celem jest stworzenie przykładowej gry opartej na rozbudowanym silniku.

Praca ma charakter integracyjny z elementami innowacyjności. Opracowany silnik

składa się z wielu małych elementów, które należy zintegrować w jedną spójną całość.

Innowacyjnymi elementami są: system zarządzania efektami, bardziej złożone efekty, takie

jak głębia ostrości, której w ramach niniejszej pracy poświęcono najwięcej czasu, a także

system portali, który jest w całości autorski.

Page 9: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

~ 9 ~

2 Architektura

2.1 Założenia sprzętowe

Architektura silnika została zaprojektowana na komputer klasy PC wyposażony

w kartę graficzną zgodną z Shader Model 3. Mogą to być m.in. karty NVidia GeForce

począwszy od serii 6xxx. Niektóre funkcje silnika, takie jak bardziej złożone efekty, będą

wymagały jednak bardziej nowoczesnych kart, obsługujących Shader Model 4, np. karty

GeForce serii 8xxx. Silnik będzie starał się wykorzystać możliwości komputera, na którym

zostanie uruchomiony.

Pozostałe komponenty komputera nie odgrywają tak istotnej roli jak karta graficzna.

Dlatego też, do poprawnego działania wystarczy procesor odpowiadający mocą

procesorowi AMD Athlon 64 3000+, a także przynajmniej 1 GB pamięci RAM.

2.2 Silnik

Opracowana architektura silnika składa się wielu elementów, są to m.in.:

interpreter,

konsola,

zbiór funkcji matematycznych,

warstwa obsługująca system operacyjny,

system obsługi shaderów,

mechanizm przechowywania scen,

mechanizm obsługi materiałów,

mechanizm wczytywania tekstur,

system animacji,

system zarządzania sceną i pamięcią,

mechanizm sterowania światłami i cieniami,

mechanizm wczytywania scen z plików,

mechanizm renderujący,

mechanizm zarządzania buforami,

silnik przetwarzania efektów,

silnik fizyki,

mechanizm efektów cząsteczkowych,

system portali.

Schemat budowy silnika przedstawiono na rysunku 1. Przedstawia on relacje między

poszczególnymi elementami. Każde połączenie między elementami oznacza, że dany

element komunikuje się z drugim elementem lub korzysta z jego mechanizmów.

Ponad wszystkimi elementami znajduje się zarządca. Może się on komunikować ze

wszystkimi elementami składającymi się na silnik.

Page 10: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

2.2 Silnik

~ 10 ~

Rys. 1 Schemat budowy silnika.

Dla zwiększenia czytelności nie narysowano wszystkich połączeń do „parametrów”, jednak element ten

łączy się ze wszystkimi innymi.

Parametry silnika są to zmienne, które można umieścić w dowolnym elemencie

silnika, dzięki czemu można je kontrolować za pomocą interfejsu użytkownika oraz

interpretera. Parametry te są dostępne z każdego elementu silnika.

Mechanizm zarządzania sceną odpowiada za przechowywanie sceny, w której

znajdują się obiekty, światła i kamery. Jego zadaniem jest zarządzanie obiektami,

używając mechanizmów podziału przestrzeni. Odpowiada również za zarządzanie

pamięcią, potrzebną do przechowywania obiektów.

Renderer korzysta z mechanizmu zarządzania sceną w celu uzyskania danych

o obiektach, które należy wyrenderować.

Mechanizm zarządzania sceną komunikuje się z silnikiem fizyki w celu przekazania

kontroli nad obiektami do silnika fizyki, a następnie na odczytaniu obliczonego położenia

obiektów z silnika fizyki. Pracuje on równolegle z innymi elementami silnika.

Mechanizm zarządzania buforami używany jest przez efekty, które uzyskują od

niego bufory, do których zlecają renderowanie sceny rendererowi.

Renderer korzysta z mechanizmu zarządzania materiałami w celu uzyskania

informacji o materiałach, których należy użyć do wyrenderowania poszczególnych

obiektów.

Page 11: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

2.3 Narzędzia

~ 11 ~

System obsługi shaderów używany jest przez renderer, efekty oraz materiały w celu

wykorzystania shaderów do renderowania. System ten udostępnia mechanizmy do

kompilowania shaderów, przekazywania do nich parametrów i używania shaderów.

Silnik efektów zarządza efektami i wykonuje je w odpowiedniej kolejności.

System portali współpracuje z mechanizmem zarządzania sceną oraz silnikiem fizyki

w celu symulacji zachowania obiektów przechodzących przez portale. Renderer

współpracuje z systemem portali w celu renderowania widoków prze portale.

System efektów cząsteczkowych opiera się na silniku fizyki do symulowania

zachowania cząsteczek. Może dynamicznie zarządzać światłami oraz potrzebuje dostępu

do danych obiektów. Renderer wykorzystywany jest do renderowania cząsteczek,

natomiast efekty mogą zarządzać sposobem ich renderowania.

Konsola oraz graficzny interfejs użytkownika mają kontrolę nad parametrami silnika.

Dodatkowo interfejs ma dostęp do mechanizmu zarządzania sceną, ponieważ umożliwia

manipulację obiektami znajdującymi się na ekranie.

Nadrzędną kontrolę nad działaniem silnika sprawuje interpreter, który interpretuje

skrypt sterujący zachowaniem silnika.

Poszczególne elementy nie są sztywno oddzielone od siebie, wiele z nich jest ze sobą

w bardzo ścisłej zależności. Dla przykładu, system portali jest praktycznie w całości

zintegrowany z mechanizmem zarządzania sceną oraz rendererem.

Diagram przebiegu wykonania dla renderowania poszczególnej klatki przedstawiono

na rysunku 2. Pokazuje on jak długa jest droga od komendy interpretera „go()” do

wyrenderowanej klatki. Wszystkie elementy muszą ze sobą współdziałać.

Diagram nie zawiera wszystkich wywołań jakie zachodzą podczas renderowania

pojedynczej klatki, jednak dobrze oddaje ogólne zależności między elementami oraz

sekwencję przebiegu.

Większość z elementów silnika została opisana w pracy inżynierskiej. Całkowicie

nowymi elementami są silnik fizyki oraz system portali. Elementy wcześniej opracowane

zostały usprawnione, a także przebudowane. Gruntownie rozbudowany został mechanizm

zarządzania sceną, do którego zaimplementowano luźne drzewa ósemkowe. Integracja

z silnikiem fizyki oraz implementacja systemu portali wymagały wielu zmian

w elementach takich jak renderer, mechanizm zarządzania sceną czy efekt renderujący.

W celu przystosowania silnika do działania w systemach Linux oraz Windows

stworzona została warstwa pośrednicząca między właściwym silnikiem a systemem

operacyjnym. Warstwa ta została napisana w dwóch wersjach, oddzielnie dla każdego

systemu. Odpowiada ona za czynności takie jak: inicjowanie aplikacji, tworzenie okna,

obsługa zdarzeń systemowych oraz odczytywanie wejścia, takiego jak wciśnięte klawisze

oraz ruch myszki.

2.3 Narzędzia

Silnik graficzny wymaga dodatkowych narzędzi, z których najważniejszy jest edytor

modeli, scen i animacji. W produkcji gier komputerowych wykorzystywany jest on do

tworzenia modeli występujących w świecie gry, plansz, animacji postaci itp. Wiele

silników ma opracowane własne edytory, jednak stworzenie ich pochłania bardzo

Page 12: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

2.3 Narzędzia

~ 12 ~

Rys. 2 Diagram sekwencji dla wyrenderowania pojedynczej klatki.

dużo czasu produkcji. Dlatego też postanowiono użyć dostępnego programu do edycji scen

trójwymiarowych – 3D Studio Max. Aby możliwe było przeniesienie stworzonych scen do

opracowanego silnika wymagane było stworzenie wtyczki do programu 3D Studio Max,

która pozwalałaby na eksport scen.

Opracowana wtyczka, powstała na bazie [24], pobiera dane o budowie sceny oraz

materiałach i zapisuje je w prostym binarnym formacie, który jest odczytywany przez

silnik. Eksporter pobiera informacje o obiektach, światłach, kamerach oraz kościach

w scenie, a następnie je przetwarza. Dla każdego obiektu zapisuje do pliku dane

o współrzędnych wierzchołków, koordynatach tekstur, a także wylicza współrzędne

przestrzeni stycznych wszystkich wierzchołków oraz dla każdego trójkąta wyznacza

indeksy sąsiadujących z nim trójkątów i zapisuje je do pliku. Dodatkowo tworzy wskaźniki

na wierzchołki składające się na ciągi trójkątów za pomocą biblioteki firmy NVidia

NvTriStrip i zapisuje je do pliku. Obiekty będące pod kontrolą systemu kości zawierają

Page 13: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

2.3 Narzędzia

~ 13 ~

dodatkowe dane zapisywane do pliku, określające, które kości kontrolują które

wierzchołki. Dla animowanych obiektów, świateł, kamer oraz kości tworzona jest lista

kluczy animacji i zapisywana do pliku. Obiekty mają dodatkowo różne właściwości, które

są zapisywane, takie jak rzucanie cieni, a także ciąg znaków, w którym mogą być zapisane

dowolne informacje. Pole to jest używane do zapisywania nazwy materiału, który ma być

nałożony na dany obiekt, a także parametry fizyczne dla silnika PhysX, takie jak np.

kształt i masa. Dla świateł zapisuje się dane o położeniu, typie oraz promieniu

określającemu zakres światła, a także zmienną określającą czy światło rzuca cienie. Dla

kamer zapisywana jest informacja o położeniu kamery oraz jej celu, czyli punktu,

w kierunku którego kamera jest skierowana, a także o kącie patrzenia. Następnie

zapisywane są dane materiałów, takie jak ścieżki do plików z teksturami.

Kolejnym użytym narzędziem jest edytor graficzny. Obrazy odczytywane przez

silnik w formatach JPG, BMP oraz TGA można tworzyć w dowolnym programie, jednak

jednym z najlepszych formatów dla grafiki trójwymiarowej jest format DDS [25].

Opracowany został przez firmę Microsoft jako format zapisu grafiki rastrowej dla DirectX,

może być jednak używany w silniku graficznym opartym na OpenGL. Format ten

umożliwia zapisanie skompresowanych tekstur w formatach obsługiwanych sprzętowo

przez karty graficzne. Dodatkowo można w nim zapisać mip-mapy dla tekstury. Do zapisu

plików w tym formacie został użyty program NVidia Texture Tools 2 [26]. Do odczytu

plików DDS użyto biblioteki nv_dds firmy NVidia.

Do tworzenia map wektorów normalnych użyto programu NVidia Normal Map

Filter.

Page 14: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

~ 14 ~

3 Implementacja

3.1 Drzewo ósemkowe

W opracowanym silniku zastosowano luźne drzewo ósemkowe (ang. loose octree)

[27], ponieważ zapewniają dobrą wydajność zarówno dla przestrzeni otwartych

i zamkniętych. Drzewa takie charakteryzują się lepszą wydajnością niż zwykłe drzewa

ósemkowe, ponieważ lepiej przypisują do swoich komórek małe obiekty.

Działanie drzewa ósemkowego zostanie przedstawione za pomocą drzewa

czwórkowego, gdyż schemat ich działania jest taki sam, z tą różnicą, że drzewo

czwórkowe operuje na płaszczyźnie, a drzewo ósemkowe na przestrzeni trójwymiarowej.

Przykładowe drzewo czwórkowe ilustruje rysunek 3.

Rys. 3 Drzewo czwórkowe. Dla małych obiektów drzewo dzieli się na mały komórki, a duże obiekty

umieszcza w większych komórkach.

Korzeniem drzewa jest kwadrat o wielkości takiej, aby objął wszystkie obiekty.

Przestrzeń jest rekurencyjnie dzielona na kwadraty w taki sposób, że począwszy od

korzenia, każdy kwadrat może dzielić się na cztery mniejsze kwadraty, wpisane w rodzica.

Mniejsze kwadraty mają długość boku równą połowie długości boku rodzica. Każdy

kwadrat (w przypadku drzewa ósemkowego sześcian) jest węzłem. W opracowanej

implementacji węzeł opisany jest klasą COctreeNode.

Page 15: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.1 Drzewo ósemkowe

~ 15 ~

class COctreeNode

{

public:

CVektor center;

float length;

COctreeNode *children[2][2][2];

COctreeNode *parent;

vector<CObiekt*> objects;

vector<CObiekt*> splitableObjects;

vector<CSwiatlo*> lights;

void MakeChildren();

COctreeNode() {...};

~COctreeNode(){...};

};

Aby stworzyć drzewo należy najpierw określić maksymalną liczba obiektów, które

mogą znajdować się w jednym kwadracie. Następnie należy kolejno dodawać wszystkie

obiekty do drzewa, dodając je do korzenia. Procedurę dodawania do węzła przedstawiono

na następującym listingu:

Procedura DodajObiektDoWęzła(Węzeł, Obiekt)

Jeśli Węzeł.LiczbaObiektów+1 > MAKSYMALNA_LICZBA_OBIEKTÓW

Jeśli istnieją Węzeł.Podwęzły

Jeśli Obiekt zawiera się w całości w którymś Podwęźle

DodajObiektDoWęzła(Podwęzel)

W przeciwnym przypadku

Węzeł.Obiekty.Dodaj(Obiekt)

W przeciwnym przypadku

PodzielWęzeł(Węzeł)

DodajObiektDoWęzła(Węzeł)

W przeciwnym przypadku

Węzeł.Obiekty.Dodaj(Obiekt)

Obiekty dodawane są do najmniejszych kwadratów, w których można je pomieścić.

Jeśli jednak obiekt, choćby był bardzo mały, będzie znajdował się np. na środku kwadratu

korzenia, nie będzie się on w całości mieścił w żadnym dziecku korzenia i zostanie

przypisany do korzenia. Jest to największa wada zwykłych drzew czwórkowych

(i ósemkowych), którą rozwiązują właśnie luźne drzewa.

Luźne drzewa mają rozluźnione granice kwadratów, tzn. nie tylko obiekty, które

zawierają się w całości w danym kwadracie mogą być do niego przypisane, ale mogą być

przypisane także obiekty, które znajdują się w „rozluźnionych” – powiększonych,

granicach. Opracowana metoda używa dwa razy większych granic niż rozmiar komórki.

Przynależność obiektu do rozluźnionej komórki ilustruje rysunek 4.

W przypadku rozluźnionych drzew obiekt może mieścić się w luźnych granicach

więcej niż jednego kwadratu, zostanie wtedy przypisany do najbliższego z nich.

Page 16: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.1 Drzewo ósemkowe

~ 16 ~

Rys. 4 Biały kwadrat oznacza komórkę, szare pole wokół niego oznacza jego rozluźnione granice.

Obiekty we wszystkich trzech przypadkach należą do komórki.

Na obrazie po prawej stronie mały czerwony obiekt należy do komórki zaznaczonej na biało, ponieważ

na lewo od tej komórki znajduje się o wiele większa komórka, więc lepiej jest przydzielić obiekt do

małej komórki.

Algorytm dodawania obiektów będzie wyglądał następująco:

Procedura DodajObiektDoWęzła(Węzeł, Obiekt)

Jeśli Węzeł.LiczbaObiektów+1 > MAKSYMALNA_LICZBA_OBIEKTÓW

Jeśli istnieją Węzeł.Podwęzły

Wybierz najbliższy Podwęzeł, w którego luźnych granicach mieści się Obiekt

Jeśli brak takiego Podwęzła

Węzeł.Obiekty.Dodaj(Obiekt)

W przeciwnym przypadku

DodajObiektDoWęzła(Podwęzeł)

W przeciwnym przypadku

PodzielWęzeł(Węzeł)

DodajObiektDoWęzła(Węzeł)

W przeciwnym przypadku

Węzeł.Obiekty.Dodaj(Obiekt)

Usuwanie obiektów z drzewa przebiega według następującego algorytmu:

Procedura UsuńObiekt(Węzeł, Obiekt)

Węzeł.Obiekty.Usuń(Obiekt)

Jeśli Węzeł != Korzeń

Połącz(Węzeł)

Pomocnicza procedura łącząca węzły:

Procedura Połącz(Węzeł)

LiczbaObiektów <- 0

Dla Każdego Podwęzła w Wężle

Jeśli istnieją Podwęzeł.Podwęzły

Przerwij procedurę

LiczbaObiektów += Podwęzeł.LiczbaObiektów

Jeśli LiczbaObiektów = 0

Usuń wszystkie Węzeł.Podwęzły

LiczbaObiektów += Węzeł.LiczbaObiektów

Jeśli LiczbaObiektów < MAKSYMALNA_LICZBA_OBIEKTÓW

Jeśli istnieją Węzeł.Podwęzły

Page 17: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.1 Drzewo ósemkowe

~ 17 ~

Dla Każdego Podwęzła w Wężle

Węzeł.Obiekty.Dodaj(Podwęzeł.Obiekty)

Usuń Podwęzeł

Jeśli Węzeł != Korzeń

Połącz(Węzeł.Rodzic)

Pobieranie obiektów widzianych przez kamerę polega na sprawdzeniu, które

komórki znajdują się w całości lub części w bryle widzenia, a następnie zwróceniu

obiektów przypisanych do tych komórek. Jeśli bryła widzenia kamery nie obejmuje danej

komórki to nie będzie wykonane sprawdzanie, czy któreś z dzieci tej komórki jest

widziane. Dzięki temu możliwe jest szybkie eliminowanie dużej liczby obiektów, które nie

są widziane przez kamerę. Ta sama procedura jest użyta do wyznaczania oświetlonych

obiektów. Jeśli światło jest punktowe to jego bryłą jest kula o promieniu takim, jaki jest

zasięg światła. Jeśli jest to światło reflektorowe (ang. spotlight), to bryłą jest stożek.

Procedura wyznaczania widocznych obiektów opisana jest następująco:

Procedura PobierzWidoczneObiekty(Węzeł, Bryła, Całość)

Obiekty <- Puste

Jeśli Całość = Prawda

Obiekty.Dodaj(Węzeł.Obiekty)

Dla każdego Podwęzła

Obiekty.Dodaj(PobierzWidoczneObiekty(Podwęzeł, Bryła, Prawda))

W przeciwnym przypadku

Jeśli Węzeł w całości w Bryle

Obiekty.Dodaj(Węzeł.Obiekty)

Dla każdego Podwęzła

Obiekty.Dodaj(PobierzWidoczneObiekty(Podwęzeł, Bryła, Prawda))

Jeśli Węzeł częściowo w Bryle

Obiekty.Dodaj(Węzeł.Obiekty)

Dla każdego Podwęzła

Obiekty.Dodaj(PobierzWidoczneObiekty(Podwęzeł, Bryła, Fałsz))

Zwróć Obiekty

Użycie parametru Całość pozwala na pominięcie sprawdzenia każdego z podwęzłów

i pobranie z nich wszystkich obiektów. Właśnie ten zabieg odpowiada za wysoką

wydajność tych drzew.

Implementacja luźnego drzewa ósemkowego znajduje się w plikach octree.h oraz

octree.cpp. Drzewo opisane jest klasą COctree, której listing wygląda następująco:

class COctree

{

public:

CVektor minBound, maxBound;

float len;

int maxObjectsPerNode;

COctreeNode *root;

void MakeOctree(CScene3D *scene, int maxObjectsPerNode=10);

void AddObject(CObiekt *ob);

void AddLight(CSwiatlo *sw);

Page 18: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.2 Silnik fizyki NVidia PhysX

~ 18 ~

void DeleteObject(CObiekt *ob);

void JoinNodes(COctreeNode *node);

void UpdateObject(CObiekt *ob);

void GetObjectsFrustum(CFrustum *frustum, vector<CObiekt*>

&visibleObjects, vector<COctreeNode*> &visibleNotEmptyNodes);

void GetObjectsLight(CSwiatlo *sw, vector<CObiekt*> &visibleObjects,

vector<COctreeNode*> &visibleNotEmptyNodes);

};

W opracowanej implementacji pobieranie obiektów będących w bryle widzenia

wykonuje metoda GetObjectsFrustum, natomiast pobieranie obiektów będących

w bryle światła metoda GetObjectsLight.

Wygląd przykładowego drzewa ósemkowego przedstawia poniższy rysunek.

Rys. 5 Drzewo ósemkowe w trójwymiarze. Żółte linie przedstawiają linie drzewa.

3.2 Silnik fizyki NVidia PhysX

Do integracji z opracowywanym silnikiem gry został wybrany silnik NVidia PhysX,

ponieważ staje się on bardzo popularnym silnikiem fizyki, używanym w coraz większej

liczbie gier. Jego wykonywalna wersja, a także zestaw narzędzi SDK (ang. source

development kit) są darmowe. Umożliwia on symulowanie zachowania ciał stałych, cieczy,

materiałów takich jak ubrania, lin, silników obrotowych oraz wielu innych. W tej pracy

zostały wykorzystane tylko podstawowe możliwości tego silnika fizyki.

Silnik PhysX opisuje świat za pomocą sceny oraz obiektów – aktorów, w niej

występujących. Obiekty muszą mieć opisany kształt oraz właściwości. Kształt obiektów

może być pudełkiem (ang. box), kulą (ang. sphere), obiektem wypukłym (ang. convex) lub

siatką trójkątów, zwaną również „zupą trójkątów”. Kształt obiektów może być opisany za

pomocą kilku podstawowych kształtów. Ze względów wydajnościowych najlepiej jest, gdy

obiekty będą opisane za pomocą najprostszych kształtów, czyli pudełek oraz kul. W silniku

Page 19: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.2 Silnik fizyki NVidia PhysX

~ 19 ~

NVidia PhysX nie są obsługiwane kolizje między kształtami typu siatka trójkątów,

ponieważ wymagają zbyt dużego nakładu obliczeń.

Obiekty mogą być statyczne, dynamiczne oraz kinematyczne. Statyczne obiekty raz

stworzone nie poruszają się, ani w żaden sposób nie zmieniają, aż do końca symulacji.

Nadają się doskonale do opisu stałych części planszy, takich jak podłoga, ściany

i wszystkie elementy, które nie mogą być przesuwane. Obiekty dynamiczne są

kontrolowane przez silnik fizyki, co oznacza, że oblicza on ich położenie. Obiekty

kinematyczne nie są poruszane przez silnik fizyki, można je przesuwać tylko ręcznie, tzn.

wywołując odpowiednie polecenia. Obiekty te nadają się jako obiekty, które kontroluje

gracz, takie jak np. postać gracza, która ma oddziaływać z innymi obiektami

dynamicznymi, jednak której ruchy są odgórnie kontrolowane. Symulacja fizyki odbywa

się w krokach. Do silnika PhysX podaje się odstęp czasu, który ma zostać zasymulowany.

Istotnym elementem jest również liczba wewnętrznych iteracji silnika fizyki (ang. solver)

dla każdego obiektu. Im większa liczba iteracji, tym silnik będzie symulował mniejsze

przedziały czasowe dla każdego obiektu i dzięki temu wzrośnie stabilność symulacji.

Stabilność symulacji jest kluczową kwestią przy symulacji zachowań fizycznych.

Przy braku stabilności obiekty zaczynają poruszać się nienaturalnie, skaczą,

przemieszczają się w losowe miejsca lub osiągają w jednej chwili ogromne prędkości, co

jest zwykle nie do przyjęcia. Kiedy obiekt z jakiegoś powodu stanie się niestabilny,

najczęściej drga coraz mocniej, aż w końcu wylatuje ze sceny z wielką prędkością.

W opracowanym silniku obiekty opisane są za pomocą klasy CObiekt, znajdującej

się w plikach obiekt.h oraz obiekt.cpp. Aby obiekty brały udział w symulacji do klasy tej

zostały dodane następujące parametry i metody:

int PhysXControlled;

NxActor *PhysX_Actor;

int PhysX_Movable;

float PhysX_Mass;

// 0 - box, 1 - sphere, 2 - convex, 3 - triangles, 4 - cylinder

int PhysX_Shape;

int PhysX_Static;

string PhysX_Proxy_Name;

bool PhysX_IsProxy;

NxMat34 PhysX_Mat34;

CMatrix PhysX_Matrix;

void SetMovable(bool movable);

void ReleaseShapes();

Zmienna PhysXControlled mówi czy obiekt bierze udział w symulacji.

Obiekt PhysX_Actor jest potrzebny dla silnika PhysX, który w nim przetrzymuje

wszystkie dane opisujące obiekt fizyczny.

Zmienna PhysX_Movable mówi czy obiekt może się poruszać, tzn. czy jest

dynamiczny czy kinematyczny.

Page 20: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.2 Silnik fizyki NVidia PhysX

~ 20 ~

Zmienna PhysX_Shape określa jaki obiekt ma kształt.

Zmienna PhysX_Static mówi, czy obiekt jest statyczny.

Aby stworzyć scenę w silniku PhysX należy najpierw stworzyć obiekt PhysicsSDK.

Tworzy go procedura NxCreatePhysicsSDK. Kiedy obiekt taki jest utworzony, można

przystąpić do tworzenia sceny. Scena opisana jest obiektem typu NxScene. Scenę tworzy

się metodą createScene obiektu PhysicsSDK.

Skrócony kod tworzenia sceny przedstawiony jest następującym listingu:

NxPhysicsSDKDesc desc;

NxSDKCreateError errorCode = NXCE_NO_ERROR;

PhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, NULL,

new ErrorStream(), desc, &errorCode);

NxSceneDesc sceneDesc;

sceneDesc.simType = NX_SIMULATION_SW;

sceneDesc.gravity = NxVec3(0, -9.81, 0);

PhysX_Scene = PhysicsSDK->createScene(sceneDesc);

Gdy scena jest stworzona można przystąpić do tworzenia i dodawania obiektów.

Stworzenie przykładowego pudełka prezentuje następujący listing:

NxBodyDesc bodyDesc;

bodyDesc.angularDamping = 0.5f;

NxActorDesc actorDesc;

NxBoxShapeDesc boxDesc;

boxDesc.dimensions = NxVec3(10, 40, 10);

boxDesc.localPose.t = localPose;

actorDesc.shapes.pushBack(&boxDesc);

actorDesc.body = &bodyDesc;

actorDesc.density = 100.0f;

actorDesc.globalPose.t = NxVec3(0, 0, 0);

PhysX_Actor = PhysX_Scene->createActor(actorDesc);

Obiekty opisane klasą CObiekt posiadają metodę InitPhysX, która tworzy z nich

obiekty fizyczne – aktorów, i dodaje je do sceny.

Aby wykonać krok symulacji należy wywołać metodę simulate, klasy NxScene.

Metoda ta przyjmuje wartość zmiennoprzecinkową, która opisuje długość kroku symulacji,

który ma zostać zasymulowany. Następnie należy wywołać metodę flushStream,

a potem fetchResults, która będzie czekać aż wszystkie obliczenia związane z fizyką

zostaną zakończone.

Ponieważ symulacji fizyki może odbywać się równolegle z innymi czynnościami,

np. z renderowaniem grafiki, dobrze ustawić wywołania metody symulacji oraz innych

czynności silnika w odpowiedniej kolejności. Opisuje to następujący pseudo-kod:

Page 21: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.2 Silnik fizyki NVidia PhysX

~ 21 ~

Pętla wykonania programu

Pobierz wyniki symulacji (fetchResults)

Symuluj (simulate)

Renderuj scenę

Reszta czynności silnika

Silnik fizyki będzie pracował równolegle z silnikiem graficznym renderującym

scenę, ponieważ metoda simulate tylko inicjuje symulację, która wykonywana jest

w oddzielnym wątku. Jest to istotne, gdyż symulacji fizyki może być wykonywana na

procesorze GPU lub CPU. Jeśli będzie wykonywana na GPU to w tym czasie procesor

CPU może wykonywać inne obliczenia związane z pracą silnika. Jeśli natomiast obliczenia

fizyki wykonywane są na CPU to mogą one być wykonywane na innym rdzeniu niż

pozostałe obliczenia, a także w tym czasie procesor GPU może renderować grafikę.

Renderując obiekty należy teraz uwzględnić ich położenie obliczone przez silnik

fizyki. Macierz przekształcenia pobiera metoda getGlobalPose() obiektu

PhysX_Actor. Aby zastosować tę macierz należy wykonać następujące operacje:

float macierz[16];

ob->PhysX_Actor->getGlobalPose().getColumnMajor44(macierz);

glMultMatrixf(macierz);

Aby ułatwić kontrolę nad obiektami na scenie wprowadzono ułatwienia do interfejsu

użytkownika, który był opracowany w ramach pracy inżynierskiej. Używając myszki

można teraz zaznaczyć dowolny obiekt i przesuwać go po scenie. Wymagało to

zaimplementowania wybierania obiektów.

Wybieranie obiektów zostało zaimplementowane w efekcie renderującym. Po

kliknięciu prawym przyciskiem myszy renderowany jest drugi obraz, który zawiera

16-bitowe, zmiennoprzecinkowe komponenty RGBA, które jednak nie opisują barwy,

tylko numer renderowanego obiektu oraz pozycję każdego wyrenderowanego piksela

w przestrzeni sceny. W miejscu kliknięcia myszką następuje odczytanie piksela pod

kursorem i pobranie z niego numeru obiektu, który znajduje się w tym miejscu na obrazie.

Następnie tworzony jest niewidoczny obiekt, który będzie kontrolowany przez

kursor myszy. Jest on obiektem kinematycznym, a jego położenie jest bezpośrednio

określane przez ruchy myszy. Wybrany obiekt jest łączony z obiektem kontrolowanym

ruchem myszy za pomocą złącza, które będzie trzymało połączone obiekty w stałej

odległości od siebie, jednak będzie pozwalało na dowolne ich obroty.

Page 22: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.2 Silnik fizyki NVidia PhysX

~ 22 ~

Rys. 6 Wiele obiektów symulowanych przez silnik fizyki.

Kod odpowiedzialny za stworzenie tego łącza prezentuje się następująco:

NxActorDesc actorDesc;

// pozycja piksela w scene

actorDesc.globalPose.t = pickPos.ToNxVec3();

// obiekt kontrolowany ruchem myszki

pickedActor = PhysX_Scene->createActor(actorDesc);

pickedActor->raiseBodyFlag(NX_BF_KINEMATIC);

pickedActor->raiseActorFlag(NX_AF_DISABLE_COLLISION);

NxD6JointDesc d6Desc;

d6Desc.actor[0] = ob->PhysX_Actor; // zaznaczony obiekt

d6Desc.actor[1] = pickedActor;

...

// pozwolenie na dowolne obroty

d6Desc.twistMotion = NX_D6JOINT_MOTION_FREE;

d6Desc.swing1Motion = NX_D6JOINT_MOTION_FREE;

d6Desc.swing2Motion = NX_D6JOINT_MOTION_FREE;

// zablokowanie obiektów w stałej odległości

d6Desc.xMotion = NX_D6JOINT_MOTION_LOCKED;

d6Desc.yMotion = NX_D6JOINT_MOTION_LOCKED;

d6Desc.zMotion = NX_D6JOINT_MOTION_LOCKED;

// stworzenie złącza

pickedJoint = (NxD6Joint*)PhysX_Scene->createJoint(d6Desc);

Page 23: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.3 Portale

~ 23 ~

Rys. 7 Usprawniony interfejs użytkownika umożliwia przesuwanie i rzucanie obiektami.

3.3 Portale

3.3.1 Wprowadzenie

Z implementacją portali wiąże się wiele zagadnień i problemów, które trzeba

rozwiązać. Aby zaimplementować portale do silnika graficznego, należy rozważyć

następujące zagadnienia: widok przez portal, integracja silnika fizyki z portalami,

oświetlenie a portale. Każde z tych zagadnień wymaga oddzielnego omówienia.

3.3.2 Renderowanie sceny za portalem

Aby wyrenderować scenę z portalami należy najpierw wyrenderować scenę, w której

umieszczone są portale. Następnie w miejscu portalu usunąć obraz oraz informację o głębi

i wyrenderować tam widok przez portal. Będziemy używać do tego bufora szablonu (ang.

stencil buffer) oraz shaderów. Najpierw czyścimy bufor szablonu dowolną wartością,

np. 0. Następnie ustawiamy parametry bufora szablonu tak, by renderowanie obiektów

powodowało ustawianie wartości szablonu na jakąś inną wartość, np. 128. Ustawiamy test

głębokości (ang. depth test) tak, by renderowane były tylko piksele znajdujące się w tej

samej bądź bliższej odległości od obserwatora, co piksele uprzednio będące na danej

pozycji. Następnie renderujemy obiekt portalu. Dzięki temu w buforze szablonu mamy

maskę określającą, gdzie obiekt portalu się znajduje. Teraz renderujemy ten obiekt jeszcze

raz, tym razem wyłączając test głębokości, ale używając testu szablonu tak, by

renderowane były tylko te piksele, które mają wartość szablonu równą 128. Dodatkowo

Page 24: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.3 Portale

~ 24 ~

Rys. 8 Widok przez portal.

używamy shadera pikseli, który modyfikuje głębię fragmentów na swoim wyjściu na

wielkość największą. W ten sposób w buforze głębokości sceny w miejscu portalu mamy

„pustkę”, tzn. dowolny obiekt wyrenderowany w tym miejscu przejdzie test głębokość

i będzie widoczny. Następnie renderujemy widok za portalem. Przykładowy widok przez

portal pokazany jest na rysunku8.

Aby tego dokonać musimy odpowiednio ustawić kamerę oraz użyć shadera pikseli,

po to, by uciąć renderowane obiekty przed płaszczyzną portalu. Używamy do tego shadera

sprawdzającego czy renderowany piksel znajduje się za płaszczyzną portalu i jeśli tak,

wykonywana jest instrukcja discard, oznaczająca przerwanie renderowania piksela i jego

odrzucenie [28].

Portale opisujemy przez podanie ich płaszczyzny, punktu środkowego, macierzy

obrotu oraz współczynnika skali. Macierz obrotu ma wymiar 3x3 i jej wiersze zawierają

wektory bazowe układu współrzędnych portalu. Są to wektory określające osie X, Y i Z.

Oś Z jest normalną do płaszczyzny portalu. Nazwijmy portal, przez który widok właśnie

renderujemy, portalem wejściowym, a sparowany z nim portal – wyjściowym. Aby

wyrenderować widok przez portal wejściowy należy ustawić pozorną kamerę odpowiednio

za portalem wyjściowym, tak aby patrzyła przez niego. Aby tego dokonać użyjemy

przekształcenia matematycznego opisanego wzorem:

W yjW ejW ejW yj PozPozPozRotRotPoz

))(**(''1

(1)

gdzie Poz’ oznacza położenie pozornej kamery, Rot oznacza macierz obrotu

odpowiedniego portalu, natomiast Rot’ oznacza macierz obrotu z zanegowanym trzecim

wierszem – wektorem normalnym. Poz oznacza bazowe położenie kamery, Poz

z indeksami Wej oraz Wyj oznacza pozycję środka odpowiedniego portalu.

Page 25: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.3 Portale

~ 25 ~

Mając w ten sposób ustawioną kamerę, możemy przystąpić do renderowania sceny

znajdującej się za portalem.

Przez portal można zobaczyć inny portal. Aby było to możliwe, należy po

wyrenderowanie widoku za portalem powtórzyć całą procedurę raz jeszcze, tym razem

jednak dla widoku przez pierwszy portal. Kolejne zagnieżdżenia portali renderuje się

rekurencyjnie. Portale mogą się też poruszać, ilustruje to poniższy rysunek.

Rys. 9 Poruszający się portal.

3.3.3 Integracja fizyki z mechanizmem portali

Docelowym efektem jest wchodzenie obiektu do jednego portalu i wyłanianie się

z drugiego portalu. Ponieważ portale tworzą swojego rodzaju tunel przestrzenny, który jest

nienaturalny z punktu widzenia fizyki, nie zostały one przewidziane przez autorów

większości silników fizycznych, w tym NVidia PhysX. Dlatego też trzeba opracować

rozwiązania pozwalające na interakcje obiektów z portalami.

Integracja fizyki z mechanizmem portali nie jest zdaniem prostym. W silniku NVidia

PhysX brakuje odległego łączenia obiektów, które by powiązało obiekty przechodzące

przez portal jako jeden obiekt, dlatego też trzeba to robić inaczej. Obiekt przechodzący

przez portal pojawia się w drugim portalu, tak więc trzeba ten „wychodzący” obiekt

stworzyć jako oddzielny obiekt w mechanizmie silnika fizyki.

Można stworzyć kopię obiektu wchodzącego do portalu i umieścić ją odpowiednio

do położenia portali tak, by wychodziła z drugiego portalu. Obiekt wchodzący byłby

w pełni symulowany przez silnik fizyki, natomiast obiekt wychodzący – kopia, byłby

kinematyczny i po każdym kroku symulacji ustawiany w odpowiedniej pozycji. Można

Page 26: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.3 Portale

~ 26 ~

powiedzieć, że obiekt wchodzący byłby symulowany i kontrolował położenie obiektu

wychodzącego. Ten ostatni byłby tylko obrazem obiektu wchodzącego.

Pozycję i obrót obiektu tworzymy według podobnego wzoru jak przy umieszczaniu

pozornej kamery:

W yjW ejW ejW yj PozPozPozRotRotPoz

))(**(''1

(2)

Natomiast macierz obrotu obiektu obliczamy następująco:

)**(''1

RotRotRotRot W ejW yj

(3)

gdzie Rot’ to wynikowa macierz obrotu, a Rot to macierz obrotu obiektu wejściowego.

Takie rozwiązanie sprawdza się dla mniejszych obiektów, które tylko przelatują

przez portal. Problem pojawia się przy obiektach dłuższych, które zatrzymują się w trakcie

przechodzenia przez portal, tzn. jedna część obiektu jest wystaje z jednego, a druga

z drugiego portalu. Wtedy obiekt, który jest kontrolujący (wchodzący) będzie oddziaływał

z innymi obiektami, natomiast obiekt wychodzący (kopia) będzie nieruchomy. Żadne

uderzenie ani siła przyłożona do obiektu wychodzącego nie spowoduje jego poruszenia,

ponieważ jest on obiektem kinetycznym. Przechodzenie długiego obiektu przez portal

pokazano na rysunku 10.

Rys. 10 Długi obiekt przechodzi przez portal.

Page 27: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.3 Portale

~ 27 ~

Innym sposobem będzie zamiana kontrolujących obiektów co drugi krok symulacji.

W krokach nieparzystych obiekt wchodzący będzie symulowany, a obiekt wychodzący

będzie kinematyczny i po kroku symulacji zostanie ustawiony w pozycji, którą ma obiekt

wchodzący. W parzystych krokach symulacji będzie na odwrót.

Po każdym kroku symulacji ustawiamy pozycję oraz rotację obiektu, który był

kinetyczny, na podstawie pozycji i rotacji obiektu, który był symulowany, za pomocą tych

samych wzorów, co w sposobie pierwszym. Ponadto musimy ustawić prędkość liniową

i kątową obiektu, który będzie symulowany w następnym kroku.

Prędkość liniową otrzymujemy ze wzoru:

W yjW ejW ejW yj VelVelVelRotRotVel

))(**(''1

(4)

gdzie Vel’ oznacza poszukiwaną prędkość, Vel oznacza prędkość obiektu, który był

symulowany, VelWej i VelWyj oznaczają prędkości portalów wejściowego oraz wyjściowego.

Prędkość kątową otrzymujemy ze wzoru:

)**(''1

AngRotRotAng W ejW yj

(5)

gdzie Ang’ oznacza poszukiwaną prędkość kątową, a Ang oznacza prędkość kątową

obiektu, który był symulowany.

Takie rozwiązanie spowoduje, że obydwa obiekty będą symulowane i będą

oddziaływały z innymi obiektami. Jednak tutaj pojawia się problem stabilności. Odgórne

ustawianie pozycji obiektu w co drugim kroku symulacji powoduje efekt drgania obiektu,

gdyż silnik fizyki nie ma ciągłości w kontrolowaniu obiektów i przez to nie działa tak jak

powinien. Ponadto takie rozwiązanie może powodować ciągłe drżenie obiektu, ponieważ

jeśli na jeden obiekt działa ciągle jakaś siła (np. grawitacji) i w każdym symulowanym

kroku przesuwa obiekt, a drugi nie (bo stoi na ziemi), to co drugą klatkę obiekt będzie

przesuwany raz w jedną, raz w drugą stronę.

Najlepszym sposobem byłoby stworzenie łączenia (ang. joint), które wiązałoby dwa

obiekty tak, by zachowywały się jako jeden. W silniki PhysX występują łączenia, jednak

nie nadają się one do tego zadania. W tym silniku występuje łączenie stałe (ang. fixed),

które łączy dwa obiekty tak, by zachowywały się jako jeden, jednak tylko w tej samej

przestrzeni. Oznacza to, że jeśli jeden obiekt poruszy się np. w lewo, to drugi obiekt

również poruszy się lewo. Nie nadaje się to, niestety, do zastosowania w przypadku

portali, ponieważ portale mogą się obracać. Dogodnym rozwiązaniem byłoby łączenie

portalowe (ang. portal joint) lub łączenie przestrzenne (ang. spatial joint), które

powodowałoby łączenie obiektów, gdzie każdy obiekt miałby swój własny układ

współrzędnych.1

1 Autor wysłał zapytanie do firmy NVidia o wprowadzenie takiego łączenia, jednak do tej pory nie uzyskał

żadnej odpowiedzi.

Page 28: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.3 Portale

~ 28 ~

Łączenie przestrzenne jest wprowadzone w innych silnikach fizyki np. Newton

Dynamics [22].

3.3.4 Fałszywe kolizje

Gdy obiekt przechodzi przez portal, to z punktu widzenia silnika fizyki nic się z nim

nie dzieje. Dlatego też, obiekty znajdujące się za portalem wejściowym będą kolidowały

z obiektem, który przychodzi przez portal. Silnik fizyki nie posiada informacji o tym, że

część obiektu przeszła przez portal i nie ma jej za portalem. Aby to rozwiązać, trzeba po

każdym kroku symulacji ciąć obiekt płaszczyzną portalu. Ilustracja fałszywych kolizji

znajduje się na poniższym rysunku.

Rys. 11 Fałszywe kolizje.

Na rysunki widać długi, zielony obiekt, przechodzący przez portal oraz mały, sześcienny obiekt,

z którym obiekt przechodzący przez portal nie powinien kolidować.

Cięcie obiektu w kształcie pudełka jest proste, wystarczy sprawdzić które z dwunastu

krawędzi pudełka przechodzą przez płaszczyznę portalu i uciąć je tak, by się kończyły na

płaszczyźnie portalu. Powstaje w ten sposób obiekt wypukły z niewielką liczbą

wierzchołków, który jest bez problemu obsługiwany przez silnik fizyki.

Przecięcie obiektu o kształcie kuli powoduje stworzenie również obiektu wypukłego,

jednak o dużo większej liczbie wierzchołków, zależnie od tego, jak dokładną powierzchnię

kuli chcemy uzyskać.

Natomiast przecięcie obiektu wypukłego wymaga sprawdzenia wszystkich

trójkątów, z których ten obiekt się składa. Jeśli któryś trójkąt znajduje się za płaszczyzną

portalu, jest usuwany. Jeśli w całości przed płaszczyzną, pozostaje bez zmian. Natomiast

jeśli przecina płaszczyznę portalu, musi zostać przecięty. Przecięcie takiego obiektu

wymaga większego nakładu obliczeń.

Page 29: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.3 Portale

~ 29 ~

3.3.5 Skalowanie portali

Portale mogą mieć różne wielkości. Gdy przeskalujemy macierze obrotu (lub po

przekształceniach zastosujemy odpowiednie skalowanie) otrzymamy przeskalowany układ

współrzędnych danego portalu. Pozwala to na tworzenie portali powiększających lub

pomniejszających. Przykład takiego portalu pokazano na poniższym rysunku.

Rys. 12 Portal przeskalowany. Do małego portalu wchodzi obiekt i z dużego wychodzi powiększony.

Po przeskalowaniu macierzy obrotu, widok przez portal będzie odpowiednio

przeskalowany, ponieważ pozorna kamera będzie umieszczana w odpowiednim miejscu,

symulującym skalowanie. Natomiast aby obiekt przechodzący przez portal został

przeskalowany, trzeba zastosować odpowiednie techniki.

W celu utworzenia drugiego obiektu – wychodzącego, należy go przeskalować

odpowiednio do relatywnej skali portalu, z którego wychodzi. Jeśli obiekt miał kształt

pudełka lub kuli należy tylko zmienić ich rozmiar. Jeśli natomiast miał kształt wypukły,

należy wszystkie wierzchołki przemnożyć przez odpowiedni współczynnik. Również przy

wyświetlaniu obiektu należy uwzględnić, czy został on przeskalowany.

3.3.6 Kamera a portale

Aby kamera przechodziła przez portale, a nie przez nie przenikała bez zachowania

efektu portali, trzeba po każdym ruchu kamery lub portalu sprawdzać, czy punkt poprzedni

kamery był po drugiej stronie portalu, niż jest punkt aktualny. Jeśli tak się stało należy

pozycję kamery przekształcić zgodnie ze wzorem (1).

Page 30: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.3 Portale

~ 30 ~

3.3.7 Oświetlenie a portale

Światło również może przechodzić przez portale. Same patrzenie przez portal jest

symulacją przechodzenia światła odbitego od obiektów przez portal, jednak oświetlenie

obiektów światłem przechodzącym przez portal wymaga oddzielnego rozpatrzenia.

Aby światło przechodziło przez portal i oświetlało obiekty po drugiej stronie, należy

przy renderowaniu sceny stworzyć pozorne światła – te, których światło wydostaje się

z portalu. Ustawienie pozycji pozornych świateł uzyskamy za pomocą tego samego wzoru,

który był używany do określenia pozycji pozornej kamery (1). Następnie należy dodać

sztuczny cień, w taki sposób, by zasymulować przechodzenie światła przez portal. Ilustruje

to rysunek 13.

Rys. 13 Sztuczny cień. Pozorne światło umieszczane jest za portalem. Obiekty znajdujące się za

płaszczyzną portalu (lewa strona) nie zostaną oświetlone, ani nie będą rzucały cienia (obiekt A).

Natomiast obiekty przed płaszczyzną portalu będą zachowywały się w normalny sposób (B, C).

Sztuczny cień będzie rzucany przez całą płaszczyznę podziału z wyciętym otworem o wielkości samego

portalu.

Innym zagadnieniem jest przechodzenie cieni przez portal. Obiekty znajdujące się

przed portalem, przez który świeci światło, będą rzucały cień, który przejdzie przez portal

i padnie na obiekty po drugiej stronie. Aby tego dokonać należy umieścić pozorne obiekty

za płaszczyzną portalu. Same portale również rzucają cienie, ponieważ światło przechodzi

przez portal i nie trafia w obiekty znajdujące się za nim. Ilustruje to rysunek 14.

Page 31: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.4 Kubiczne mapy otoczenia

~ 31 ~

Rys. 14 Cienie przechodzące przez portal.

3.4 Kubiczne mapy otoczenia

Jednym z ciekawszych mechanizmów opracowanych w ramach niniejszej pracy jest

ten umożliwiający tworzenie materiałów symulujących odbicia i załamania światła na

powierzchniach zakrzywionych. W grafice komputerowej takie materiały uzyskuje się

często techniką śledzenia promieni (ang. raytracing), jednak technika ta wymaga bardzo

wielu obliczeń. Można jednak symulować takie materiały w inny sposób, mniej dokładny,

jednak wystarczający dla pewnych zastosowań, takich jak gry komputerowe.

Opracowany mechanizm polega na stworzeniu kubicznej mapy otoczenia dla

każdego obiektu posiadającego materia odbijający lub przezroczysty. Mapa taka jest

sześcianem o środku w pozycji środka obiektu, na którego ścianach, stojąc w jego środku,

widać to, co widać patrząc ze środka obiektu.

Po stworzeniu wszystkich map następuje renderowanie poszczególnych obiektów.

Poszczególne mapy otoczenia są przekazywane do shaderów odpowiednich materiałów,

które mogą ich użyć w celu wyznaczenia odbić lub załamań światła na tych obiektach.

Współrzędne tekstury, potrzebne do uzyskania barwy piksela z tekstury kubicznej, są

wektorem trójelementowym, reprezentującym wektor kierunkowy, oznaczający kierunek

ze środka sześcianu. Z miejsca przecięcia wektora z sześcianem odczytywana jest wartość

barwy piksela.

Metoda ta nie jest zbyt dokładna, bowiem nie pozwala na uzyskanie odbić zgodnych

z prawami fizyki, jednak daje wystarczające przybliżenie by uzyskać wrażenie

prawdziwych odbić i załamań. Najlepiej prezentują się obiekty o kształcie kuli,

a największe niedoskonałości ujawniają obiekty, których jeden z wymiarów jest znacznie

większy niż pozostałe, np. długi metalowy długopis.

Wadą tej metody jest to, że na mapie otocznia obiektu będą widoczne tylko inne

obiekty, przez co obiekt sam w sobie nie będzie się odbijał. Ograniczenie to dotyczy

Page 32: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.4 Kubiczne mapy otoczenia

~ 32 ~

jedynie odpowiednio zakrzywionych obiektów, jednak przy większości zastosowań będzie

to niezauważalne.

Jednym ze sposobów na ukrycie niedoskonałości opracowanej metody, a także

w celu uzyskania bardziej matowych, lecz ciągle odbijających, obiektów, jest rozmycie

mapy otoczenia. Każda z 6 ścian mapy kubicznej jest rozmywana dwuprzebiegowym

filtrem Gaussa, jednak przesunięcie próbek nie jest określane w tekselach na

dwuwymiarowej teksturze, tylko w stopniach obrotu promienia kierunkowego.

Podczas renderowania map otoczenia obiektów, do wyrenderowania innych

obiektów z materiałem odbijającym, potrzebne są mapy otoczenia tych obiektów, a te

dopiero są tworzone. Dla obiektów, które jeszcze w danej klatce nie miały stworzonej

mapy otoczenia, używana jest ich mapa otoczenia z poprzedniej klatki. Taki zabieg

powoduje pewne opóźnienie w renderowaniu odbić drugiego rzędu, jednak w praktyce

opóźnienie to jest zwykle niezauważalne. Zastosowanie map z poprzedniej klatki

zaoszczędza bardzo kosztownego rekurencyjnego renderowania map dla obiektów, które

odbijają się w sobie nawzajem.

Przykładowe materiały stworzone przy pomocy opracowanego mechanizmu

zaprezentowano na poniższych obrazach. Więcej przykładów pokazano w rozdziale 4.6.

Page 33: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.4 Kubiczne mapy otoczenia

~ 33 ~

Page 34: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.4 Kubiczne mapy otoczenia

~ 34 ~

Rys. 15 Przykładowe materiały symulujące odbijanie i załamywanie promieni światła.

Page 35: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.5 Efekty cząsteczkowe

~ 35 ~

3.5 Efekty cząsteczkowe

Efekty cząsteczkowe są bardzo ważnym elementem silnika. Za ich pomocą można

symulować takie zjawiska jak dym, sypiące się iskry, ogień, eksplozje, deszcz lub śnieg,

a także wodę. Zjawiska te dodają realizmu do przedstawianych scen. Iskry mogą być

generowane przy silnym zderzaniu się obiektów ze sobą, co podkreśli siłę uderzenia.

Ogień, eksplozje i dym są nieodłącznymi elementami gier akcji. Efekty cząsteczkowe

mogą być również wykorzystywane do symulacji zachowania wody. Nie tylko

powierzchni wody, ale całej objętości cieczy.

Do symulacji zachowania cząsteczek wykorzystano silnik fizyki. Cząsteczki są

niewidocznymi kulami o małej masie, dzięki czemu nie wpływają na zachowania innych

obiektów. Jest to pożądany efekt, ponieważ cząsteczki przeważnie reprezentują obiekty

bardzo lekkie, nie wpływające na zachowania innych obiektów w scenie, takie jak

cząsteczki dymu czy iskry. Nic jednak nie stoi na przeszkodzie, by nadać im większą

masę.

Cząsteczki nie zderzają się ze sobą, ponieważ reprezentowane są przez kule

w silniku fizyki, a przedstawiają tylko bardzo małe obiekty (jak iskry) lub fragmenty

obiektów gazowych (jak dym). Brak kolizji między cząsteczkami oznacza również

znaczące zwiększenie wydajności, ponieważ cząsteczek jest bardzo wiele i kolizję między

nimi byłyby bardzo obciążające dla silnika fizyki.

Cząsteczki generowane są przez emiter cząstek. W niniejszej pracy emiter jest kołem

o określonym promieniu, umieszczonym w pewnym punkcie sceny i skierowanym

w jakimś kierunku. Wszystkie parametry emitera mogą być animowane w czasie. Emiter

wyrzuca z siebie cząstki w określonych odstępach czasu. Początkowe prędkości i kierunki

cząstek mogą być losowane z określonego zakresu.

Każda cząsteczka opisana jest przez kilka parametrów, takich jak położenie,

wielkość, pozostały czas życia i kolor. Dodatkowo cząstka może być dynamicznym

źródłem światła, wtedy opisują ją dodatkowo parametry takie jak promień światła

i natężenie. Wielkość, promień światła oraz natężenie mogą się zmieniać w trakcie życia

cząstki. Cząstka może rosnąć (np. dla dymu) lub maleć (np. dla iskier).

Emiter opisany jest przez bardzo wiele parametrów, które pozwalają skonfigurować

go w taki sposób, by generował cząstki np. dymu, iskier, ognia.

Renderowanie cząsteczki polega na wyświetleniu obiektu typu „billboard”

w miejscu położenia cząstki. Obiekt tego typu jest płaską teksturą zwróconą zawsze prosto

w stronę obserwatora. Cząsteczki mogą również być renderowane jako trzy kwadraty

o wspólnym środku, ustawione prostopadle do siebie. Taki sposób wyświetlania może

generować lepsze efekty dla wyświetlania dymu.

Implementacja efektów cząsteczkowych znajduje się w plikach particles.cpp oraz

particles.h. Klasę opisująca emiter przedstawia poniższy listing:

Page 36: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.5 Efekty cząsteczkowe

~ 36 ~

class CParticleEmiter

{

public:

vec3 position; // pozycja emitera

vec3 direction; // kierunek wyrzucania cząsteczek

vec3 upDir; // kierunek wskazujący „górę” emitera

float timeBetweenParticles; // czas pomiędzy wyrzuceniem cząstek

float lifetime; // czas życia cząstek

float radius; // średnica emitera

float timeToEmit;

float angle; // rozpiętość kierunku wyrzucania cząstek

float initialSpeed; // początkowa szybkość cząstek

float speedVariation; // wariacja początkowej szybkości

float initialSize; // początkowa wielkość cząstek

float sizeVariation; // wariacja początkowej wielkości

bool shrink; // określa, czy cząstki mają maleć czy rosnąć

float growRate; // szybkość wzrostu cząstek

bool generateLights; // czy cząstki mają być źródłami światła

vec3 lightsColorMin; // cząstki dostają losowy kolor z zakresu

vec3 lightsColorMax; // pomiędzy Min i Max

bool lightIntensityShrink; // czy natężenie światła ma maleć

float initialAttenuation; // początkowy promień światła

float attenuationVariance; // wariacja początkowego promienia

bool attenuationShrink; // czy promień światła ma maleć

vec3 applyForce; // siła działająca na cząstki

bool sortBlend; // czy sortować cząstki przed rendrowaniem

bool motionBlur; // czy zastosować rozmycie ruchu cząstek

bool perLight; // czy renderować ze światłami

void Setup(vec3 _position, vec3 _direction, vec3 _upDir,

float _radius,

float _timeBetweenParticles, float _lifetime,

float _angle, float _initialSpeed, float _speedVariation,

float _initialSize, float _sizeVariation,

bool _shrink, float _growRate, vec3 _applyForce,

bool _sortBlend, bool _motionBlur, bool _perLight);

void SetupLights(vec3 lightsColorMin, vec3 lightsColorMax,

bool _lightIntensityShrink,

float _initialAttenuation, float _attenuationVariance,

bool _attenuationShrink);

void SetTexture(const char *filename);

void SetMaterial(int _material);

deque<CParticle*> particles; // kontener przechowujący cząstki

CTexture particleTexture; // tekstura cząstek

int material;

CParticleEmiter();

~CParticleEmiter();

void Update(float timeElapsed);

void Render(CSwiatlo *sw);

};

Opis poszczególnych parametrów znajduję na powyższym listingu.

Page 37: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.5 Efekty cząsteczkowe

~ 37 ~

Poprzez metodę Setup należy określić wszystkie parametry emitera.

Metoda SetupLights ustawia parametry świateł przypisanych do cząsteczek.

Metoda SetTexture ustawia teksturę cząsteczek.

SetMaterial służy do ustawiania materiału, którym będzie używany do

renderowania cząstek.

Update wykonywany jest w każdej klatce i służy do tworzenia nowych cząsteczek,

niszczenia starych, aplikowania sił i zmieniania parametrów cząsteczek.

Metoda Render renderuje cząsteczki.

Klasę opisującą cząsteczkę przedstawia poniższy listing:

class CParticle

{

public:

vec3 position; // pozycja cząstki

float lifetime; // pozostały czas życia

float initialSize; // początkowa wielkość

float size; // aktualna wielkość

vec3 color; // kolor

float initialAttenuation;// początkowy promień światła

NxActor *PhysX_Actor; // aktor silnika fizyki PhysX

CParticleEmiter *emiter;

CSwiatlo *light; // wskaźnik do światła, jeśli cząsteczka

// jest dynamiczym światłem

float toCamDist;

vec3 lastPosition;

CParticle();

~CParticle();

void Update(float timeElapsed);

};

Przykładowe obrazy prezentujące efekty cząsteczkowe przedstawiono na rysunku 16.

Page 38: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

3.5 Efekty cząsteczkowe

~ 38 ~

Rys. 16 Efekty cząsteczkowe. Cząsteczki są małymi świecącymi punkami. Każda cząsteczka jest

dynamicznym źródłem światła.

Page 39: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

~ 39 ~

4 Efekty i materiały

4.1 Stereoskopia

Obraz generowany przez silnik grafiki trójwymiarowej jest dwuwymiarowy, płaski,

ponieważ jest tylko rzutem perspektywicznym sceny trójwymiarowej na płaską

powierzchnię monitora. Nie daje wrażenia głębi, choć oczywiście człowiek postrzega tę

głębię, wie które obiekty są bliżej, a które dalej, jednak nie odczuwa wrażenia głębi. Dzieje

się tak dlatego, gdyż obraz jest wyświetlany na płaskim ekranie. Istnieją jednak sposoby,

by obraz wyświetlany na płaskim ekranie dawał wrażenie głębi.

Techniką umożliwiającą odbieranie wrażenia głębi jest stereoskopia. Polega ona na

dostarczaniu do każdego oka innych obrazów, tak jak to się dzieje w prawdziwym świecie.

Jedno oko jest przesunięte względem drugiego o kilka centymetrów, dzięki czemu obrazy

trafiające do oczu różnią się od siebie. Należy stworzyć dwa obrazy sceny trójwymiarowej,

w jednym ustawić kamerę w pozycji jednego oka, a w drugim drugiego. Następnie należy

zadbać, by każde oko otrzymało obraz dla niego przeznaczony. Obrazy te będą bardzo

podobne do siebie, jednak będą one zawierały małe różnice i to właśnie one powodują

wrażenie głębi. Dwa obrazy trafiają do mózgu, gdzie składane są w jeden, widziany przez

człowieka, obraz, w taki sposób, że czujemy, które obiekty są blisko, a które daleko.

Rys. 17 Przykładowy obraz stereoskopowy.

Ostatnio wiele filmów jest produkowanych w technologii trójwymiarowej.

Wymagało to stworzenia specjalnych kamer, które byłyby tak naprawdę połączonymi

dwiema kamerami ustawionymi obok siebie. Jednym z najbardziej znaczących filmów dla

technologii 3D w kinie był „Avatar”, dla którego wiele kin zakupiło sprzęt potrzebny do

wyświetlania filmów trójwymiarowych. Równocześnie z premierą tego filmu weszła na

rynek gra o tym samym tytule, również przystosowana do technologii 3D. Jest to jedna

z pierwszych gier, które od początku były tworzone z myślą o stereoskopii.

Zaimplementowanie stereoskopii w opracowywanym silniku nie było trudnym

zadaniem, gdyż wymagało tylko renderowania dwóch obrazów zamiast jednego.

Problemem okazało się dobranie odpowiednich ustawień położenia kamer względem

siebie oraz punktu, na który kamery są skierowane. Ustawienia te mają znaczący wpływ na

Page 40: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.1 Stereoskopia

~ 40 ~

komfort odbioru obrazu trójwymiarowego. Jednym z największych problemów

związanych z technologią stereoskopii jest właśnie komfort oglądania trójwymiarowych

filmów czy gier, ponieważ wiele osób odczuwa bóle oczu i głowy, zawroty oraz szybkie

zmęczenie podczas oglądania. Dzieje się tak dlatego, że oglądając obraz trójwymiarowy

oczy muszą ciągle pracować, zmieniać ogniskową patrząc na przemian na bliskie i dalekie

obiekty. Zły dobór ustawień kamer powoduje szybsze męczenie, ponieważ wtedy oczy

pracują w sposób nienaturalny. Te różnice w pracy oczu, nawet jeśli bardzo znikome,

powodują wymienione wcześniej skutki uboczne. Nawet przesunięcie kamer względem

siebie o kilka centymetrów lub zmiana kąta między nimi o kilka stopni, może znacząco

polepszyć lub pogorszyć komfort oglądania. Dlatego właśnie dobre ustawienie tych dwóch

parametrów okazało się wyzwaniem. Specjalnie dla filmu „Avatar” została opracowana

kamera rejestrująca obraz stereoskopowy, która miała odległość między obiektywami

równą średniej odległości między oczami ludzkimi, a także zmieniała punkt, na który

skierowane są kamery, tak, by oba obiektywy celowały w obiekt będący w centrum

zainteresowania.

Rys. 18 Różne sposoby ustawienia kamer. Na rysunku po lewej kamery skierowane są równolegle do

siebie, jednak nie jest to najlepsze rozwiązanie, ponieważ oczy ludzkie kierują się na obiekt, który

obserwują. Dlatego lepszym rozwiązaniem jest automatyczne kierowanie kamer na obiekt

zainteresowania, jak pokazano na rysunku po prawej.

Kolejną kwestią w stereoskopii jest sposób dostarczania dwóch obrazów oddzielnie

dla każdego z oczu. Jednym ze sposobów jest użycie specjalnych okularów, które

przefiltrują obraz wyświetlany na ekranie, przepuszczając dla każdego oka inny obraz.

Najprostsze są okulary czerwono-zielone lub czerwono-niebieskie lub cyjanowe,

w których jedno szkło jest czerwone i tym samym przepuszcza tylko czerwony kolor

(kanał R), a drugie zielone, niebieskie lub cyjanowe i przepuszcza tylko kanał G, B lub

Page 41: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.1 Stereoskopia

~ 41 ~

cyjan. Obraz na ekranie jest wyświetlany w taki sposób, że składowa obrazu dla każdego

oka jest wyświetlana tylko w danym kanale. Rozwiązanie takie było popularne

w telewizyjnych programach, przeważnie dla dzieci, a także w niektórych starszych

filmach kinowych. Rozdzielenie obrazów na kanały powoduje jednak, że tracimy

informację o kolorze, przez co widziany obraz jest szaro-bury.

Lepszym rozwiązaniem jest zastosowanie polaryzacji do oddzielenia obrazów lub

najnowszej technologii filtrowania długości fal światła, która została opracowana przez

firmę Infitec. W pierwszym przypadku obrazy dla lewego i prawego oka są spolaryzowane

prostopadle do siebie, a w okularach zamontowane są szła polaryzacyjne, ustawione tak,

by filtrowały odpowiednie obrazy. Taka technologia jest używana w kinach IMAX.

W drugiej technologii stosuje się różne długości fal światła dla składowych RGB koloru.

Trójkąt odwzorowania koloru dla każdego oka jest trochę inny, co nie przeszkadza

w dostarczeniu do oczu pełnej informacji o kolorze. W okularach zamontowane są szkła,

które filtrują światło o pewnych długościach fali, dzięki czemu do każdego oka trafia tylko

przeznaczony mu obraz. Technologia ta jest używana dziś w większości kin [29].

Technologie te nie są używane w przypadku monitorów komputerowych

i telewizorów, ponieważ wymagają specjalnego sprzętu. Dla monitorów oraz telewizorów

stosuje się technikę używającą okularów migawkowych. Monitor wyświetla dwa razy

więcej klatek niż normalnie i są one naprzemiennie kierowane dla jednego i drugiego oka.

Okulary zasłaniają jedno lub drugie szkło, dzięki czemu obrazy trafiają do odpowiedniego

oka. Taką technologię wspiera m.in. produkt NVidia 3D Vision.

Implementacja opracowana w niniejszej pracy używa wyświetlania czerwono-

niebieskiego obrazu, ponieważ do oglądania wystarczą zwykłe okulary z czerwonym

i niebieskim szkłem. Nic nie stoi jednak na przeszkodzie aby przystosować ją np. do

technologii NVidia 3D Vision.

Widok stereoskopowy został zaimplementowany jako kombinacja efektów: dwóch

efektów „render” oraz specjalnego efektu o nazwie „3D”, który łączy dwa obrazy w ten

sposób, że w obrazie wynikowym umieszcza kanał R pierwszego obrazu oraz kanał B

drugiego obrazu. Dwa efekty renderujące mają parametry określające przesunięcie oraz

obrót kamery w lewo lub prawo. Schemat połączenia efektów przedstawiono na poniższym

rysunku.

Rys. 19 Schemat połączenia efektów dla uzyskania obrazu stereoskopowego.

Istnieje możliwość regulacji odstępu między kamerami. Każdy efekt renderujący

wykrywa następujący po nim efekt „3D”, oraz slot, do którego podpięta jest gałąź

Page 42: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 42 ~

z efektem renderującym. Z efektu „3D” pobierana jest zmienna „rozstaw”, która mówi

o ile efekt renderujący powinien przesunąć kamerę w lewo lub w prawo, w zależności od

tego, do którego slotu jest podpięty (slot o indeksie 0 odpowiada lewemu oku, a o indeksie

1 prawemu oku). Kamery automatycznie ustawiane są tak, by celować w obiekt znajdujący

się na środku ekranu. W każdej klatce wykrywana jest głębokość środkowego punktu

lewego obrazu, i na jej podstawie ustalany jest punkt skupienia kamer w kolejnej klatce.

Przykładowe obrazy stereoskopowe przedstawiono poniżej.

Rys. 20 Obraz stereoskopowy.

Rys. 21 Obraz stereoskopowy przeznaczony do oglądania przez czerwono-niebieskie okulary.

4.2 Głębia ostrości

Symulacja efektów graficznych, które występują w rzeczywistej fotografii nie jest

zadaniem łatwym. Niemniej dzisiejsze karty graficzne posiadają wystarczającą moc, by

Page 43: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 43 ~

generować niesamowite efekty oraz częściowo symulować prawdziwe zjawiska fizyczne

zachodzące w obiektywach aparatów fotograficznych.

Generowanie głębi ostrości zaczyna odgrywać coraz ważniejszą rolę w grafice, m.in.

za sprawą gier komputerowych, które starają się być coraz bardziej realistyczne

i „filmowe”. W filmach głębia ostrości odgrywa wielką rolę, pozwalając sterować obrazem

w taki sposób, by widz skupiał uwagę na konkretnym elemencie sceny bądź bohaterze,

kryjąc elementy nieistotne bądź rozpraszające uwagę, za mgłą rozmycia.

Głębia ostrości jest to parametr używany w optyce, opisujący zakres odległości,

w którym obiekty obserwowane będą ostre, wyraźne. Głębia ostrości w optyce zależy od

ustawienia wielkości przysłony (otworu, przez który wpada światło), a także od rozmiaru

obiektywu (soczewek).

Duża głębia ostrości oznacza, że pole, w którym obiekty są wyraźne jest duże,

natomiast mała oznacza, że tylko obiekty w punkcie skupienia soczewki będą wyraźne,

a reszta będzie rozmyta. Dzieje się tak dlatego, ponieważ soczewka skupia promienie

światła pochodzące od obiektów, nie będących w jej punkcie skupienia, za kliszą bądź

matrycą lub przed. Proces ten ilustruje rysunek 22.

Rys. 22 Schemat powstawania rozmycia poza punktem skupienia. Źródło: Wikipedia.

Nieskończona głębia ostrości jest bardzo prosta do osiągnięcia w grafice, gdyż na

obrazach generowanych komputerowo obiekty od razu są wyraźne, natomiast rozmycie

może być efektem dodatkowym. Interesuje nas zatem mała głębia ostrości, tzn. by obiekty

nie były ostre. Można to osiągnąć nakładając odpowiednie rozmycie na wygenerowany

obraz. Takie nałożenie filtrów na gotowy obraz, a nie wpływanie na sam proces

generowania obrazu, nazywane jest obróbką post-processingu. Najpierw jednak zostanie

przedstawiona metoda generowania dokładnego rozmycia za pomocą wielokrotnego

renderowania sceny.

Page 44: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 44 ~

Rys. 23 Zdjęcie pokazujące rozmycie bliskich obiektów.

Renderujemy wiele obrazów, za każdym razem ustawiając kamerę w różnych

punktach soczewki, tzn. przesuwając kamerę na boki oraz w górę i dół. Pozwala to

symulować wpadanie światła przez różne punkty soczewki. W ten sposób wygenerujemy

bardzo dokładne rozmycie, jednak słaba wydajność tego rozwiązania uniemożliwia jego

stosowanie w grafice czasu rzeczywistego. Wielokrotne renderowanie sceny zmniejsza

wydajność tyle razy, ile próbek będziemy renderować. Dla otrzymania zadowalających

rezultatów potrzeba ok. 100 próbek, co zmniejsza wydajność 100-krotnie, przez co

rozwiązanie to nie może być stosowane na dzisiejszych domowych komputerach.

Uzyskane jednak w ten sposób obrazy posłużą jako obrazy referencyjne dla

opracowywanych rozwiązań.

Przykładowe obrazy uzyskane tą metodą przedstawione są na rysunkach 24 i 25.

Rys. 24 Wzorcowe rozmycie. Na lewym obrazie punkt ostrości jest na najbliższym obiekcie, a na

prawym na najdalszym obiekcie.

Page 45: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 45 ~

Rys. 25 Wzorcowe rozmycie. Punkt ostrości ustawiony na najdalszym obiekcie. Widać poprawne

rozmycie obiektu najbliższego, z zachowaniem szczegółów obiektów leżących za nim.

W metodach post-processingu generujemy obraz, który poddamy rozmywaniu,

a także mapę głębokości, określającą odległości do obiektów znajdujących się na obrazie.

Następnie określamy, na której głębokości (czyli odległości od obserwatora) będzie punkt

ostrości, a co za tym idzie, obiekty znajdujące się za nim jak i przed nim będą rozmyte.

Dalej stosujemy filtr rozmywający na obrazie, biorąc za stopień rozmycia każdego piksela

wartość określoną przy pomocy mapy głębokości.

Proces ten wydaje się prosty, jednak stwarza wiele problemów. Najpierw należy

określić w jaki sposób piksele będą rozmywane. Każdy piksel jest rozmywany za pomocą

pikseli w jego otoczeniu. Wielkość otoczenia ma kluczowy wpływ na wydajność

algorytmu, a także na jakość wynikowego obrazu. Im większe otoczenie, tym większe

rozmycia można uzyskać, jednak tracąc na wydajności.

Promień rozmycia każdego piksela wylicza się na podstawie jego głębokości wg

wzoru:

)( fdf

lm

gdzie m oznacza promień rozmycia, f oznacza rozmiar soczewki, d oznacza głębokość

piksela, f oznacza głębokość ostrości.

Następnie możemy przystąpić do rozmywania obrazu. W tym celu należy

zastosować shader pikseli, który dla każdego piksela obrazu przegląda jego otoczenia

Page 46: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 46 ~

o wielkości proporcjonalnej do promienia rozmycia. Piksele z obszaru otoczenia są

próbkowane i na każdej takiej próbce wykonywane są odpowiednie operacje. Stosując filtr

Gaussa operacje te sprowadzają się do przemnożenia koloru próbki przez odpowiednią

wagę. Jednak przy bardziej złożonych algorytmach operacji na każdej próbce może być

znacznie więcej. Shader realizujący rozmycie Gaussa przedstawia poniższy listing:

struct vertout

{

vec4 Color0 : COLOR0;

vec4 TexCoord0 : TEXCOORD0;

};

vec4 main(

vertout IN,

// obraz wejsciowy RGBA, zawierający kolory RGB oraz głębokość

// w kanale A

uniform sampler2D obrazWejsciowy : TEXUNIT0,

uniform vec blur_moc,

uniform vec blur_size,

uniform vec noise_size,

uniform vec noise_rand

) : COLOR0

{

vec4 color = 0;

vec baseDepth;

vec2 pixelSizes =

vec2(1.0/MAIN_SCREEN_RES_X, 1.0/MAIN_SCREEN_RES_Y);

// głębokość piksela, 0 oznacza najbliżej obserwatora (największe

// rozmycie), 0.5 oznacza punkt skupienia (brak rozmycia),

// 1 oznacza najdalej od obserwatora (największe rozmycie)

baseDepth = s4tex2D(obrazWejsciowy, IN.TexCoord0.xy).w;

// moc – promień rozmycia

vec baseMoc = abs(baseDepth*2-1);

vec radius = baseDepth * 2 - 1;

radius *= blur_size;

vec2 texSize = pixelSizes.xy*radius;

vec4 cHigh;

vec4 cLow;

vec l;

vec4 c;

vec moc;

const int SAMPLES = 100;

// próbki to wektory 3-elem: x, y i waga próbki z rozkładu gaussa

const vec3 samples[100] = {...};

for (int i=0; i<SAMPLES; i++)

{

c = s4tex2D(obrazWejsciowy, IN.TexCoord0.xy +

samples[i].xy)*texSize);

Page 47: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 47 ~

color.xyz += c.xyz * samples.z;

color.w += samples.z;

}

return float4(color.rgb/color.w, 1);

}

Efekt zastosowania rozmycia z filtrem Gaussa przedstawiono na rysunku 26.

Rys. 26 Zastosowanie rozmycia Gaussa. Widać efekt „wyciekania” koloru wyraźnych obiektów

(czerwona kula).

Jak widać na powyższym rysunku powstały efekty „wyciekania” koloru obiektu

znajdującego się w polu ostrości. Dzieje się tak ponieważ algorytm rozmywania nie bierze

pod uwagę głębokości pikseli, a co za tym idzie piksele, które znajdują się za polem

ostrości (są tłem), będą rozmywane również przez piksele będące w polu ostrości.

Efekt „wyciekania” można zminimalizować sprawdzając głębokość pikseli podczas

rozmywania w taki sposób, że piksele, które znajdują się bliżej i nie są rozmyte w takim

stopniu jak rozmywany piksel, nie będą na niego wpływały. Próbkując otoczenie

rozmywanego piksela możemy każdej próbce przypisać wagę równą stopniowi rozmycia

danej próbki. Podejście takie zredukuje efekt „wyciekania”, ponieważ obiekty będące

w polu ostrości będą miały stopień rozmycia równy zeru. Wynik zastosowania tej redukcji

przedstawia rysunek 27.

Page 48: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 48 ~

Rys. 27 Zastosowanie redukcji „wyciekania”.

Na przedstawionym rysunku widzimy teraz inny problem – obiekty znajdujące się

przed punktem skupienia są rozmyte, jednak mają ostre krawędzie. Dzieje się tak dlatego,

ponieważ operujemy na obrazie wejściowym, który jest cały wyraźny i nie możemy

zobaczyć tego, co jest za tymi obiektami, a właśnie to powinniśmy zobaczyć rozmywając

ich krawędzie. Wynik jaki powinnyśmy zobaczyć ilustruje rysunek 28, uzyskany metodą

wielokrotnego renderowania.

Rys. 28 Poprawne rozmycie bliskiego obiektu.

Page 49: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 49 ~

Jest to największe ograniczenie algorytmów operujących na jednym wyraźnym

obrazie wejściowym. Wydaje się, że nie jest możliwe rozwiązanie tego problemu mając

tylko jeden wyraźny obraz wejściowy.

Można zatem dostarczyć kilka obrazów, tzw. warstw, np. obraz z obiektami

znajdującymi się przed punktem skupienia oraz drugi z obiektami za i w punkcie

skupienia. Oba te obrazy rozmywane będą oddzielnie. Drugi obraz rozmyty zostanie w taki

sam sposób jak w pierwotnym sposobie, ponieważ będzie on zawierał tylko obiekty będące

w polu skupienia oraz za nim. Pierwszy obraz, zawierający obiekty przed punktem

skupienia, będzie rozmyty w podobny sposób, jednak dodatkowo będzie uwzględnione

rozmywanie kanału alfa – przezroczystości. Dzięki temu krawędzie obiektów zostaną

rozmyte. Następnie obrazy zostaną połączone w taki sposób, że na obraz drugi zostanie

nałożony obraz pierwszy, uwzględniając kanał alfa pierwszego obrazu.

Rozwiązanie to sprawdzi się tylko wtedy, gdy żadne obiekty nie będą na tyle długie,

by ich części znalazły się na dwóch obrazach. W takim przypadku miejsce przecięcia

obiektu będzie widoczne na obrazie wynikowym i nie będzie dobrze się prezentowało.

Problem ten był rozpatrywany przez wielu badaczy, co zaowocowało wieloma

rozwiązaniami, jednak jednym z najlepszych z nich jest praca [30]. Przedstawiono w niej

sposób generowania obrazu używający dwóch obrazów. Pierwszym jest ostry obraz

wejściowy, drugi (tzw. ukryty) generowany jest poprzez renderowanie sceny jeszcze raz,

tym razem ustawiając odpowiednio test głębokości. Ustawiany jest on w taki sposób by

odrzucane były piksele będące tłem (będące za punktem ostrości), mające tę samą

głębokość co w warstwie pierwszej oraz te, które na pewno będą niewidoczne. To, czy

piksel będzie na pewno niewidoczny sprawdza się porównując głębię pikseli w jego

otoczeniu. Jeśli różnica w głębi jest odpowiednio mała oznacza to, że piksel nie będzie

odsłonięty. Duża różnica w głębokości oznacza, że piksel znajduje się blisko krawędzi,

która może być rozmyta, a zatem piksel ten może zostać odsłonięty.

Następnie te dwa obrazy są rozmywane za pomocą powiększania pikseli (ang. point

splatting) w taki sposób, że każdy piksel rozmytego obrazu zostaje przydzielony do jednej

z trzech warstw w zależności od głębokości piksela i jego otoczenia. Dalej warstwy te są

łączone, co przy odpowiednim doborze parametrów daje zadowalające rezultaty. Algorytm

ten dobrze radzi sobie z rozmywaniem obiektów znajdujących się przed punktem

skupienia, odsłaniając to co jest za nimi, a także z długimi obiektami, które przechodzą

przez wszystkie trzy obszary: rozmyty przed punktem skupienia, ostry w punkcie

w skupienia, rozmyty za punktem skupienia.

Dla potrzeb gry można jednak zrezygnować z poprawnego rozmywania obiektów

znajdujących się blisko. Dlatego autor zdecydował się na opracowanie najbardziej

atrakcyjnego wizualnie efektu głębi ostrości, cechującego się dobrą wydajnością.

Aby osiągnąć „filmowy” efekt głębi ostrości należy rozpatrzyć tzw. efekt

soczewkowy lub efekt „bokeh”, który powoduje, że jasne punkty rozmywane są w duże

i jasne figury geometryczne, takie jak pięciokąt lub sześciokąt foremny bądź koło.

Page 50: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 50 ~

Rys. 29 Jasne punkty tła zostały rozmyte w jasne plamy w kształcie kół.

Aby osiągnąć taki efekt należy zmodyfikować filtr rozmywający tak, aby za

otoczenie piksela rozmywanego brał obszar o kształcie figury jaką chcemy uzyskać na

rozmyciu. Aby osiągnąć dobry efekt musimy użyć bardzo wielu próbek z sąsiedztwa,

w praktyce powyżej stu. Wynik działania przykładowego filtru prezentuje następujący

rysunek:

Rys. 30 Zastosowanie rozmycia z próbkami rozmieszczonymi wg maski.

Page 51: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 51 ~

Maska rozmieszczenia próbek wyglądała następująco:

Rys. 31 Rozmieszczenie punktów wg maski.

W celu osiągnięcia efektu wyraźniejszych krawędzi można zwiększyć liczby próbek

na krawędzi maski. Po takim zabiegu maska może wyglądać następująco:

Rys. 32 Rozmieszczenie punktów wg maski z większą ilością próbek na krawędzi.

Rozmieszczenie próbek na kształcie maski odbywa się automatycznie na podstawie

bitmapy maski. Najpierw znajdowany jest brzeg figury przedstawionej na bitmapie,

następnie wybierane są punkty leżące wzdłuż brzegu w równej odległości od siebie. Dalej

w miejscu wszystkich punktów brzegowych stawiane są czarne koła (usuwanie maski)

o promieniu równym odstępowi między wybieranymi punktami, i znów następuje

wyszukanie brzegu i rozmieszczenie punktów. Operacja ta jest wykonywana do momentu,

gdy cała maska zostanie usunięta.

Istotnym pomysłem, który znacząco wpłynął na jakość efektu soczewkowego jest

ważenie próbek za pomocą ich jasności. Jasność próbki określamy wg następującego

wzoru:

bbrl 11.059.03.0

gdzie l oznacza jasność, a r, g i b składowe RGB piksela.

Stosując wagę próbki równą l uzyskujemy zwiększenie udziału jasnych pikseli

w obrazie, co daje bardzo dobry wynik. Możemy również jako wagę próbki używać potęgi

l, co da jeszcze większy udział jasnych pikseli w rozmyciu. Zastosowanie jako wagi l oraz

l2 pokazano na rysunku 33.

Page 52: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 52 ~

Rys. 33 Na lewym rysunku zastosowano wagę l, a na prawym l

2.

Punkty maski również mogą mieć swoje wagi dla każdej składowej RGB, co

pozwala na stosowanie dowolnych obrazów jako maski, dzięki czemu możliwe jest

uzyskiwanie ciekawych rezultatów. Wynik zastosowania kolorowych masek

przedstawiono na rysunku 34.

Rys. 34 Zastosowanie kolorowych masek.

Jak widzimy na powyższych rysunkach opracowany sposób pozwala na generowanie

atrakcyjnych wizualnie obrazów, mających „filmowy” wygląd.

Page 53: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 53 ~

Shader pikseli implementujący wszystkie wyżej wymienione metody różni się od

poprzednio przedstawionego o zawartość pętli oraz kilka dodatkowych instrukcji.

// próbki to wektory 2-elem (x, y) oraz wagi RGB

const vec2 samplesXY[SAMPLES] = {...};

const vec3 samplesRGB[SAMPLES] = {...};

// sumator wag RGB

vec3 ca = 0;

for (int i=0; i<SAMPLES; i++)

{

c = s4tex2D(obrazWejsciowy, IN.TexCoord0.xy +

samplesXY[i].xy)*texSize);

moc = abs(c.a*2-1);

l = 0.3*c.r + 0.59*c.g + 0.11 * c.b;

c.a *= moc * (l*l + 0.01)

color.xyz += c.xyz * c.a * samplesRGB[i];

ca.rgb += c.a * samplesRGB[i];

}

return float4(color.rgb/ca.rgb, 1);

Zastosowanie stu próbek maski pozwala na uzyskanie promienia rozmycia

o długości ok. 10 pikseli, ponieważ w przypadku użycia większego promienia ujawni się

struktura maski, co prezentuje rysunek poniżej.

Rys. 35 Zbyt duże rozmycie powoduje ujawnienie się punktów maski.

Page 54: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 54 ~

Aby temu zapobiec zastosowano dwa obrazy wejściowe, jeden oryginalny, a drugi

rozmyty filtrem Gaussa o odpowiednim promieniu. Rozmyty obraz jest używany gdy

promień nakładanej maski przekracza wielkość ok. 10 pikseli. Dobranie odpowiedniego

momentu przejścia między ostrym i rozmytym obrazem jest kluczowe dla jakości

końcowego obrazu.

Finalny shader przedstawia następujący listing:

// próbki to wektory 2-elem (x, y) oraz wagi RGB

const vec2 samplesXY[SAMPLES] = {...};

const vec3 samplesRGB[SAMPLES] = {...};

// sumator wag RGB

vec3 ca = 0;

for (int i=0; i<SAMPLES; i++)

{

cHigh = s4tex2D(obrazWejsciowy, IN.TexCoord0.xy +

samplesXY[i].xy)*texSize);

cLow = s4tex2D(obrazRozmyty, IN.TexCoord0.xy +

samplesXY[i].xy)*texSize);

moc = abs(cHigh.a*2-1);

// wartości dobrane do rozmiaru ekranu i maski oraz promienia

// rozmycia

l = saturate((moc - 0.10) * 9);

c = lerp(cHigh, cLow, l);

l = 0.3*c.r + 0.59*c.g + 0.11 * c.b;

c.a *= moc * (l*l + 0.01)

color.xyz += c.xyz * c.a * samplesRGB[i];

ca.rgb += c.a * samplesRGB[i];

}

return float4(color.rgb/ca.rgb, 1);

Opracowane rozmycie przedstawiono na kolejnych rysunkach.

Page 55: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 55 ~

Page 56: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.2 Głębia ostrości

~ 56 ~

Rys. 36 Rozmycie soczewkowe. Jasne punkty rozmywane są w pięciokąty foremne.

Page 57: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.3 Rozmycie ruchu

~ 57 ~

Kolejną istotną kwestią jest wydajność. Mogłoby się wydawać, że powyższy kod

zostanie skompilowany przez kompilator dostarczony przez producenta karty graficznej

w sposób najbardziej optymalny. Jednak (z niewiadomych przyczyn) kompilator ten

potrafi skompilować identyczny kod do postaci, które różnią się szybkością wykonania o

rząd wielkości. Najbardziej optymalna wersja shadera, poza oczywistym

zoptymalizowaniem kodu według podstawowej wiedzy programistycznej, została

opracowana metodą prób i błędów, która pozwoliła wskazać błędy kompilatora języka CG

w wersji 2.2.0017 lub sterowników NVidii w wersji 257.21.

Jednym ze znalezionych błędów było bardzo powolne wykonywanie

skompilowanego kodu, który zawierał następującą operację wykonywaną dla każdej

próbki:

c.a = (c.a < baseDepth) ? 1.0 : saturate(c.a*2-1);

Gdy kod został przepisany do następującej postaci:

if (c.a < baseDepth)c.a = 1.0;

else c.a = saturate(c.a*2-1);

był wykonywany 5 razy szybciej. Te dwie wersje kodu mają identyczne działanie, jednak

z niewiadomego powodu druga z nich wykonuje się znacznie szybciej.

Najważniejszą optymalizacją było ręczne rozwinięcie pętli. Wykonano to za pomocą

procedury w języku C++ generującej kod języka CG dla każdej próbki z maski.

Przyspieszyło to działanie programu z 10 klatek na sekundę do 55!

W przyszłych pracach można zastosować metody opisane w pracy [30] w celu

poprawy wyglądu rozmycia obiektów znajdujących się blisko obserwatora.

4.3 Rozmycie ruchu

Rozmycie ruchu jest jednym z „filmowych” efektów, który daje bardzo atrakcyjne

wizualnie wyniki. Może on służyć również do stworzenia wrażenia płynności ruchu, gdy

liczba klatek na sekundę jest niewystarczająca do płynnej animacji.

Rozmycie ruchu powstaje na klatce filmowej gdy w czasie ekspozycji obiekt będzie

się poruszał. Ponieważ obiekt będzie zmieniał swoją pozycję, w każdej chwili czasu

zostawi on swój ślad w innym miejscu klatki filmowej.

Efekt ten można uzyskać na kilka sposobów. Najprostszym jest wyrenderowanie

kilku lub kilkunastu klatek animacji, w zwolnionym tempie, i złączeniu ich w jedną.

Uzyskane w ten sposób klatki będą zawierały rozmycie ruchu, w którego jakość zależna

jest od liczby klatek składających się na jedną wynikową klatkę.

Innym sposobem jest zastosowanie post-processingu. Wyrenderowany obraz

zostanie rozmyty w taki sposób, by oddać kierunek ruchu obiektów. Wymaga to

przygotowania dodatkowego obrazu, który zawierał będzie dwuwymiarowy wektor ruchu

każdego piksela na ekranie. Obraz taki uzyskuje się renderując scenę ze specjalnym

shaderem, który dostaje na wejściu, oprócz współrzędnej położenia renderowanego

piksela, współrzędną położenia piksela w poprzednio renderowanej klatce. Shader ten

Page 58: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.3 Rozmycie ruchu

~ 58 ~

odejmuje te dwie współrzędne i uzyskuje w ten sposób wektor ruchu. Kod shadera

wierzchołków przedstawia poniższy listing:

struct vertout

{

float4 HPosition : POSITION;

float4 OldPosition : TEXCOORD0;

};

vertout main(appin IN,

// macierz przekształcenia danej klatki

uniform float4x4 ModelViewProj,

// macierz przekształcenia poprzedniej klatki

uniform float4x4 prevModelaViewProj)

{

vertout OUT;

float4 position = float4(IN.Position.xyz, 1);

// pozycja renderowanego piksela na ekranie

float4 HP = mul(ModelViewProj, position);

// pozycja tego piksela w poprzedniej klatke

float4 HPprev = mul(prevModelaViewProj, position);

OUT.HPosition = HP;

OUT.OldPosition = HPprev;

return OUT;

}

Kod shadera pikseli przedstawia następujący listing:

struct vertout

{

vec4 Position : POSITION;

vec4 OldPosition : TEXCOORD0;

};

vec4 main(vertout IN) : COLOR0

{

// w wektorach a i b zapisane będą współrzędne pikseli na ekranie

// w zakresie (0, 1)

float2 a = IN.OldPosition.xy;

a /= IN.OldPosition.w;

a = a * 0.5 + 0.5;

float2 b = IN.Position.xy;

b /= IN.Position.w;

b = b * 0.5 + 0.5;

float2 velocity = (b - a);

velocity.x *= MAIN_SCREEN_RES_X;

velocity.y *= MAIN_SCREEN_RES_Y;

// przycięcie wektora do zakresu (-128, 128) i zapisanie w formie

// koloru w składowych RG

velocity = clamp(velocity / 128 + 0.5, 0, 1);

return float4(velocity, 1, 1);

}

Page 59: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.3 Rozmycie ruchu

~ 59 ~

Następnie należy rozmyć wejściowy obraz używając mapy ruchu. W ramach

niniejszej pracy opracowano w tym celu shader, który dla każdego piksela, próbkuje

piksele leżące w kierunku ruchu rozmywanego piksela. Kod shadera pikseli przedstawia

następujący listing:

struct vertout

{

vec4 TexCoord0 : TEXCOORD0;

};

vec4 main(vertout IN,

uniform sampler2D obrazWejsciowy : TEXUNIT0,

uniform sampler2D mapaRuchu : TEXUNIT1,

// rozmiar rozmycia

uniform float size) : COLOR0

{

vec2 b = IN.TexCoord0.xy;

// wektor rozmycia

vec2 vel = (s2tex2D(mapaRuchu, b).xy - 0.5) * 128;

vel.x /= MAIN_SCREEN_RES_X;

vel.y /= MAIN_SCREEN_RES_Y;

vel *= size;

vec3 c;

vec2 v;

vec4 color = 0;

vec l;

const int SAMPLES = 20;

for (vec i=0; i<=SAMPLES; i++)

{

vec t = i / SAMPLES;

// próbki leżą na wektorze ruchu

c = s3tex2D(obrazWejsciowy, b + vel * t).rgb;

// próbki leżące dalej od rozmywanego piksela mają

// mniejszy wpływ w rozmywaniu

color.rgb += c * (1.0 - t);

color.a += (1.0 - t);

}

color.rgb /= color.a;

return vec4(color.rgb, 1);

}

Przykładowe obrazy uzyskane za pomocą tego rozwiązania przedstawiono na

rysunkach 37 i 38.

Page 60: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.3 Rozmycie ruchu

~ 60 ~

Rys. 37 Rozmycie podczas ruchu kamery.

Rys. 38 Rozmycie poruszającego się obiektu.

Aby osiągnąć bardziej „filmowy” efekt, można zastosować, podobnie jak w efekcie

głębi ostrości, ważenie próbek za pomocą ich jasności. Podejście takie uzasadnione jest

faktem, że jaśniejsze fragmenty obrazu z reguły mocniej się utrwalają na ekranie. Aby

zastosować to ważenie należy zmodyfikować pętlę w kodzie shadera w sposób

następujący:

Page 61: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.3 Rozmycie ruchu

~ 61 ~

for (vec i=0; i<=SAMPLES; i++)

{

vec t = i / SAMPLES;

// próbki leżą na wektorze ruchu

c = s3tex2D(obrazWejsciowy, b + vel * t).rgb;

l = 0.3*c.r + 0.59*c.g + 0.11*c.b + 0.05;

l = l*l * (1.0 - t);

color.rgb += c * l;

color.a += l;

}

Poniżej przedstawiono porównanie rozmycia ruchu z zastosowaniem ważenia po

jasności oraz bez ważenia próbek.

Rys. 39 Porównanie rozmycia bez ważenia próbek po jasności (po lewej) i z ważeniem (po prawej).

Przykładowe obrazy pokazujące opracowane rozmycie ruchu w działaniu

przedstawiono na kolejnych rysunkach.

Page 62: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.3 Rozmycie ruchu

~ 62 ~

Rys. 40 Przykładowe obrazy prezentujące rozmycie ruchu.

Page 63: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.4 Mgła

~ 63 ~

4.4 Mgła

Mgła jest bardzo ważnym zjawiskiem, ponieważ w prawdziwym świecie występuje

niemal wszędzie. Nawet na zdjęciach wykonanych w bardzo pogodny dzień widać lekką

niebieską mgłę na oddalonych obiektach. Mgła dodaje realizmu do generowanej grafiki,

a umiejętne posługiwanie się mgłą daje wielkie możliwości w kontrolowaniu nastroju, jaki

udziela się oglądającemu obraz, czy klimatu w filmie lub grze komputerowej.

Symulacja mgły jest możliwa prawie od początków grafiki trójwymiarowej w czasie

rzeczywistym. Początkowo mgła była używana do ograniczania widoczności w celu

zwiększenia wydajności renderowania. Dziś również w niektórych grach, jak np. „World

of Warcraft”, spotkać można mgłę służącą do ograniczania pola widzenia w celu

zmniejszenia ilości renderowanej geometrii.

Najprostszym sposobem generowania mgły jest mieszanie kolorów na

wygenerowanym obrazie z ustalonym kolorem mgły, którego natężenie rośnie wraz ze

wzrostem odległości od obserwatora. Natężenie koloru mgły może następować liniowo

wraz ze wzrostem odległości – mówimy wtedy o mgle liniowej – lub wykładniczo –

mówimy wtedy o mgle wykładniczej. Mgła liniowa wymaga określenia początku mgły

oraz końca fazy przejścia. Obiekty przed początkiem nie będą zmieszane z kolorem mgły,

natomiast obiekty za końcem fazy przejścia będą zastąpione całkowicie kolorem mgły.

Mgła wykładnicza natomiast wymaga określenia gęstości i wygląda o wiele naturalniej.

Natężenie mgły wykładniczej oblicza się ze wzoru:

*1 def

gdzie d to odległość od obserwatora, a δ to gęstość mgły.

Taka mgła wygląda dobrze i dlatego jest bardzo szeroko stosowana. Jednak

obserwując prawdziwą mgłę można dostrzec, że tworzy ona wiele innych efektów

wizualnych, których ten podstawowy model nie obejmuje.

Przyjrzyjmy się fotografiom przedstawiającym prawdziwą mgłę (Rys. 41). Widać na

nich, że lampy rozświetlają obszar wokół siebie. Dzieje się tak dlatego, że cząstki mgły

odbijają do obserwatora pewną część światła generowanego przez lampę. Drugą istotną

cechą, którą widać na fotografiach jest to, że jasne obiekty, takie jak lampa, przebijają się

przez mgłę o wiele bardziej skutecznie. Przyglądając się prawdziwej mgle możemy

również dostrzec, że jasne obiekty przebijające się przez mgłę będą niewyraźne, rozmyte.

Właśnie te cechy mgły zostały rozpatrzone i zasymulowane w rozwiązaniu

opracowanym w ramach niniejszej pracy.

Efekt rozświetlenia wokół punktowych źródeł światła został osiągnięty przez

dodanie flar. Są one teksturami nałożonymi na wygenerowany obraz w miejscu źródła

światła. Tekstura flary pokazana jest na rysunku 42.

Flary są obiektami typu „billboard”, co oznacza, że zawsze są zwrócone w stronę

obserwatora. Tekstura flary jest barwiona kolorem światła oraz jego intensywnością,

a rozmiar obiektu flary jest dopasowany do promienia światła. Dodatkowo, flary

renderowane są za pomocą specjalnego shadera, który symuluje „kulistość” flary, tzn. jeśli

flara będzie renderowana za jakimś obiektem, to zostanie odpowiednio przyciemniona.

Page 64: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.4 Mgła

~ 64 ~

Rys. 41 Zdjęcia prawdziwej mgły.

Rys. 42 Tekstura flary.

Page 65: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.4 Mgła

~ 65 ~

Efekt zastosowania flar przedstawia poniższy rysunek.

Rys. 43 Działanie flar. Po lewej flary włączone, po prawej wyłączone.

Rozmycie obiektów widocznych przez mgłę zostało osiągnięte przy użyciu rozmytej

wersji wejściowego obrazu. Rozmywany obraz jest mieszany z kolorem mgły by powstało

tzw. tło, w taki sposób, by jasne obiekty bardziej przebijały się przez mgłę niż obiekty

ciemne. Realizuje to następujący shader pikseli:

struct vertout

{

vec4 TexCoord0 : TEXCOORD0;

};

vec4 main(vertout IN,

uniform sampler2D obraz : TEXUNIT0,

uniform sampler2D mapaGlebokosci : TEXUNIT1,

uniform vec3 color,

uniform vec ambient,

uniform vec density

) : COLOR0

{

vec3 tlo = tex2D(obraz, IN.TexCoord0.xy);

vec dep = -tex2D(mapaGlebokosci, IN.TexCoord0.xy).a;

// wyliczanie jasności

float l = clamp(0.3*tlo.r + 0.59*tlo.g + 0.11*tlo.b - 0.05, 0, 1);

// kwadrat jasności

l = l*l;

// ciemnienie z odlegloscią

// składnik (1.0-l) odpowiada za mocniejsze przebijanie się

// jasnych obiektów

float moc = exp(-(dep/100)*density*0.25*(1.0-l));

// rozjasnianie z oglegloscią

moc *= 1.0-exp(-((dep)/100)*density);

vec3 res = (tlo*moc + float3(ambient, ambient, ambient)) * color;

return float4(res, 0);

}

Page 66: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.4 Mgła

~ 66 ~

Następnie na obraz wejściowy nakładana jest mgła wykładnicza, za kolor mgły

przyjmowane jest tło. Również tutaj zastosowano dodatkową modyfikację pozwalającą by

jasne obiekty bardziej przebijały się przez mgłę. Kod shadera pikseli wykonującego

tę operację przedstawia następujący listing:

struct vertout

{

vec4 TexCoord0 : TEXCOORD0;

vec4 TexCoord1 : TEXCOORD1;

};

vec4 main(vertout IN,

uniform sampler2D obraz : TEXUNIT0,

uniform sampler2D mapaGlebokosci : TEXUNIT1,

uniform sampler2D tlo : TEXUNIT2,

uniform vec density

) : COLOR0

{

vec2 a = IN.TexCoord1.xy;

a /= IN.TexCoord1.w;

a = a * 0.5 + 0.5;

vec dep = -tex2D(mapaGlebokosci, a).a;

vec3 fog = tex2D(obraz, IN.TexCoord0.xy).rgb;

vec3 background_color = tex2D(tlo, IN.TexCoord0.xy).rgb;

// obliczanie jasności

float l = 0.3*background_color.r + 0.59*background_color.g +

0.11*background_color.b - 0.15;

l = clamp(l, 0, 1);

// kwadrat jasności

l = l*l;

// tłumienie jasności w zależności od gęstości mgły

l = pow(l, density+0.001);

// obliczanie mocy mgły wykładniczej z uwzględnieniem jasności

vec moc = 1.0 - exp(-(dep/100) * density * (1.0 - l));

return float4(lerp(background_color, fog, moc), 1);

}

Przykładowe wyniki działania opracowanej mgły przedstawiono na kolejnych

rysunkach.

Page 67: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.4 Mgła

~ 67 ~

Rys. 44 Przykładowe obrazy przedstawiające mgłę.

Rys. 45 Obrazy przedstawiające rosnącą gęstość mgły.

Page 68: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.4 Mgła

~ 68 ~

Kolejnym typem mgły jest mgła wolumetryczna. Służy ona do jeszcze dokładniejszej

symulacji świecenia oświetlonych cząstek mgły. Dzięki mgle wolumetrycznej możliwe jest

stworzenie struktur cieni widocznych we mgle. Za jej pomocą można tworzyć bardzo

atrakcyjne wizualnie obrazy.

W rozwiązaniu opracowanym w ramach niniejszej pracy skupiono się na stworzeniu

mgły oświetlanej przez punktowe źródło światła.

Aby wygenerować taką mgłę należy stworzyć kubiczną mapę głębokości o środku

w źródle światła. Mając taką mapę można określić, czy dowolny punkt na scenie jest

oświetlony przez światło, czy też znajduje się w cieniu. Następnie należy zastosować

próbkowanie promieni wyemitowanych od obserwatora do każdego punktu

wyrenderowanej sceny.

Każdy promień próbkowany jest określoną liczbą próbek. Mając trójwymiarową

pozycję próbki możemy określić, czy jest ona w cieniu czy nie, używając kubicznej mapy

głębokości światła. Należy zsumować próbki nie będące w cieniu, a następnie zastosować

dodatkowo mieszanie stosowane w metodzie mgły wykładniczej.

Światło punktowe może świecić różnymi kolorami w różne strony. Wykorzystuje się

do tego mapę kubiczną światła. Można za jej pomocą stworzyć światło reflektorowe, które

tylko na jednej ścianie mapy kubicznej będzie miało jasne koło. Zastosowanie kolorowych

świateł wraz z mgłą wolumetryczną daje bardzo efektowne rezultaty.

Ostateczny shader pikseli renderujący mgłę wolumetryczną przedstawia następujący

listing:

struct vertout

{

vec4 TexCoord0 : TEXCOORD0;

vec4 TexCoord1 : TEXCOORD1;

vec4 Color0 : COLOR0;

};

vec4 main(vertout IN,

uniform sampler2D tlo : TEXUNIT0,

uniform sampler2D mapaGlebokosciScreen : TEXUNIT1,

uniform sampler2D obraz : TEXUNIT2,

uniform samplerCUBE mapaGlebokosciSwiatla : TEXUNIT3,

uniform samplerCUBE kolorSwiatla : TEXUNIT4,

uniform vec density,

uniform vec3 cam_pos,

uniform vec3 light_pos

) : COLOR0

{

vec dep = -tex2D(mapaGlebokosciScreen, IN.TexCoord0.xy).a;

// kierunek promienia od kamery do piksela

vec3 dir = normalize(IN.TexCoord1.xyz);

const int SAMPLES = 40;

dir = dir * dep / SAMPLES;

// pozycja próbki w stosunku do swiatla

vec3 pos = cam_pos - light_pos;

Page 69: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.4 Mgła

~ 69 ~

vec moc = 0;

vec3 fog_color = 0;

for (int i=0; i<SAMPLES; i++)

{

vec light_dep = texCUBE(mapaGlebokosciSwiatla, pos).r;

vec3 light_color = texCUBE(kolorSwiatla, pos).rgb;

// jesli próbka nie jest w cieniu

if (dot(pos, pos) < light_dep*light_dep)

{

moc += 1.0/SAMPLES;

fog_color += light_color * 1.0/SAMPLES;

}

pos += dir;

}

fog_color += 0.01;

fog_color /= moc+0.01;

// obliczanie mgły jak w podstawowej mgle

vec3 fog = tex2D(obraz, IN.TexCoord0.xy).rgb;

vec3 background_color = tex2D(tlo, IN.TexCoord0.xy).rgb;

float l = 0.3*background_color.r + 0.59*background_color.g +

0.11*background_color.b - 0.15;

l = clamp(l, 0, 1);

l = l*l;

l = pow(l, density);

moc = 1.0 - exp(-(moc) * density * (1.0 - l));

vec4 res;

// dodawanie koloru światła fog_color

res = float4(lerp(background_color, fog+fog_color, moc), 1);

return res * IN.Color0;

}

Przykładowe obrazy przedstawiające opracowaną mgłę wolumetryczną

przedstawiono na rysunku 46.

Page 70: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.4 Mgła

~ 70 ~

Rys. 46 Obrazy przedstawiające mgłę wolumetryczną. Na dolnych rysunkach widać mgłę, która

została wygenerowana przez kolorowe światło (z teksturą witrażu).

Page 71: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.5 Inne efekty

~ 71 ~

4.5 Inne efekty

Poza efektami wymienionymi w poprzednich podrozdziałach, opracowano również

wiele innych, prostszych efektów. Są to m.in.:

rozmycie – z możliwością regulacji promienia rozmycia,

wyostrzenie – z możliwością regulacji promienia maski wyostrzającej oraz

mocy wyostrzania,

poświata – z możliwością regulacji stopnia rozmycia i koloru poświaty oraz

długości powidoku,

odbicie lustrzane – w pionie lub poziomie,

ustawienie dwóch obrazów obok siebie – w pionie albo poziomie,

mieszanie obrazów – z regulacją wag poszczególnych obrazów,

rozmycie radialne – z regulacją stopnia rozmycia,

zmiana jasności, kontrastu oraz parametru gamma obrazu,

konwersja do przestrzeni HSV i w drugą stronę,

dodanie szumu – z regulacją mocy szumu,

efekt „telewizyjny” – dodanie filtra z czarnych linii,

współrzędne biegunowe – przekształcenie obrazu do współrzędnych

biegunowych oraz w drugą stronę.

Przykład zastosowania wielu efektów i złączenia ich wszystkich na jednym obrazie

ilustruje rysunek 47. Schemat połączenia efektów ilustruje rysunek 48.

Rys. 47 Zastosowanie wielu efektów.

Page 72: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.6 Materiały

~ 72 ~

Rys. 48 Schemat połączenia wielu efektów.

4.6 Materiały

Materiały są bardzo ważnym elementem w grafice trójwymiarowej, ponieważ to od

nich zależy realność generowanego obrazu. Materiał w grafice trójwymiarowej musi

uwzględniać takie parametry jak kąt patrzenia i kąt padania światła. Patrząc na obiekt

z różnych stron i pod różnymi kątami możemy zobaczyć zupełnie inne kolory, np. patrząc

wprost na powierzchnię mokrego kamienia zobaczymy jego kolor, jednak patrząc pod

kątem zobaczymy to, co odbija się w wodzie. Skrajnym przypadkiem jest lustro, które

odbija wszystko i samo nie ma żadnego koloru.

W większości materiałów wyodrębnić można parametry takie jak rozpraszanie

(ang. diffuse) i odblask (ang. specular). Za pomocą tylko tych dwóch cech można stworzyć

bardzo wiele ciekawych materiałów.

Kolejnym elementem, który przydaje się podczas tworzenia materiałów, jest mapa

otoczenia obiektu. Za jej pomocą można tworzyć materiały lustrzane i szklane. Również

wypolerowany kamień odbija otaczający świat, więc można wykorzystać mapę otoczenia.

Opracowano bazę materiałów, które można nakładać na obiekty. Najprostszymi

są materiały matowe, takie jak drewno, cegła. Zaprezentowano je na rysunkach 49 i 50.

Page 73: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.6 Materiały

~ 73 ~

Rys. 49 Drewniana deska.

Rys. 50 Mur ceglany.

Bardziej efektowne materiały to wyszlifowany kamień oraz drewno pokryte

lakierem. Przedstawiono je na rysunkach 51 i 52.

Page 74: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.6 Materiały

~ 74 ~

Rys. 51 Polakierowane drewno.

Rys. 52 Wyszlifowany kamienny królik.

Page 75: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.6 Materiały

~ 75 ~

Poniżej przedstawiono kilka dodatkowych materiałów, takich jak różnego rodzaju

metale i ceramiczne kafelki.

Rys. 53 Zardzewiały metal.

Rys. 54 Podłoga z kafelków.

Page 76: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.6 Materiały

~ 76 ~

Rys. 55 Różne rodzaje metali.

Dzięki zastosowaniu kubicznych map otoczenia można było uzyskać materiały

wyglądające jak różnego rodzaju metale, szkło i obiekty wyszlifowane lub polakierowane.

Zostały one częściowo pokazane w rozdziale 3.4. Kolejne przykłady zaprezentowano

następnych rysunkach.

Page 77: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.6 Materiały

~ 77 ~

Rys. 56 Model królika

2 ze szkła prezentujący aberrację chromatyczną.

Rys. 57 Model szklanego smoka

3. Zaprezentowano różne materiały szkła.

2, 3

Źródło: The Stanford 3D Scanning Repository. http://graphics.stanford.edu/data/3Dscanrep.

Page 78: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.6 Materiały

~ 78 ~

Rys. 58 Zastosowano różny stopień rozmycia kubicznej mapy otoczenia.

Rys. 59 Prezentacja różnych materiałów metalowych. Metal może być błyszczący bądź matowy.

Page 79: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.6 Materiały

~ 79 ~

Rys. 60 Materiał odwzorowujący polakierowany plastik. Może on odbijać wyraźnie swoje otocznie

(obrazek po prawej) lub jego rozmytą wersję (obrazek po lewej).

Interesującym materiałem jest animowany materiał znikający. Przedstawiono go na

poniższym rysunku.

Rys. 61 Obiekt przedstawiający znikający materiał.

Page 80: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

4.6 Materiały

~ 80 ~

W obiekcie powstają dziury, aż w końcu znika on cały. Sposób znikania określany

jest za pomocą monochromatycznej tekstury. Animację osiąga się za pomocą zmiennej,

określającą próg. Piksel jest odrzucany, gdy próg jest większy od jasności tekstury.

Przykładowa tekstura, która dobrze symuluje tworzenie się dziur w obiekcie przedstawiona

jest na rysunku 62.

Rys. 62 Im jaśniejsze piksele, tym dłużej pozostaną widoczne.

Fragment shadera pikseli realizującego odrzucanie pikseli przedstawia poniższy

listing.

float4 main(

vertout IN,

uniform sampler2D texMaska : TEXUNIT0,

...

const uniform float prog)

{

float w = tex2D(texMaska, IN.TexCoord.xy).r;

if (w < prog)discard; // odrzucenie piksela

... // obliczanie koloru materiału

return color;

}

Page 81: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

~ 81 ~

5 Przykładowa gra

Przydatność silnika do tworzenia gier powinna zostać sprawdzona poprzez napisanie

gry. Dlatego też opracowano prostą grę wykorzystującą zintegrowany silnik fizyki.

Stworzenie gry nie wymagało wiele pracy, ponieważ silnik udostępniał solidną bazę.

Stworzona gra polega na jak najszybszym przeprowadzeniu kuli przez labirynt. Za

pomocą myszki gracz obraca planszę, tym samym kierując kulę w odpowiednim kierunku.

Stworzenie gry wymagało tylko stworzenia modelu planszy i oprogramowania

sterowania nią. Gra nakazuje silnikowi wczytanie planszy. Silnik przekazuje ruchy myszki

oraz wciśnięte klawisze do gry, która z kolei przekazuje silnikowi położenie kamery oraz

zmienia kierunek siły grawitacji.

Kilka obrazów z gry przedstawiono na kolejnych rysunkach.

Rys. 63 Gra.

Page 82: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

5 Przykładowa gra

~ 82 ~

Rys. 64 Początek i koniec gry.

Rys. 65 Różne wersje kolorystyczne gry.

Rys. 66 Druga wersja gry.

Page 83: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

~ 83 ~

6 Podsumowanie

Opracowany został wieloplatformowy silnik umożliwiający zbudowanie gry

komputerowej. Najważniejsza część, czyli silnik graficzny, umożliwia tworzenie efektów

i wygodne zarządzanie nimi. Dzięki temu możliwe było stworzenie wielu interesujących

efektów, takich jak głębia ostrości, rozmycie ruchu czy mgła. Zaimplementowano również

bazę mniej zaawansowanych efektów, takich jak rozmycie, wyostrzenie, korekcja gamma

itp.

Z silnikiem został zintegrowany silnik fizyki PhysX firmy NVidia. Wymagało to

wprowadzenia wielu modyfikacji w pierwotnej wersji silnika, powstałego w ramach pracy

inżynierskiej. Silnik graficzny ściśle współpracuje z silnikiem fizyki, ponieważ obiekty

symulowane przez silnik fizyki są tymi, które są wyświetlane na ekranie.

System efektów cząsteczkowych został oparty o silnik fizyki, dzięki czemu

cząsteczki mogą wchodzić w interakcje ze wszystkimi obiektami na scenie. System

umożliwia tworzenie zjawisk, takich jak iskry, dym, ogień oraz eksplozje.

Przystosowanie silnika do działania pod kontrolą systemów Linux oraz Windows,

wymagało stworzenia dla każdego systemu oddzielnej implementacji warstwy

pośredniczącej między silnikiem a systemem operacyjnym.

Opracowany został system portali, pozwalających na przechodzenie światła oraz

obiektów przez portale. Najtrudniejszym zadaniem była integracja silnika fizyki

z systemem portali, ponieważ zastosowany silnik fizyki nie przewidywał takich rozwiązań.

Portale mogą służyć do tworzenia zadań logicznych dla gracza i sprawdzać jego orientację

w nienaturalnie zniekształconej przestrzeni. Najprawdopodobniej portale pojawią się

w wielu nadchodzących produkcjach. Opracowany system portali jest w całości autorski.

Mechanizm zarządzania sceną został oparty o luźne drzewa ósemkowe. Rozwiązanie

to sprawdza się dla zamkniętych oraz otwartych przestrzeni.

Symulacji głębi ostrości w czasie rzeczywistym nie jest zadaniem łatwym, jednak

opracowane rozwiązania są wystarczające m.in. dla gier komputerowych, gdzie jakość nie

musi być idealnie zgodna z rzeczywistością. Opracowane rozwiązanie pozwala na

symulowanie efektu soczewkowego, dzięki czemu rozmyte jasne punkty przybierają

kształt jakiejś figury geometrycznej. Odpowiednie operowanie głębią ostrości nadaje

obrazom filmowego charakteru.

Rozmycie ruchu również jest bardzo ważnym efektem, nadającym grom czy

animacjom filmowego charakteru. Opracowane rozwiązanie wykorzystuje spostrzeżenie,

że podczas ruchu jasne punkty zostawiają wyraźniejszy, jaśniejszy, ślad. Wynik działania

opracowanego efektu jest podobny do tego na obrazach widzianych na poszczególnych

klatkach w filmach.

Opracowany efekt mgły wykorzystuje podobne spostrzeżenie co dwa poprzednie

efekty. Zauważono, że jasne punkty są bardziej widoczne przez mgłę. Można to

zaobserwować w prawdziwym świecie, gdzie w gęstej mgle będziemy widzieć w oddali

tylko jasne punkty, np. źródła światła, natomiast cała reszta będzie całkowicie zakryta

kolorem mgły. Wyniki działania opracowanego efektu są spektakularne.

Page 84: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

6 Podsumowanie

~ 84 ~

Stworzona została baza materiałów dla obiektów. Mechanizm renderowania

kubicznych map otoczenia pozwala na tworzenie przezroczystych i odbijających

materiałów. Materiały takie jak drewno, kamień, szkło, metal prezentują się atrakcyjnie

wizualnie.

W celu zademonstrowania możliwości silnika została stworzona prosta gra.

W dalszej pracy silnik może zostać rozbudowany o kolejne elementy. Mechanizm

zarządzania sceną może być rozszerzony w taki sposób, by mógł również zarządzać

geometrią sceny poprzez portale. Mechanizm ten powinien również pozwalać na płynne

przejścia między scenami otwartymi i zamkniętymi, z których pierwsze będą zarządzane

luźnym drzewem ósemkowym, a drugie portalami.

Ponadto, silnik może zostać rozbudowany o kolejne efekty i materiały. Architektura

pozwala na dodawanie efektów w formie wtyczek, co ułatwia ich tworzenie.

Page 85: Silnik graficzny 3D · tworzenia gier komputerowych. ... 1.1.2 Fizyka ... pokazujące możliwości sprzętu oraz programistów. Systemy

~ 85 ~

Bibliografia

[1] J. D. Foley, A. van Dam, S. K. Feiner, J. F. Hughes, R. L. Phillips, Wprowadzenie do

grafiki komputerowej, Wydawnictwa Naukowo–Techniczne, 2001.

[2] S. Zerbst, O. Düvel, 3D Game Engine Programming, Course Technology PTR, 2004.

[3] J. Gregory, Game Engine Architecture, A K Peters, 2009.

[4] W. Jawor, Principia Silnika

[5] J. Simpson, Game Engine Anatomy 101, http://www.extremetech.com/article2/

0,2845,594,00.asp

[6] V. Mönkkönen, Multithreaded Game Engine Architectures,

http://www.gamasutra.com/features/20060906/monkkonen_01.shtml

[7] OpenGL Overview, http://www.opengl.org/about/overview/

[8] Quake source code, http://www.idsoftware.com/firstweb/business/techdownloads/

[9] Unreal Engine 3 Technology, http://www.unrealtechnology.com/technology.php

[10] John Carmack’s Blog, http://www.armadilloaerospace.com/n.x/johnc/recent updates

[11] Carmack on Shadow Volumes, http://developer.nvidia.com/attach/6832

[12] CryENGINE 3 Specifications, http://www.crytek.com/technology/cryengine-3/

specifications/

[13] Unigine, http://unigine.com/

[14] BSP Tree FAQ, http://www.gamedev.net/reference/articles/article657.asp

[15] BSP Trees: Theory and Implementation, http://www.devmaster.net/articles/bsp-trees

[16] BSP Trees in 3D Worlds, http://web.cs.wpi.edu/~matt/courses/cs563/talks/bsp/

bsp.html

[17] M. Kozioł, Portale, http://kb.komires.net/article.php?id=21

[18] J. Bikker, Building a 3D Portal Engine, http://www.flipcode.com/archives/

Building_a_3D_Portal_Engine-Issue_01_Introduction.shtml

[19] D. Ginsburg, Octree Construction, Game Programming Gems 1, Charles River

Media, 2000.

[20] Havok Physics, http://www.havok.com/index.php?page=havok-physics

[21] NVidia PhysX, http://developer.nvidia.com/object/physx_features.html

[22] Newton Game Dynamics, http://newtondynamics.com

[23] J. Barnett, K. Swift, E. Wolpaw, Thinking With Portals: Creating Valve's New IP,

http://www.gamasutra.com/view/feature/3839/thinking_with_portals_creating_.php

[24] Skinned Mesh, http://frustum.unigine.com/3d/feedback.php?demo=45

[25] DDS, http://msdn.microsoft.com/en-us/library/bb943990.aspx

[26] NVidia Texture Tools 2, http://developer.nvidia.com/object/texture_tools.html

[27] T. Ulrich, Loose Octrees, Game Programming Gems 1, Charles River Media, 2000.

[28] NVidia CG Reference Manual

[29] H. Jorke, M. Fritz, Infitec – A new stereoscopic visualization tool by wavelength

multiplex imaging, Journal of Three Dimensional Images, 2005

[30] T. Igarashi, N. Max, and F. Sillion, Real-Time Depth-of-Field Rendering Using Point

Splatting on Per-Pixel Layers