Obecnie biblioteki graficzne umieją już bardzo, bardzo dużo. W narzędziach takich jak DirectX czy OpenGL mamy ogromne możliwości efektywnego wyświetlania grafiki trójwymiarowej, a jednocześnie ich obsługa staje się z kolejnymi wersjami coraz łatwiejsza. Należy oczywiście poznać nieco matematycznych podstaw stojących za całą grafiką 3D: przekształceniami, oświetleniem, buforem głębi, i tak dalej. Zasadnicza część – czyli cały potok renderowania oraz rasteryzacja – jest już jednak wykonana za nas.
Powiedzmy jednak, że nie zadowala nas ten stan rzeczy i ze względów edukacyjnych – lub możliwych czynników zewnętrznych :) – chcemy sami dowiedzieć (w sposób najbardziej praktyczny, czyli poprzez implementację), jak to wszystko działa. Wówczas do szczęścia potrzebujemy właściwie tylko jednej operacji: wyświetlenia pojedynczego piksela w określonym kolorze. I jest tylko jedno ale: aby to wszystko działało w jakikolwiek sensowny sposób, operacja ta musi być diabelnie szybka.
Niestety (a może właśnie ‘stety’) czasy, w których łatwo możemy pisać bezpośrednio po pamięci ekranu, najwyraźniej już dawno się skończyły. Nowoczesny system operacyjny nie pozwala po prostu na takie niskopoziomowe operacje żadnym programom użytkownika. Z drugiej strony korzystanie z tego, co w zakresie wyświetlania grafiki ów system oferuje, może być niewystarczająco – pod względem wydajnościowym, rzecz jasna.
Dlatego wydaje się, że do eksperymentów dobrze nadają istniejące biblioteki graficzne – tyle że ograniczone do jednej funkcjonalności: renderowania sprite’ów punktowych (point sprites). Polega ona na rysowaniu quadów (kwadratów złożonych z dwóch trójkątów) w taki sposób, że zawsze są one zwrócone przodem do kamery. Biorąc pod uwagę to, że wielkość takich punktów możemy określić, ustawienie jej na równą jednemu pikselowi sprawi, że wyświetlanie sprite’ów punktowych będzie dobrym substytutem “stawiania” pojedyncznych pikseli.
Gdzie w zakamarkach bibliotek graficznych ukryta jest tego rodzaju funkcjonalność? Otóż:
D3DRS_POINTSCALEENABLE
na FALSE
(co zresztą jest domyślną wartością) oraz D3DRS_POINTSIZE
na 1.0f
, czyli wielkość odpowiadającą jednemu pikselowi. Co ciekawe, trzeci o nazwie D3RS_POINTSPRITEENABLE
wbrew pozorom lepiej jest zostawić ustawiony domyślnie na FALSE
. Nie dotyczy on bowiem (co jest dość mylące) samej możliwości rysowania sprite’ów punktowych, a jedynie sposobu mapowania współrzędnych tekstur – co w przypadku symulowania pikseli nie jest nam potrzebne.D3DFVF_XYZRHW
oraz D3DFVF_DIFFUSE
przy pomocy dowolnych wywołań DrawPrimitive[UP]
z typem prymitywów D3DPT_POINTLIST
.1.0f
). Jeśli zaś chodzi o samo rysowanie punktów to jednym ze sposobów (i pewnie nie najlepszym :)) jest ustawienie wszystkich macierzy na jednostkowe i podawanie współrzędnych punktów jako wektorów 4D z ustaloną współrzędną Z (np. 0) oraz W równą 1, do funkcji glVertex4*
. Musi być ona umieszczona oczywiście między glBegin
(z parametrem GL_POINTS
) a glEnd
, zaś kolor punktu-piksela określić możemy przez glColor*
.Jak widać, mechanizmy wspomagające nas w ręcznej, pikselowej robocie są nieco ukryte. Co więcej, w DirectX 10 na przykład sprite’y punktowe zostaną wyeliminowane z biblioteki i konieczne będzie samodzielne tworzenie odpowiednich quadów, jeśli zechcemy uzyskać podobną funkcjonalność. To prawdopodobnie słuszna decyzja, bowiem nawet w swoim głównym zastosowaniu – czyli systemach cząsteczkowych – są one zbyt ograniczone (choćby dlatego, że są cały czas zwrócone do kamery, co wyklucza możliwość obrotu cząstek). A przyznajmy, że samodzielne stawianie “pikseli” jest dość egzotyczne – co nie znaczy, że nie warto wiedzieć, jak to się robi, jeśli kiedykolwiek będzie to nam potrzebne…
Ech, szkoda słów :P
Po pierwsze, jeśli już rysować pojedyncze piksele jako geometrię, to D3DPT_POINTLIST do tego służy.
Po drugie, jeśli już rysować pojedyncze piksele, to nie jako geometrię, tylko zablokować BackBuffer i masz dostęp do jego pamięci. Szybki. Tylko nie zapomnij o D3DPRESENTFLAG_LOCKABLE_BACKBUFFER. Zobacz: http://www.dimmension3.spine.pl/modules.php?name=Tutorials&wtd=show_tutorial&nr=49
Albo jeszcze lepiej, stwórz dynamiczną teksturę, blokuj ją, wypełniaj jej pamięć i renderuj ją na ekranie za pomocą FullScreen Quad.
A właśnie że nie szkoda, bo jakoś nie zaprzeczyłeś niczemu, co napisałem ;P
Co do blokowania bufora tylnego albo użycia dynamicznej tekstury – wszystko OK, tylko że jest to trochę bardziej skomplikowane. Przede wszystkim trzeba się przez chwilę pobawić ze wskaźnikami i nie zapomnieć o różnicy między Width a Pitch.