Posts tagged ‘DirectX’

Prosty menedżer zasobów?…

2010-06-28 18:09

Silnikologia (przynajmniej ta warsztatowa) ma swoje dziwnostki. Za jedną z nich uważam przykładanie zbyt dużego znaczenia do tych podsystemów engine‘u gry, które są zdecydowanie mniej ważne niż silnik graficzny czy fizyczny. Podpadają pod to (między innymi): mechanizmy logowania, VFS-y (wirtualne systemy plików), kod odpowiedzialny za wczytywanie i zapisywanie opcji konfiguracyjnych, itp. Blisko szczytu tej listy znajduje się też podsystem, o którym chcę napisać dzisiaj kilka słów. Chodzi mianowicie o menedżer zasobów (resource manager).

Pod tą nazwą kryję się obiekt, którego zadaniem jest – jak nazwa zresztą wskazuje – zarządzanie różnego rodzaju zasobami gry, gdzie pod pojęciem ‘zasobu’ kryje się zwykle jakiś element jej contentu: model, tekstura, próbka dźwiękowa, czcionka, shader… Menedżer ma za zadanie udostępniać odwołania do tych zasobów elementom silnika/gry, które ich potrzebują. To jest jego główny, nadrzędny cel.
Tu jednak dochodzimy do małego paradoksu. Jeśli bowiem jest to jedyne przeznaczenie modułu od zarządzania zasobami, to tak naprawdę mogłoby go w ogóle nie być!… W praktyce jego istnienie usprawiedliwia przynajmniej jeden z poniższych powodów:

  • jednoczesne załadowanie wszystkich zasobów gry czy nawet pojedynczego etapu/krainy/lokacji/itp. nie jest możliwe z uwagi na ograniczony rozmiar pamięci
  • ładowanie zasobów trwa na tyle długo, że rozsądne jest wydzielenie tego procesu do osobnego wątku
  • kompatybilność z DirectX w wersjach poniżej 9Ex wymaga obsługi zjawiska utraty urządzenia, a więc ponownego wczytania zasobów umieszczonych w pamięci karty graficznej
  • między poszczególnymi zasobami występują zależności, które nie pozwalają na łatwe określenie prawidłowej kolejności ich zwalniania

Tego rodzaju wymagania rzeczywiście można rozsądnie zrealizować dopiero wówczas, gdy w logiczny sposób wydzielimy z kodu część, która za kontrolę zasobów będzie odpowiadać. Jeśli jednak żadna z powyższych sytuacji nie aplikuje się do nas, nie ma żadnego powodu, by zabierać się za tworzenie modułu zarządzania zasobami tylko dlatego, że “przecież zasoby gdzieś muszą być”. Coś takiego jak ‘prosty menedżer zasobów’ to oksymoron: jeśli faktycznie może być prosty, to najpewniej znaczy, iż jest zbędny. No, chyba że pod tym pojęciem rozumiemy też coś, co w praktyce da się zredukować do:

  1. Texture* sprites[SPRITES_COUNT];
  2. Sound* sounds[SOUNDS_COUNT];

W takim przypadku powinniśmy zadbać przede wszystkich o to, by nasz Menedżer Zasobów (caps intended) dobrze współpracował z Modułem Wyświetlania Życia Gracza, że o Silniku Od PauzyTM nie wspomnę ;P

Po co jest FORCE_DWORD

2010-05-12 15:28

Przeglądając dokumentację do DirectX (a przynajmniej do części graficznej) można natknąć się na wiele typów wyliczeniowych. Większość z nich (a może wszystkie?) na końcu swojej definicji ma stałą o nazwie kończącej się na _FORCE_DWORD. Przykładem jest znany, lubiany i przez wszystkich używany D3DRENDERSTATETYPE:
typedef enum D3DRENDERSTATETYPE
{
// (co najmniej 1<<8 różnych stałych) D3DRS_FORCE_WORD = 0x7fffffff; } D3DRENDERSTATETYPE, *LPD3DRENDERSTATETYPE;[/cpp] Zjechanie na sam dół pomocy pouczy nas, że stała ta zasadniczo... nie jest używana. Jednocześnie jednak ma ona wymuszać kompilację typu wyliczeniowego jako 32-bitowego. O co tutaj właściwie chodzi? Kompilatory C++ mogą mianowicie wybierać dla typów wyliczeniowych właściwie dowolne typy liczbowe - byle tylko wszystkie wartości stałych się zmieściły. To sprawia, że wielkość enuma może się różnić nie tylko między kompilatorami, ale i między różnymi ustawieniami kompilacji. Nietrudno na przykład wyobrazić sobie, że przy optymalizacji szybkości ów enum będzie miał rozmiar równy słowu maszynowemu, zaś przy optymalizacji zajętości pamięci będzie to rozmiar najmniejszy możliwy.
No i tu zaczynają się schody tudzież pagórki. Zmienność (a raczej niezdefiniowanie) wielkości typu wyliczeniowego jest może kłopotliwa w pewnych sytuacjach. Trudno byłoby na przykład przewidzieć to, w jaki sposób należy odczytać wartość zwróconą przez metodę GetRenderState urządzenia, która jest zapisana w 32-bitowym DWORD-zie, jeśli nie zajmowałaby ona w nim wszystkich 4 bajtów. Podejrzewam też, że na którymś etapie renderowania we wnętrzu DirectX określony rozmiar pewnych flag (np. typów prymitywów) jest po prostu wymuszany przez sterownik karty graficznej. Całkiem rozsądne jest więc zapewnienie go od samego początku – czyli już w kodzie pisanym przez programistę-użytkownika DirectX.

Czemu jednak potrzebny jest takich hack? Ano tutaj znowu wychodzi niedookreślenie pewnych rzeczy w standardzie C++, zapewne z powodu źle pojętej przenośności. Częściowo zostanie to naprawione w przyszłej wersji standardu, gdzie – podobnie jak np. w C# – możliwe będzie określenie typów liczbowych używanych wewnętrznie przez enumy.

Tags: , , , ,
Author: Xion, posted under Programming » Comments Off on Po co jest FORCE_DWORD

Jak się robi screeny

2010-04-20 16:19

W robieniu zrzutów ekranowych (screenshotów) nie ma, wydawałoby się, nic nadzwyczajnego. Wciskamy po prostu klawisz Print Screen i obraz ekranu ląduje nam w systemowym Schowku, po czym możemy go użyć w dowolny sposób. Przynajmniej tego właśnie się spodziewamy zazwyczaj.
W rzeczywistości robienie screenów to dość śliski temat, jeśli mamy do czynienia z czymś więcej niż zwykłymi aplikacjami okienkowymi. Dotyczy to chociażby pełnoekranowych gier, korzystających z bibliotek graficznych typu DirectX i OpenGL. Wykorzystują one mechanizm znany jako hardware overlay, pozwalający – w skrócie – na ominięcie narzutu systemu operacyjnego związanego z zarządzeniem oknami wielu aplikacji na współdzielonym ekranie monitora. Skutkiem ubocznym jest między innymi to, że wbudowany w system mechanizm tworzenia zrzutów ekranu staje się w tej sytuacji bezużyteczny, jeśli chcemy wykonać screenshota tego rodzaju aplikacji.

Skąd w takim razie biorą się screeny, a nawet całe filmy ze współczesnych gier?… Cóż, istnieją oczywiście metody pozwalające na przechwytywanie obrazów generowanych przez aplikację – można bowiem skłonić ją do wywoływania naszej własnej wersji metody IDirect3DDeviceN::Present zamiast tej wbudowanej :) Brzmi to pewnie tajemniczo i nieco hakersko, ale tak mniej więcej działają programy do przechwytywania wideo typu Fraps.

Do zwykłych screenów zazwyczaj jednak przewiduje się odpowiednie rozwiązania w samych grach. Jednym ze sposobów jest, w przypadku wykrycia wciśnięcia klawisza odpowiadającego za screenshoty (czyli zwykle Print Screen), wyrenderowanie sceny najpierw do tekstury, a ją potem na ekran. Rzeczoną teksturę można później zapisać do pliku.


Aliasing (i nie tylko)
w pełnej krasie

Czy to dobre rozwiązanie? Otóż nie, i to aż z trzech powodów. Stosunkowo najmniej ważnym jest to, iż implementacja może być kłopotliwa, bo wymaga dobrania się do początku i do końca potoku renderingu w aplikacji z kolejnym render targetem. Bardziej istotny jest brak multisamplingu (a więc wygładzania krawędzi wielokątów znanego jako anti-aliasing) w powstałym obrazku. Nie będzie on więc wyglądać zbyt ładnie, już nie wspominając o tym, że nie będzie on odpowiadał temu, co widzimy na ekranie.

Jak więc należy robić screeny? Dokładnie tak, jak nam podpowiada intuicja: należy wziąć to, co widać na ekranie – czyli zawartość bufora przedniego (front buffer) i zapisać to do pliku. W DirectX mamy do tego metodę urządzenia o nazwie GetFrontBufferData, która pobiera nam to jako powierzchnię:

  1. IDirect3DSurface9* screen;
  2. dev->CreateOffscreenPlaneSurface(Width, Height, D3DFMT_A8R8G8B8,
  3.     D3DPOOL_SCRATCH, &screen, NULL);
  4. // screenshot i zapis go do pliku
  5. dev->GetFrontBufferData(0, &screen);
  6. D3DXSaveSurfaceToFile(ScreenFile, D3DXIFF_JPG, screen, NULL, NULL);

Ważne jest, by pamiętać o właściwych rozmiarach powierzchni – takich, jakie ma bufor przedni. W trybie pełnoekranowym odpowiada to buforowi tylnemu, ale dla aplikacji okienkowej wcale nie musi, jeśli rozmiar jej okna możemy zmieniać.

W OpenGL podobną rolę spełnia do GetFrontBufferData spełnia funkcja glReadPixels. Z zapisaniem pobranego obrazu może być tylko “nieco” trudniej ;-)

Narzędzia do DirectX

2010-03-06 17:38

Zasadniczą i najważniejszą częścią DirectX SDK są pliki nagłówkowe oraz biblioteki (statyczne i dynamiczne), które pozwalają na pisanie programów korzystających z tego API. Do tego mamy jeszcze niezbędną dokumentację oraz przykładowe aplikacje (samples), pokazujące wykorzystanie poszczególnych jego elementów lub prezentujących implementacje różnych efektów graficznych.
Ale to nie wszystko, co można znaleźć w tym kilkusetmegabajtowym (i ciągle rosnącym) pakiecie. Niemal równie ważne są narzędzia pomocnicze, które można tam znaleźć. Podczas tworzenia aplikacji wykorzystujących zwłaszcza Direct3D umiejętność korzystania z tych programów jest niekiedy prawie tak samo ważna, jak znajomość samego API czy zagadnień z dziedziny grafiki.

Dlatego też postanowiłem pokrótce opisać niektóre z nich, żeby co mniej zaawansowani programiści DirectX mogli przynajmniej dowiedzieć się, że takowe istnieją :) Oto więc rzeczone aplikacje:

  • DirectX Caps Viewer pozwala na podejrzenie możliwości naszego sprzętu, czyli jego capsów (od capabilities) – normalnie zwracanych przez metodę GetDeviceCaps urządzenia – w postaci przejrzystego interfejsu drzewiastego. Dobrze jest rzecz jasna wiedzieć, czego szukamy, ale w większości przypadków programistów grafiki 3D interesować będzie gałąź Direct3D9/10 Devices/<model karty graficznej>/D3D Device Types/HAL/Caps.
  • DirectX Control Panel to, jak sama jego nazwa wskazuje, panel kontrolny całego DirectX-a, którym możemy kontrolować zachowanie poszczególnych jego elementów. Pewnie najbardziej interesującym dla programistów elementem jest “magiczny” suwak Debug Output Level, dzięki któremu możemy regulować ilość komunikatów wysyłanych do debugera przez runtime DirectX, co przy wyższych ustawieniach ułatwia znajdowanie (przyczyn) błędów.
  • DirectX Error Lookup to z kolei małe sprytne narzędzie tłumaczące liczbowe kody błędów funkcji DX (typu HRESULT) na odpowiadające im stałe i komunikaty. To pierwsze potrafi też częściowo zrobić debuger Visual Studio (czujką $err,hr lub $eax,hr), ale mimo to programik ten bywa niekiedy przydatny.
  • DirectX Texture Tool jest natomiast już bardziej skomplikowaną aplikacją. Jej ogólnym przeznaczeniem jest tworzenie i edycja tekstur w dedykowanym dla DirectX formacie .dds. Oczywiście nasze programy mogą korzystać z tekstur w innych formatach graficznych, ale użycie Texture Tool jest niekiedy niezbędne – chociażby wtedy, gdy musimy stworzyć mapy sześcienne (cubemaps).
  • DirectX Viewer to podglądacz – przy jego pomocy możemy obejrzeć w 3D modele zapisane w plikach .x, a także rezultaty uruchomienia efektów (z plików .fx). Napisanie czegoś podobnego nie byłoby trudne, ale dzięki istnieniu Viewera nie musimy tego robić :)
  • Wreszcie, PIX for Windows to zdecydowanie największa aplikacja w tym zestawie. Jej przeznaczeniem jest wszechstronne (także wydajnościowe) debugowanie aplikacji wykorzystujących Direct3D. Na jej temat można by napisać sporo, więc całkiem możliwe, że nie omieszkam tego zrobić w przyszłości :)

Niektóre z tych narzędzi są na tyle użyteczne, że warto zrobić sobie do nich skróty w łatwo dostępnych miejscach (dotyczy to chociażby Control Panelu). Wszystkie zaś możemy znaleźć wśród linków tworzonych w menu Start przez instalator SDK, w podkatalogu DirectX Utilities.

Tags: ,
Author: Xion, posted under Programming » Comments Off on Narzędzia do DirectX

O obiektowości

2010-02-12 16:58

Kiedy programiści się nudzą, to często spierają się o terminologię. Przedmiotem takich sporów są zwykle nieco mgliste określenia, które czasami – zwykle niesłusznie – używane są jako atrybuty oceniające. Przykład? Choćby “obiektowość” – w zależności od punktu widzenia stwierdzenie, że dany kod/biblioteka/moduł/itp. są bardziej lub mniej obiektowe może stanowić albo zaletę, albo wadę. Zależy to głównie od ‘poglądów’ danej osoby na temat użyteczności podejścia OO w programowaniu.

Co to jednak znaczy, że dany kod jest obiektowy?… Nie uważam wcale, że należy go w tym celu napisać go w języku obiektowym. Twierdzę na przykład, że Windows API jest całkiem dobrym przykładem biblioteki realizującej obiektowy paradygmat, mimo tego że została ona stworzona w jak najbardziej strukturalnym C. Praktyczna różnica między poniższymi wywołaniami:

  1. foo->DoSomething (...);
  2. DoSomething (foo, ...);

jest bowiem właściwie żadna. Dodajmy do tego fakt, że z punktu widzenia programisty-użytkownika w WinAPI występuje zarówno dziedziczenie (rodzajów uchwytów), jak i polimorfizm (funkcje niezależne od typu uchwytów, np. CloseHandle), a więc bardzo obiektowe feature‘y.
Jeśli komuś wydaje się to naciągane i twierdzi, że w ten sposób pod obiektowość można podciągnąć właściwie każdy kod, to już spieszę z przykładem ukazującym, że tak nie jest. Otóż większa część biblioteki OpenGL obiektowa na pewno nie jest, zaś w tych nielicznych miejscach gdzie OOP jest niemal konieczny (jak zarządzanie teksturami czy buforami wierzchołków) pojawia się nieco dziwna koncepcja ‘indeksów’ używanych do identyfikowania poszczególnych obiektów.

Dla niektórych (łącznie ze mną) taki interfejs nie jest szczytem marzeń, a preferowane jest wyraźnie obiektowe podejście DirectX. Absolutnie jednak nie zgadzam się z twierdzeniem, że DirectX jest lepszy, bo bardziej obiektowy – to trochę tak jak powiedzenie, że ten obrazek jest ładniejszy, bo bardziej zielony. W obu przypadkach jest to kwestia gustu i nie powinniśmy zakładać, że cechy pozytywne dla nas są tak samo dobrze odbierane przez innych.
A wyższość DirectX nad OpenGL da się przecież uargumentować na wiele innych, lepszych sposobów :)

Tags: , , ,
Author: Xion, posted under Programming, Thoughts » 3 comments

Łączenie efektów graficznych

2010-01-27 9:29

Internet pełen jest opisów, tutoriali i przykładowych kodów pokazujących, jak implementować różne efekty graficzne. Zakodowanie ich pojedynczo zazwyczaj nie jest więc problemem, o ile mamy jako takie pojęcie o grafice czasu rzeczywistego, bibliotece DirectX/OpenGL i programowaniu w ogóle.
Znacznie większym problemem jest połączenie kilku(nastu/dziesięciu) efektów tak, by było one zaaplikowane w jednym momencie do tej samej sceny. Ze względu na to, że każdy pojedynczy efekt może wymagać kodu w bardzo różnych miejscach potoku graficznego (chociażby w samej aplikacji oraz w kodzie shaderów), zintegrowanie wszystkich tych fragmentów nie wydaje się sprawą prostą.

Ostatnio aczkolwiek zajmowałem się praktycznym rozwiązywaniem tych kwestii; było to łączenie różnych rodzajów oświetlenia z cieniami generowanymi techniką shadow depth mapping i efektami postprocessingu w rodzaju depth of field. Pozwolę więc sobie podzielić kilkoma uwagami na ten temat. To może jeszcze nie są rady, jak dobrze zaprojektować architekturę silnika 3D, ale mały framework pewnie można o nie oprzeć ;] A zatem:

  • Należy wydzielić kod zajmujący się rysowaniem samych obiektów na scenie, gdyż będzie on wywoływany wielokrotnie. Niektórym może wydawać się to oczywiste, ale w ilu przykładowych kodach wywołania DrawPrimitive czy DrawSubset są w tej samej funkcji co Begin/EndScene? W rzeczywistym kodzie zapewne tak nie będzie, bo dana scena będzie na pewno renderowana wielokrotnie.
  • Trzeba odpowiednio zająć się macierzami przekształceń. Ważne jest na przykład wydzielenie w shaderze macierzy lokalnego przekształcenia każdego obiektu. Nie można jej po prostu złączyć z macierzą WORLD (lub MODELVIEW w OpenGL), bo nasza scena będzie renderowana kilka razy w potencjalnie różnych widokach (kamery, światła, obiektu odbijającego otoczenie, itp.). Dodatkowo mogą być nam potrzebne punkty w różnych przestrzeniach, np. w układzie widoku obserwatora i widoku od konkretnego światła naraz. Wreszcie, nie należy zapominać o prawidłowym przekształcaniu wektorów normalnych. W sumie więc sekcja deklaracji pliku z shaderami może wyglądać np. tak:
    1. float4x4 ObjectTransform; // przekszt. lokalne obiektu
    2. float4x4 CameraWorld; // przekszt. globalne sceny
    3. float4x4 CameraWorldRotation; // jw. ale z samą rotacją
    4. float4x4 CameraView; // przekszt. do przestrzeni widoku
    5. float4x4 CameraProjection; // przekszt. do przestrzeni rzutowania
    6. float4x4 LightWorldViewProjection; // przekst. do przestrzeni światła
    7. // itd.

    Są tutaj jeszcze dwie sprawy warte zaznaczania. Po pierwsze, obiekty rysujące się na scenie muszą wiedzieć, gdzie ustawiać swoją macierz lokalnego przekształcenia. We wszystkich używanych shaderach nazwa odpowiedniej stałej (tutaj ObjectTransform) musi być taka sama; najlepiej też żeby mapowała się na te same rejestry stałych cn. Naturalnie kod renderujący obiekty musi też “wiedzieć”, żeby korzystać właśnie z niej zamiast z macierzy przekształceń z fixed pipeline – czyli np. wywoływać effect->SetMatrix("ObjectTransform", &mat); zamiast device->SetTransform (D3DTS_WORLD, &(currWorld * mat)); w przypadku DirectX).
    Po drugie, nie trzeba “dla efektywności” przekazywać do shadera iloczynów macierzy, jeśli używamy także ich poszczególnych czynników. Można bowiem zupełnie bezkarnie mnożyć je na początku kodu shadera:

    1. float4x4 CameraObjectWorld = mul(ObjectTransform, CameraWorld);
    2. float4x4 CameraWVP = mul(CameraObjectWorld, mul(CameraView, CameraProjection));
    3. // dalej reszta shadera
    4. Out.Position = mul(float4(In.Position, 1), CameraWVP);

    Kompilator wydzieli ten kod w postaci tzw. preshadera i zapewni, że będzie on wykonywany tylko raz (a nie dla każdego wierzchołka/piksela).

  • Konieczne jest zadbanie o dobrą obsługę render targetów. Powinna być ona przezroczysta dla poszczególnych efektów – nie muszą one wiedzieć, czy renderują bezpośrednio na ekran, czy do tekstury. Jednocześnie każdy efekt powinien móc określić, do którego RT chce aktualnie renderować i mieć potem możliwość wykorzystania wyników jako tekstur w kolejnych przebiegach. Generalnie do tych celów wystarcza prosty menedżer oparty np. na słowniku identyfikującym poszczególne RT za pomocą nazw: "ShadowMap", "DepthMap, "Scene" itp.
  • W bardziej skomplikowanych przypadkach trzeba pewnie będzie złączyć shadery. W chwili obecnej jest to pewnie jeden z najbardziej złożonych problemów przy tworzeniu silnika graficznego, ale istnieje szansa, że wprowadzane w DirectX 11 dynamiczne linkowanie shaderów będzie to w istotny sposób ułatwiało.
    Jeśli na razie nie chcemy się mierzyć z tym problemem, to można niekiedy go ominąć kosztem dodatkowych przebiegów renderowania. Przykładowo, cienie można nakładać na gotową scenę z już policzonym oświetleniem zamiast oświetlać i cieniować piksele w jednym passie.

Ogólnie trzeba przyznać, że implementowanie wielu efektów działających naraz w tej samej scenie to zagadnienie złożone i dość trudne. Chociaż więc starałem się podać kilka porad na ten temat, to w rzeczywistości niezbędne jest tutaj spore doświadczenie z różnymi rodzajami efektów, zarówno w teorii jak i praktyce.

Shadery i pliki efektów – sprostowanie

2009-12-11 16:58

Płynne przejście od programowania grafiki 3D przy pomocy fixed pipeline do wykorzystania shaderów nie jest z początku takie proste. Jest oczywiście w sieci mnóstwo tutoriali, które to ułatwiają. Zauważyłem jednak, że mają one tendencję do przekazywania naciąganych – mówiąc delikatnie – faktów na temat tego, w jaki sposób shadery oraz pliki efektów .fx (często omawiane łącznie) muszą być wykorzystywane w aplikacjach DirectX.
Dlatego pomyślałem sobie, że dobrze byłoby sprostować kilka mitów, jakie się tutaj pojawiają i wprowadzają zamieszanie do i tak niełatwej dziedziny programowania. Warto bowiem wiedzieć, iż to nieprawda, że:

  • …korzystając z shaderów, musimy używać też plików .fx. Shader wierzchołków czy pikseli możemy samodzielnie wczytać z pliku (w wersji skomplikowanej lub przeprowadzić kompilację przy pomocy funkcji D3DX) czy nawet wygenerować dynamicznie, a potem zwyczajnie ustawić go jako aktualny VS/PS przy pomocy metod SetVertex/PixelShader urządzenia DirectX. Plik efektów nie jest wtedy do niczego potrzebny.
  • …pliki .fx są tylko po to, by łatwiej wczytywać/aplikować shadery do renderowania. W rzeczywistości celem istnienia plików efektów jest uproszczenie kodowania różnych efektów graficznych w wersjach zależnych od możliwości poszczególnych kart. Zamiast samodzielnie sprawdzać capsy (możliwości) sprzętu, na którym uruchamia się nasza aplikacja, możemy po prostu pozwolić DirectX-owi wyszukać w pliku .fx tą wersję efektu (tzw. technikę), która na danej konfiguracji sprzętowej będzie działać. W ten sposób możemy na przykład napisać dwie wersje oświetlenia: jedną per pixel z użyciem shaderów i drugą, używającą fixed pipeline i oświetlającą wierzchołkowo. Nie musi ona wtedy korzystać z żadnych shaderów.
  • …aby stosować shadery, musimy porzucić FVF na rzecz vertex declaration. Jest to piramidalna bzdura, która na szczęście rzadko bywa wygłaszana wprost, ale często wynika ze sposobu omawiania tematu VD w tutorialach. W istocie stałe D3DFVF tak samo dobrze opisują format danych dla vertex shaderów, jak deklaracje wierzchołków (IDirect3DVertexDeclarationX) i DirectX nie ma problemu z łączeniem jednego z drugim. Analogia działa zresztą też w drugą stroną: użycie fixed pipeline nie wymusza korzystania z FVF.
    Oczywiście bardziej zaawansowane techniki pisane z użyciem shaderów najczęściej potrzebują na wejściu danych, których w FVF zapisać się nie da (np. wektorów stycznych i binormalnych). Wtedy rzecz jasna potrzebne jest określenie formatu wierzchołków poprzez vertex declaration. Dla prostych VS-ów (jak choćby zwykłego m4x4 oPos, v0, c4) sam fakt korzystania z programowalnego potoku grafiki nic nie wymusza w zakresie formatu danych wierzchołków.
  • …vertex i pixel shadery musimy zawsze stosować łącznie. To prawie logiczna konsekwencja częstego stwierdzenia (też niespecjalnie prawdziwego), że wyjście VS-a jest wejściem PS-a. W rzeczywistości nie jest to prawdą i łatwo można podać przykłady technik, w których możemy np. użyć pixel shadera bez vertex shadera – jak choćby zamiana wszystkich kolorów w scenie na odcienie szarości.
  • …shadery zastępują cały potok renderowania. Chociaż oświetlenie, teksturowanie, transformacje macierzowe wierzchołków itp. to spory kawałek potoku renderowania, zastąpienie ich shaderami wcale nie oznacza, że nic już poza tym nie zostało. Rzeczy takie jak alpha blending, usuwanie tylnich ścian (culling), różne testy pikseli (głębokości, alfa, stencil) i przycinanie (np. płaszczyznami oraz scissor test) to tylko niektóre z elementów potoku, które są dostępne niezależnie od tego, czy obecne są w nim także załadowane przez użytkownika shadery.
Tags: , ,
Author: Xion, posted under Programming » 2 comments
 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.