8th Grudzień 2009, 10:39 am
Tym razem będzie mały update dotyczący silnika. Niby nic, ale można się pochwalić.
Po pierwsze Environmental mapping, czyli proste przybliżenie zjawiska odbicia światła na powierzchni ciała oraz załamania światła na granicy dwóch ośrodków. Wymaga dostarczenia tylko dodatkowej tekstury sześciennej (cube-mapa) oraz skorzystania z dwóch dodatkowych funkcji HLSL: refract i reflect. Można go wzbogacić dodatkowo o efekt Fresnela i rozszczepienie chromatyczne. Całość wygląda jak na screenie obok. Efekt ten nie jest trudny do zaimplementowania, więc szczegółów implementacyjnych nie będzie, zresztą są one w książce CG Tutorial.
Kolejną rzeczą jest skybox. Jak nazwa wskazuje, jest to pudełko z tłem nieba, które powoduje, że scena z obiektami nie wygląda po prostu pusto. Poniżej przedstawiam trzy kroki rysowania skyboxa:
- Potrzebne jest pudełko wraz z teksturą, którą jest już wspomniana wcześniej cube-mapa. Biblioteka D3DX posiada do tego odpowiednią funkcję: D3DXCreateBox(), która jako parametry przyjmuje wymiary pudełka. Najlepiej jest stworzyć sześcian 1:1:1.
- Pudełko powinno obracać się razem z kamerą, ale nie może zmieniać swojej pozycji. Aby to osiągnąć należy użyć tylko macierzy widoku i projekcji. przy czym ostatni wiersz tej pierwszej powinien mieć takie wartości: (0, 0, 0, 1).
- Skybox powinien być pierwszym elementem rysowanym na scenie, a dodatkowym warunkiem koniecznym by był on tłem całej sceny jest wyłączenie zapisu do ZBuffera. Dzięki temu każdy kolejny obiekt sceny będzie rysowany zawsze przed skyboxem. Aby odczytać kolor tekstury zamiast funkcji tex2D należy wykorzystać funkcję texCUBE, do której jako współrzędne tekstury przekazuje się pozycję danego pixela.
Jak widać nie jest to skomplikowane, a dzięki temu scena wygląda o wiele lepiej.
25th Wrzesień 2009, 11:14 pm
Już trochę czasu temu postanowiłem napisać konwerter wspomnianego formatu .sdkmesh do .x i w końcu dopiąłem swego. Mimo tego, że jak już wcześniej wspomniałem, format ten nie nadaje się do modeli animowanych (posiada informacje o animacji, ale nie ma informacji o kościach), można ostatecznie wyciągnąć z niego model statyczny.
To właśnie robi poniższa aplikacja. Wystarczy tylko przeciągnąć plik .sdkmesh na plik aplikacji i w katalogu mesha źródłowego pojawia się wygenerowany plik w formacie .x.
Pobierz
18th Wrzesień 2009, 09:55 pm
Stwierdziłem, że wypadałoby się wreszcie odezwać po kolejnej długiej nieobecności. W końcu udało mi się zaimplementować Skinning w silniku, oczywiście bazując na przykładzie SkinnedMesh z DX SDK. W praktyce zrobiłem to co było w ów sample’u, a jedyną różnicą jest miejsce położenia kości, które u mnie znajdują się w hierarchii zaraz za modelem. Samą hierarchię tworzą modele (zwykłe drzewo, które ustanawia hierarchię na podstawie której tworzone są wynikowe macierze świata dla tych modeli). Póki co mam tylko dwa rodzaje modeli: StaticMesh i SkinnedMesh, ale bez problemu można do niej wstawić innego rodzaju obiekty, które występują na scenie.
Kolejną sprawą są stosowane przeze mnie formaty. Otóż w poprzedniej notce napisałem jakoby format .x był bardzo prostym formatem. Nic bardziej mylnego, jest on bardziej złożony niż myślałem (chodzi mi tylko o format modeli, nie o system zapisu danych). W standardowym pliku .x może znajdować się więcej modeli, które są ułożone w hierarchię, a do ich załadowania służy funkcja:
D3DXLoadMeshHierarchyFromX(…);
Funkcja ta ładuje nie tyko wspomnianą hierarchię meshy, ale również informacje o kościach i animacji. Niestety format materiałów dalej nie zawiera nic więcej poza strukturą D3DMATERIAL9 i nazwą tekstury diffuse.
Natomiast format SDKMesh jest jeszcze gorszy (mimo jednej zalety, że posiada nazwy dla tekstur normal i specular), ponieważ nie posiada on żadnych informacji o kościach mimo, że są informacje o animacji. Po prostu wczytywany mesh jest już przekonwertowany i gotowy do wyświetlenia (co blokuje możliwość prostego użycia ID3DXSkinInfo, którym mógłbym software’owo przekształcić model np. do kolizji per vertex).
3rd Sierpień 2009, 11:20 pm
Pisząc różne efekty graficzne, w pewnym momencie możemy stwierdzić, że potrzebujemy jakiegoś innego modelu. Wtedy nasuwa się również pytanie “skąd wziąć taki model?”. Tutaj przychodzi z pomocą DirectX SDK, które posiada dużą ilość różnych modeli w formacie .x oraz .sdkmesh. Z formatem .x można sobie poradzić funkcją
Funkcja ta wczytuje i parsuje model, zwracając wskaźnik, z którego można już korzystać.
Niestety format .x jest bardzo prosty, ponieważ zawiera on tylko jeden zestaw materiałów i teksturę diffuse na subset. Problem ten rozwiązano tworząc nowy format z rozszerzeniem .sdkmesh. Powstał on i jest wykorzystywany na potrzeby przykładów Direct3D 10. Pliki tego formatu mogą zawierać kilka modeli, podzielonych na kilka subsetów. Materiały zostały rozszerzone o tekstury normal i specular. Dodane zostały również informacje o klatkach animacji, natomiast szczegóły danej klatki zawiera osobny plik z rozszerzeniem .sdkmesh_anim. Więcej szczegółów dotyczących tego formatu znajduje się w dokumentacji pod hasłem “Overview of the SDK Mesh File Format”.
Postanowiłem się zainteresować tym formatem nie tylko z powodu tego, że część modeli jest w nim zapisana, ale potrzebowałem modeli, które mógłbym wykorzystać do mojej zabawy z animacją opartą na klatkach kluczowych. Właśnie z tego powodu napisałem aplikację, którą tu zamieszczam. SDKMeshInfo jest programem do podglądu bebechów plików .sdkmesh. Nie posiada on opcji ich wyświetlania, ale pokazuje pełne dane z nagłówków zawartych we wskazanym pliku.
Download
26th Lipiec 2009, 01:46 am
Postanowiłem wreszcie zająć się wyświetlaniem grafiki opartej na shaderach. Stworzyłem sobie prosty framework oparty na moim “silniku”, który jest aktualnie tylko szkieletem aplikacji. Framework składa się z kilku klas, których zadaniem jest uprościć wykonywanie różnych rzeczy. Aktualnie w zestawie jest kamerka FPP, prosty system cząsteczek oraz klasy do wczytywania modeli i zarządzania efektami.
Na dole tej notki znajduje się link do aplikacji, która reprezentuje aktualny stan kodu. Aplikacja wyświetla 4 modele wczytane z plików w formacie .x, 1 model z formatu .sdkmesh oraz cząsteczki, których pozycja obliczana jest w shaderze. Światło użyte w scenie jest punktowe, a obliczenia są wykonywane w pixel shaderze. Materiały modeli pochodzą z ich plików, dlatego nie wszystko wygląda super ;).
Kamerkę obsługuje się za pomocą myszy i klawiszy WSAD, a światło za pomocą strzałek oraz klawiszy PG_UP i PG_DN.
Pobierz
12th Luty 2009, 01:00 am
Nawiązując do poprzedniej notki chcę tylko zamieścić jak wyglądają klasy, które napisałem i czemu są takie fajne:
// Interfejs
class IMesh
{
public:
/*
...
*/
// Funkcja aktualizująca mesha
virtual void SetMesh(LPD3DXMESH mesh) = 0;
// Funkcja dodająca materiał do wektora
virtual void AddMaterial(const Material& mat) = 0;
// Funkcja zwraca materiał o podanym indeksie
virtual Material GetMaterial(Dword index) = 0;
/*
...
*/
};
// Główna klasa Mesh
class Mesh : public IMesh
{
public:
// Konstruktor
Mesh();
// Destruktor
~Mesh();
public:
/* Funkcje używane do rysowania modelu
...
*/
protected:
/*
...
*/
// Funkcja aktualizująca mesha
virtual void SetMesh(LPD3DXMESH mesh) = 0;
// Funkcja dodająca materiał do wektora
virtual void AddMaterial(const Material& mat) = 0;
// Funkcja zwraca materiał o podanym indeksie
virtual Material GetMaterial(Dword index) = 0;
/*
...
*/
};
// Przykład loadera
class X_file
{
public:
// Konstruktor
X_file(IMesh* mesh) { m_mesh = mesh; }
// Funkcja ładująca
RESULT LoadMesh(String filename)
{
LPD3DXMESH mesh;
/*
... cuda-niewidy...
*/
m_mesh->AddMesh(mesh); // Wywołanie funkcji interfejsu
}
private:
// Obiekt na którym działam
IMesh* m_mesh;
};
Nie lubię się rozpisywać. Jak można zauważyć dzięki interfejsowi IMesh można załadować dowolny model do klasy Mesh, ponieważ są publiczne wszystkie funkcje umożliwiające to zadanie, natomiast klasa Mesh posiada publiczne funkcje służące tylko i wyłącznie do rysowania modelu. Dzięki takiemu rozwiązaniu kod jest przejrzysty.
8th Luty 2009, 04:49 am
Minęło trochę czasu od mojej ostatniej notki, ale co poradzić, studia są trochę wymagające, więc trzeba się czasem pouczyć, zwłaszcza, że jest to styczeń czyli ostatni miesiąc przed sesją. W momencie, gdy piszę tę notkę, upływa właśnie pierwszy tydzień tej najbardziej nielubianej przez studentów pory roku :). Wracając jednak do tematyki tego devBloga należałoby powiedzieć czym ciekawym zajmowałem się przez ten czas, przecież nie samą nauką człowiek żyje.
Postanowiłem dodać do mojego silnika możliwość wczytywania różnego rodzaju plików z meshami. Na początek napisałem prostą klasę opakowującą interfejs ID3DXMesh, w której umieściłem strukturę przechowującą materiały danego mesha. Następnie napisałem funkcje automatyzujące odczyt i zapis plików w formacie .x. Schody zaczęły się, gdy chciałem dodać obsługę dodatkowego formatu – zdecydowałem, że będą to pliki .obj (Object files). OBJ są to pliki tekstowe, przechowujące informacje o całym meshu (fajna specyfikacja tego formatu znajduje się tutaj), z którymi w parze są jeszcze pliki .mtl, gdzie trzymane są informacje o materiałach dla danego subsetu (więcej info tutaj). Najważniejszą sprawą w całym tym przedsięwzięciu było napisanie parsera dla tego formatu. Na szczęście z pomocą przychodzi Microsoft DirectX SDK, w którym znajduje się przykładowy program (“MeshFromOBJ”) wczytujący pliki OBJ.
Pisząc klasy obsługujące różne modele chciałem, aby istniała możliwość konwersji między tymi formatami. Powstała więc koncepcja dwóch rodzajów klas – klasa Mesh oraz klasy obsługujące formaty. Klasy te mają być od siebie w jak największym stopniu niezależne, czyli ma istnieć możliwość dodawania funkcjonalności do klasy Mesh bez konfliktu z klasami wczytującym oraz musi istnieć możliwość pisania dodatkowych klas obsługujących różne formaty. Szczerze mówiąc, pierwszy raz zetknąłem się z takim problemem, ale na szczęście udało mi się znaleźć proste i eleganckie rozwiązanie. Mianowicie zastosowałem prosty interfejs, po którym dziedziczy klasa Mesh, z tym wyjątkiem, że klasa interfejsu posiada funkcje abstrakcyjne public, a klasa Mesh definiuje te funkcje jako protected, bądź private. Dzięki takiemu podejściu klasy korzystające z interfejsu, mogą operować na tych funkcjach, a klasa Mesh ma te funkcje ukryte :).
4th Maj 2008, 01:59 am
Zajmując się ostatnio moim silnikiem, natrafiłem na jedną bardzo ciekawą zaletę Visty. Otóż przypadkiem odkryłem jak sztucznie wywołać utratę urządzenia w DirectGraphics. Wystarczy do tego użyć Ctrl + Alt + Del, gdyż Vista w tym momencie przechodzi na ekran wyboru czynności, więc prawdopodobnie przejmuje całkowitą kontrolę nad urządzeniem, czego skutkiem (pewnie ubocznym) jest utrata urządzenia.
Mnie ta sztuczka się bardzo przydała, ponieważ dzięki temu wykryłem, że miałem problem z odzyskiwaniem urządzenia.
23rd Kwiecień 2008, 07:46 pm
Postanowiłem napisać do mojego silnika moduł 2D, gdyż od jakiegoś czasu mnie nosiło, aby napisać symulacje rzutów fizycznych (głównie ukośnego – pewnie przez to, że zbliżają się matury). Po raz pierwszy jednak udało mi się napisać własny shader, co uważam za niebywałe osiągniecie, chociaż jedyną czynnością jaką on wykonuje jest wyświetlenie tekstur. Wracając do modułu. Jest on bardzo prosty, ponieważ składa się tylko z 2 klas: ImageBox oraz Image. Klasa ImageBox jest pojemnikiem na obiekty typu Image. Jej zadaniami są:
- Tworzenie i likwidacja obiektów Image
- Zarządzanie wewnętrznym Vertex Bufferem
- Zarządzanie shaderem (który służy tylko do próbkowania tekstury)
- Likwidacja i przywracanie zasobów na czas resetowania urządzenia
Funkcja rysująca i zestaw funkcji do manipulowania obrazkami są dostępne w klasie Image, dzięki czemu można samodzielnie kontrolować czy obrazek ma zostać narysowany oraz co zostanie zmienione. Z racji tego, że przed narysowaniem czegokolwiek, konieczne jest ustawienie shadera, rysowanie odbywa się w bloku BeginPainting() (…) EndPainting(). Podobnie jest z manipulacją obiektami, gdyż trzeba zablokować VertexBuffer.
Oto prosty przykład zastosowania tego modułu w “symulacji” rzutu ukośnego.
Download – wymaga sm 1.1
27th Listopad 2007, 09:13 pm
Ostatnio wymyśliłem dość prosty TextureManager (Resource był w planie, ale nie wiem wszystkiego o zasobach DX, które trzeba zwalniać, a o teksturach wiem wystarczająco). Właściwie to nazwanie go jest lekką przesadą, ponieważ składa się on ze struktury, vector’a i 2 funkcji. Vector zawiera obiekty struktury, która składa się ze adresu tekstury, licznika użyć oraz wskaźnika na interfejs tekstury. Vector’em zarządzają 2 funkcje:
LoadTexture
DeleteTexture
Pierwsza sprawdza czy nie ma już załadowanej takiej tekstury jeżeli jest to zwiększa jej licznik i zwraca interfejs. W przeciwnym przypadku tworzy i dodaje do vector’a
Druga sprawdza po wskaźniku czy jest, jeżeli tak to dekrementuje po czym sprawdza licznik, jeżeli spadł do 0 to zwalnia teksturę, kasuje z vector’a.
Składniki zawierają się w sekcji prywatnej klasy Mesh, przy czym vector jest statyczny, dzięki czemu dostępny dla wszystkich potomków klasy abstrakcyjnej Mesh.