1 DirectX ª% Env. Cube Skoro wiemy już wszystko o mapowaniu szeÅ›ciennym od strony teoretycznej czas przystÄ…pić do zastosowania naszych wiadomoÅ›ci w praktyce. Napiszemy sobie dzisiaj prosty w miarÄ™ przykÅ‚adzik, który ostatecznie udowodni nasze teoretyczne rozważania a co ważniejsze nauczy nas tworzenia nowego, fascynujÄ…cego efektu. Nie ma na co czekać wiÄ™c od razu przystÄ™pujemy do analizy ważniejszych części kodu. // vertex shader declarator for cube enviroment mapping DWORD dwDecl[] = { D3DVSD_STREAM (0 ), D3DVSD_REG( D3DVSDE_POSITION, D3DVSDT_FLOAT3 ), D3DVSD_REG( D3DVSDE_NORMAL, D3DVSDT_FLOAT3 ), D3DVSD_REG( D3DVSDE_DIFFUSE, D3DVSDT_D3DCOLOR ), D3DVSD_REG( D3DVSDE_TEXCOORD0, D3DVSDT_FLOAT2 ), D3DVSD_REG( D3DVSDE_TEXCOORD1, D3DVSDT_FLOAT2 ), D3DVSD_END(), }; // our custom vertex format... struct CUSTOMVERTEX { FLOAT x, y, z; // untransformed, 3D position for the vertex FLOAT nx, ny, nz; // vertex normal DWORD color; // vertex color FLOAT tx; // first texture map coord FLOAT ty; // first texture map coord FLOAT tx1; // second texture map coord FLOAT ty1; // second texture map coord }; // ...and apropriate vertex type #define D3DFVF_CUSTOMVERTEX ( D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX2 ) Tak w zasadzie to już nie muszÄ™ wam tego tÅ‚umaczyć, prawda? Na samym poczÄ…tku definiujemy deklarator wejÅ›ciowy naszego vertex shadera, który posÅ‚uży nam do policzenia współrzÄ™dnych mapowania dla tekstury szeÅ›ciennej nakÅ‚adanej na obiekt. Podobnie jak w przypadku przykÅ‚adu z mapowaniem sferycznym tutaj nasz obiekt także bÄ™dzie posiadaÅ‚ jakÄ…Å› wÅ‚asnÄ… teksturÄ™, reprezentujÄ…cÄ… jego powierzchniÄ™, zaÅ› druga tekstura - szeÅ›cienna bÄ™dzie reprezentować odblaski. Dlatego oczywiÅ›cie taki a nie inny zestaw danych w strukturze wierzchoÅ‚ków - współrzÄ™dne, normalne (niezbÄ™dne tym razem), kolor wierzchoÅ‚ków oraz dwa zestawy współrzÄ™dnych teksturowania. Co do czego mam nadziejÄ™, że potraficie wyrecytować obudzeni o północy po ciężkim dniu kopania rowu ;-). Inicjalizacji i Å‚adowania danych dla obiektów na scenie z plików też nie bardzo mi siÄ™ chce omawiać (zapraszam do odpowiedniej lekcji), a zwrócÄ™ uwagÄ™ tylko na jednÄ… rzecz, która może kogoÅ› zastanowić, choć tak naprawdÄ™ już o niej wspominaliÅ›my w przykÅ‚adzie mapowania sferycznego. pTorusMesh->CloneMeshFVF( 0L, D3DFVF_CUSTOMVERTEX, pD3DDevice, &pTorusMeshClone ); pTorusMesh = pTorusMeshClone; pTorusMesh->GetVertexBuffer( &pTorusVB ); pTorusMesh->GetIndexBuffer( &pTorusIB ) Na naszej przykÅ‚adowej scenie jeden z obiektów bÄ™dzie odbijaÅ‚ caÅ‚e otoczenie, reszta natomiast bÄ™dzie siÄ™ w nim odbijać. Te odbijane obiekty wystarczy, że bÄ™dziemy rysować za pomocÄ… prostej metody obiektu wczytanego z pliku *.x - DrawSubset(). Natomiast obiekt, który nas najbardziej interesuje musi oczywiÅ›cie posiadać swój wÅ‚asny typ wierzchoÅ‚ków, którego nie jesteÅ›my w stanie zaÅ‚adować z pliku, bÄ™dzie to oczywiÅ›cie ten, który zdefiniowaliÅ›my sobie na poczÄ…tku. Sposób powinniÅ›my także już znać z poprzedniego przykÅ‚adu, ale dla pewnoÅ›ci szybkie przypomnienie. Obiekt typu LPD3DXMESH posiada metodÄ™ CloneMeshFVF() która tworzy a w zasadzie kopiuje do nowego obiektu takiego samego typu wszystkie dane wierzchoÅ‚ków z oryginalnego obiektu zachowujÄ…c jednak przy tym narzucony przez nas format wierzchoÅ‚ków. O dane, których nie ma w oryginalnym obiekcie oczywiÅ›cie musimy zatroszczyć siÄ™ już sami (jakoÅ› je wyliczyć, w zależnoÅ›ci od tego, co nam bÄ™dzie potrzebne). Po wywoÅ‚aniu tej metody robimy prostÄ… zamianÄ™ obiektów, tak, że nasz oryginalny pokazuje teraz ten podmieniony, żeby nam siÄ™ lepiej po prostu pracowaÅ‚o i nie byÅ‚o zbyt dużego zamieszania ze zmiennymi w programie. Ponieważ majÄ…c wÅ‚asny format wierzchoÅ‚ków nie bÄ™dziemy korzystać z metody DrawSubset() wiÄ™c koniecznym stanie siÄ™ również pozyskanie bufora wierzchoÅ‚ków i bufora indeksów do wierzchoÅ‚ków ponieważ w takiej to wÅ‚aÅ›nie formie sÄ… przechowywane wierzchoÅ‚ki i Å›ciany w pliku *.x. 2 DirectX ª% Env. Cube pD3DDevice->SetVertexShaderConstant( 0, D3DXMatrixTranspose( &matWorld, &matWorld ), 4 ); pD3DDevice->SetVertexShaderConstant( 4, D3DXMatrixTranspose( &matViewProj, &matViewProj ), 4 ); pD3DDevice->SetVertexShaderConstant( 8, &vectPos, 1 ); pD3DDevice->SetVertexShaderConstant( 9, &vect, 1 ); MajÄ…c już zaÅ‚adowane odpowiednie modele na scenÄ™ i tekstury, skopiowane i ustawione odpowiednie wÅ‚aÅ›ciwoÅ›ci wierzchoÅ‚ków możemy przystÄ…pić do ustawienia macierzy, które bÄ™dÄ… nam pomocne przy renderingu sceny - a wiÄ™c macierzy widoku, projekcji i Å›wiata. Nie bÄ™dzie we fragmencie programu za to odpowiedzialnemu wielkiej filozofii i raczej wszystko powinniÅ›my wiedzieć. Ustalmy sobie ino, jaka macierz bÄ™dzie siÄ™ znajdować w jakich rejestrach pamiÄ™ci staÅ‚ej. Jak widać na powyższym kawaÅ‚eczku kodu: " macierz Å›wiata - rejestry c0 - c3 " poÅ‚Ä…czone macierze widoku i projekcji - rejestry c4 - c7 " wektor reprezentujÄ…cy poÅ‚ożenie naszej kamery w Å›wiecie - c8 " zestaw kilku staÅ‚ych, które nam mogÄ… siÄ™ przydać w shaderze - c9 Tak uzbrojeni w dane w zasadzie możemy przystÄ…pić do zagÅ‚Ä™bienia siÄ™ w sam shader. Jak nadmieniÅ‚em w części bardziej teoretycznej shader posÅ‚uży nam do dwóch rzeczy - po pierwsze tradycyjnego już przeliczenia punktów przez macierze w celu otrzymania pożądanego przez nas widoku sceny a po drugie obliczenia współrzÄ™dnych mapowania szeÅ›ciennej tekstury reprezentujÄ…cej nasze zrenderowane Å›rodowisko. NapisaÅ‚em również w części teoretycznej o sposobie obliczania takich współrzÄ™dnych. W przeciwieÅ„stwie do shadera zastosowanego w przypadku mapowania sferycznego ten nasz dzisiejszy w cudowny sposób zamiast siÄ™ skomplikować to siÄ™ uproÅ›ci a da nam w zamian o wiele lepsze efekty niż ten poprzedni :-). Jak wiemy, wystarczy dla potrzeb naszej mapy szeÅ›ciennej, aby poprawnie jÄ… zmapować na obiekt policzyć tylko współrzÄ™dne wektora odbitego (refleksu) ze znanego nam już zapewne doskonale wzoru: Nie trzeba potem już w żadne specjalny sposób już przeksztaÅ‚cać i wykorzystywać jego współrzÄ™dnych, poza normalizacjÄ… wektora, bo jak Å‚atwo siÄ™ domyÅ›leć jego poszczególne współrzÄ™dne nie mogÄ… posiadać wartoÅ›ci wiÄ™kszej niż 1, bo inaczej nasza tekstura nam by siÄ™ nie uÅ‚ożyÅ‚a odpowiednio na bryle. No ale wszystko wyjdzie w praniu - zatem przyjrzyjmy siÄ™ bliżej temu czemuÅ›, co ma nam zaradzić na wszystkie nasze bolÄ…czki: ; Transform position and normal into camera-space m4x4 r0, v0, c0 ; vertex in the world space m3x3 r1, v3, c0 ; normal in the world space Jak zawsze na sam poczÄ…tek wszystkie potrzebne transformacje. WspółrzÄ™dne wierzchoÅ‚ka (przypominam nieustannie, że standardowo w v0) mnożymy przez zestaw wektorów zawartych w rejestrach c0-c4 (macierz Å›wiata) i wynik umieszczamy w rejestrze tymczasowym r0. W tym momencie mamy już przeksztaÅ‚cony wierzchoÅ‚ek przez macierz Å›wiata. To samo dotyczy współrzÄ™dnej odpowiedzialnej za normalnÄ… wierzchoÅ‚ka - ponieważ w znacznym stopniu obliczanie mapowania bazuje na normalnej, wiÄ™c i jÄ… musimy przenieść do Å›wiata w którym operujemy, żeby same wierzchoÅ‚ki nie czuÅ‚y siÄ™ osamotnione - normalna po przeksztaÅ‚ceniu (w v3 na wejÅ›ciu) powÄ™druje do rejestru r1. ; Compute normalized view vector add r2, r0,-c8 ; from eye to world vertex rsq r2.w, r2.w ; normalize vertex mul r2, r2, r2.w ; normalized eye vector NastÄ™pnie do obliczenia wektora refleksu potrzebować bÄ™dziemy wektora Å‚Ä…czÄ…cego oko kamery z wierzchoÅ‚kiem we współrzÄ™dnych Å›wiata. WspółrzÄ™dne wierzchoÅ‚ka przed momentem obliczyliÅ›my i mamy je w rejestrze r0, natomiast jak wspominaliÅ›my wczeÅ›niej poÅ‚ożenie naszej kamery jest przekazane do vertex shadera poprzez rejestr c8. Aby policzyć wektor patrzenia wystarczy po prostu odjąć od poÅ‚ożenia wierzchoÅ‚ka w Å›wiecie - r0 poÅ‚ożenie kamery - c8. Wynik w naszym programie umieÅ›cimy sobie dla przykÅ‚adu w rejestrze tymczasowym r2. Aby wszystko przebiegaÅ‚o zgodnie z naszymi oczekiwaniami należy jeszcze do tego wszystkiego obliczony przed chwilÄ… wektor znormalizować. A jak to robić za pomocÄ… asemblera shadera przecież także doskonale już wiemy! Poznany przed tygodniem sposób szybkiej normalizacji stosować bÄ™dziemy w zasadzie bez przerwy, krótko wiÄ™c przypomnijmy. Rozkazem rsq obliczamy odwrotny pierwiastek z tego co znajduje siÄ™ pod pierwiastkiem, a tam mamy nasz wektor pomnożony przez samego siebie, zgodnie z wszelkimi wzorami wyglÄ…da to tak: 3 DirectX ª% Env. Cube MajÄ…c tÄ™ wartość (dÅ‚ugość wektora w mianowniku) wystarczy podzielić nasz wektor przez tÄ™ dÅ‚ugość (pomnożyć przez wynik operacji rsq) i otrzymamy wektor znormalizowany. Mam nadziejÄ™, że to już wam siÄ™ utrwali i już nie bÄ™dziemy tego waÅ‚kować n-ty raz mogÄ…c poÅ›wiÄ™cać czas bardziej wyrafinowanym sztuczkom. Po znormalizowaniu wektora nadal przechowujemy jego wartość w rejestrze r2 (no bo po co mamy sobie w kodzie i w rejestrach baÅ‚aganić). ; renormalize normal dp3 r1.w, r1, r1 rsq r1.w, r1.w mul r1, r1, r1.w DokÅ‚adnie ten sam manewr co przed chwilÄ… robimy z naszÄ… przeliczonÄ… normalnÄ…. Najpierw mnożymy wektor przez siebie, potem pierwiastek z tego w mianowniku i mnożenie przez wektor - no ale przecież miaÅ‚em siÄ™ nie powtarzać. Uczulam tylko na operacje na wektorach, które akurat w tym przypadku sÄ… przeprowadzane na wektorach znormalizowanych - w ten sposób unikamy niedokÅ‚adnoÅ›ci i zakłóceÅ„. OczywiÅ›cie nie wszÄ™dzie tak bÄ™dzie, ale akurat w tym przypadku nie sÄ… ważne dÅ‚ugoÅ›ci wektorów a raczej ich kierunki a z tymi lepiej mieć do czynienia jako ze znormalizowanymi. ; calculate reflection vector dp3 r4, r2, r1 add r4, r4, r4 mul r1, r1, r4 add oT0, r2,-r1 mov oT0.w, c9.z MajÄ…c przygotowane odpowiednie wektory - patrzenia i normalny, oba znormalizowane - o dÅ‚ugoÅ›ci jeden i majÄ…c gotowy przepis na wektor refleksu możemy przystÄ…pić do jego obliczenia. Nie pozostaje nam nic innego, jak tylko po kolei wykonywać operacje potrzebne do jego obliczenia. W shaderze do mapowania sferycznego już też to robiliÅ›my wiÄ™c teraz tylko skrócony opis: dp3 r4, r2, r1 Najpierw potrzebujemy iloczynu skalarnego wyżej wymienionych wektorów, które przypomnijmy mamy w rejestrach r1 (normalna) i r2 (patrzenia). Wynik operacji iloczynu umieszczamy w rejestrze r4. add r4, r4, r4 NastÄ™pnie musimy tÄ™ wartość pomnożyć przez dwa, wiÄ™c aby byÅ‚o szybciej dodamy sobie po prostu ten sam wektor do siebie, co da nam dokÅ‚adnie ten sam efekt a bÄ™dzie szybciej niż mnożenie. mul r1, r1, r4 MajÄ…c wartość 2*(V*N) mnożymy przez niÄ… wektor normalny zgodnie z ogólnym wzorem. Ponieważ wektor normalny nie bÄ™dzie nam już potrzebny w swojej oryginalnej postaci, wiÄ™c zastÄ…pi sobie jego wartoÅ›ci w rejestrze r1. add oT0, r2,-r1 KoÅ„czÄ…c, za jednym zamachem wykonujemy dwie operacje. Po pierwsze do wektora patrzenia odejmujemy wartość 2*(V*N)*N otrzymujÄ…c tym samym szukanÄ… wartość wektora refleksu. Po drugie, jak wiemy z lekcji teoretycznej, współrzÄ™dne x i y wektora refleksu stanowiÄ… dla nas w tym przypadku po prostu współrzÄ™dne mapowania dla tekstury szeÅ›ciennej! Dlatego też wynikowym rejestrem operacji dodawania bÄ™dzie oT0, czyli rejestr reprezentujÄ…cy współrzÄ™dne mapowania tekstury dla pierwszego poziomu tekstur na obiekcie (drugim bÄ™dzie poziom zawierajÄ…cy wÅ‚aÅ›ciwe tekstury obiektu). ; Project position m4x4 oPos, r0, c4 mov oT1, v7 4 DirectX ª% Env. Cube mov oD0, v5 No i to w zasadzie byÅ‚by koniec - wektor refleksu i współrzÄ™dne mapowania obliczone, wiÄ™c teraz tylko pozostaje dopeÅ‚nić formalność. Aby zobaczyć we wÅ‚aÅ›ciwej perspektywie naszÄ… bryÅ‚Ä™ należy pomnożyć jej wierzchoÅ‚ki przez macierz widoku i projekcji - u nas jednÄ…, powstaÅ‚Ä… z poÅ‚Ä…czenia powyższych i umieszczonÄ… w rejestrach od c4 do c7. Jako wynik operacji otrzymamy wyjÅ›ciowe wartość z shadera, które bezpoÅ›rednio umieszczamy w rejestrze odpowiedzialny za pozycjÄ™ wierzchoÅ‚ka oPos. mov oT1, v7 Drugi zestaw współrzÄ™dnych tekstur pozostanie bez zmian, wiÄ™c nie pozostaje nam nic innego jak tylko przepisanie go z wejÅ›cia - v7 na wyjÅ›cie, czyli do rejestru za to odpowiedzialnego oT1. Musi to być zrobione ponieważ nasza aplikacja bÄ™dzie oczekiwać od shadera w tym rejestrze danych a jeÅ›li ich nie dostarczymy to bÄ™dzie jedna wielka kiszka. mov oD0, v5 No i to samo bÄ™dzie oczywiÅ›cie w przypadku koloru diffuse wierzchoÅ‚ków. Wbijmy sobie tutaj niejako przy okazji do gÅ‚owy raz na zawsze, że wszystkie rejestry wyjÅ›ciowe shadera, których wykorzystanie zadeklarujemy definiujÄ…c nasz wÅ‚asny format wierzchoÅ‚ków muszÄ… zostać jawnie wypeÅ‚nione w kodzie shadera - oszczÄ™dzi nam to sporo frustracji z dochodzeniem, co jest nie tak i nauczy naprawdÄ™ dobrych nawyków. A wracajÄ…c do sprawy to... już koniec naszego shaderka. Jak widać jest naprawdÄ™ banalnie prosty a jak spojrzeć z perspektywy czasu na moje pierwsze zmagania z shaderami i próby ich zrozumienia to aż Å›miech bierze i wstyd jak można byÅ‚o myÅ›leć, że to jakiÅ› hardcore, którego nie da siÄ™ zrozumieć ;-). A teraz to niemal każdy, jaki tego sobie zażyczymy napiszemy naprawdÄ™ bez żadnego trudu! Dobra, koniec samouwielbienia, czas wracać do ciężkiej pracy bo to jeszcze nie koniec, a w zasadzie jesteÅ›my prawie na samym poczÄ…tku. Pomimo iż paraliÅ›my siÄ™ z vertex shaderem, którego sama nazwa może przerażać to jednak dzisiaj byÅ‚ on jednÄ… z Å‚atwiejszych części aplikacji - teraz czas na nowe, bardziej wciÄ…gajÄ…ce rzeczy, czyli o tym jak wyrenderować Å›rodowisko, stworzyć mapÄ™ szeÅ›ciennÄ… i otrzymać to co chcemy - refleksy na obiekcie. Obiekty Å‚Ä…cznie z teksturami załóżmy, że mamy już na scenie zaÅ‚adowane, scena ustawiona, wszystko siÄ™ obraca jak należy. Co zrobić, żeby siÄ™ to wszystko odbijaÅ‚o? Ano idea jest prosta - Aby w naszym obiekcie odbijaÅ‚o siÄ™ wszystko musimy wyrenderować na mapÄ™ Å›rodowiska wszystko co go otacza prócz jego samego. Funkcja renderujÄ…ca wiÄ™c trochÄ™ nam siÄ™ skomplikuje, bo bÄ™dzie trochÄ™ kombinacji - no ale zobaczmy. Przyjrzyjmy siÄ™ wiÄ™c trochÄ™ dokÅ‚adniej naszej funkcji renderujÄ…cej, w której znajdziemy... zadziwiajÄ…co maÅ‚o (czyżby ot byÅ‚o aż tak proste???;) void Render() { RenderScene( TRUE ); pD3DDevice->Present( NULL, NULL, NULL, NULL ); } Mamy tu tylko dwie funkcje, z których jednÄ… znamy przecież doskonale - metoda Present() naszego urzÄ…dzenia renderujÄ…cego powoduje przerzucenie tego, co mamy w buforach na ekran, czyli w rzeczywistoÅ›ci tak naprawdÄ™ tutaj wszystko rysujemy. Druga funkcja to nasz tajemniczy wymysÅ‚, o którym wÅ‚aÅ›nie teraz dowiemy siÄ™ wszystkiego i caÅ‚Ä… tajemnica powinna nam siÄ™ wyjaÅ›nić. Przypatrzmy siÄ™ wiÄ™c naszej tajemniczej funkcji: void RenderScene( BOOL bRender ) { ... pD3DDevice->BeginScene(); { ... // draw sky box ... // draw rotating torus ... if( bRender ) // if not need don't render torus { RenderToCube(); // render teapot } ... } pD3DDevice->EndScene(); 5 DirectX ª% Env. Cube } Wiem, nie jest dokÅ‚adnie to, co w przykÅ‚adzie i jak sÅ‚usznie zauważyliÅ›cie jest to pewnego rodzaju pseudo-kod. Ponieważ prawdziwy kod naszej funkcji zajmuje mnóstwo linii i tylko by nam tutaj zaciemniÅ‚, wiÄ™c my sobie jÄ… przedstawimy w sposób taki, który przedstawi na idee naszej dziaÅ‚alnoÅ›ci i cele a nie samo dziaÅ‚anie na konkretnych obiektach (to jest banalne i każdy z nas potrafi to doskonale). Jak widać na poczÄ…tku po wywoÅ‚aniach metod urzÄ…dzenia renderujÄ…cego jest to wÅ‚aÅ›ciwy kod naszej funkcji do rysowania prymitywów, wiÄ™c spodziewamy siÄ™ tutaj mnóstwa rożnych ustawieÅ„ dla urzÄ…dzenia i konkretnych obiektów. I tak w zasadzie bÄ™dzie, choć dadzÄ… siÄ™ też zauważyć pewne różnice. Po pierwsze nasza funkcja rysujÄ…ca po raz pierwszy przyjmuje jakiÅ› parametr typu BOOL (prawda, lub faÅ‚sz). Po co nam on bÄ™dzie potrzebny okaże siÄ™ już za moment a my może skupmy siÄ™ na tym, co siÄ™ dzieje na samym poczÄ…tku. Scena stoi, wszystko przygotowane, czas wiÄ™c ruszyć wszystko z miejsca. Nie czekajÄ…c wiÄ™c na nic mamy od razu na poczÄ…tku wyczyszczenie wszystkich buforów, (metoda Clear() urzÄ…dzenia), nastÄ™pnie wywoÅ‚anie pary BeginScene() i EndScene(). PomiÄ™dzy nimi robimy to co zwykle, czyli ustawiamy dla naszych modeli odpowiednie macierze przeksztaÅ‚ceÅ„, jeÅ›li sÄ… im potrzebne no i rysujemy poszczególne obiekty na scenie. I tak nam wszystko siÄ™ piÄ™knie odbywa do pewnego momentu. Otóż w pewnym momencie nasz program nagle spotyka warunek: if( bRender ) // if not need don't render torus { RenderToCube(); // render teapot } Ponieważ za pierwszym wywoÅ‚aniem naszej funkcji jak prosto to sprawdzić jej parametr bRender, jego wartość wynosi TRUE, wiÄ™c program posÅ‚usznie przystÄ…pi do wykonania warunku, czyli wywoÅ‚a kolejna funkcjÄ™ o jeszcze bardziej fascynujÄ…cej nazwie czyli RenderToCube(). W tym momencie musimy sobie przerwać analizÄ™ naszej dotychczasowej funkcji RenderScene() trzeba wskoczyć do nastÄ™pnej, żeby siÄ™ dowiedzieć, co w ogóle jest tutaj grane. Zobaczmy wiÄ™c: void RenderToCube() { // render scene to cube map LPDIRECT3DSURFACE8 pBackBuffer; LPDIRECT3DSURFACE8 pZBuffer; D3DXMATRIX matProjSave; D3DXMATRIX matViewSave; pD3DDevice->GetTransform( D3DTS_VIEW, &matViewSave ); pD3DDevice->GetTransform( D3DTS_PROJECTION, &matProjSave ); D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/2, 1.0f, 1.0f, 1500.0f ); pD3DDevice->SetTransform( D3DTS_PROJECTION, &matProj ); D3DXMATRIX matViewDir; pD3DDevice->GetTransform( D3DTS_VIEW, &matViewDir ); matViewDir._41 = 0.0f; matViewDir._42 = 0.0f; matViewDir._43 = 0.0f; pD3DDevice->GetRenderTarget( &pBackBuffer ); pD3DDevice->GetDepthStencilSurface( &pZBuffer ); for( int i = 0; i < 6; i++ ) { D3DXMATRIX matView; matView = GetCubeMapViewMatrix( (D3DCUBEMAP_FACES) i ); D3DXMatrixMultiply( &matView, &matViewDir, &matView ); pD3DDevice->SetTransform( D3DTS_VIEW, &matView ); pCubeMap->GetCubeMapSurface( (D3DCUBEMAP_FACES) i, 0, &pCubeSurface ); pD3DDevice->SetRenderTarget( pCubeSurface, pZBufferSurface ); DXRELEASE( pCubeSurface ); // Render the scene (except for the teapot) RenderScene( FALSE ); } pD3DDevice->SetRenderTarget( pBackBuffer, pZBuffer ); DXRELEASE( pBackBuffer ); 6 DirectX ª% Env. Cube DXRELEASE( pZBuffer ); pD3DDevice->SetTransform( D3DTS_VIEW, &matViewSave ); pD3DDevice->SetTransform( D3DTS_PROJECTION, &matProjSave ); } Na pierwszy rzut oka wyglÄ…da to dosyć groznie, no ale nie z takimi już żeÅ›my sobie radzili, dlatego bÄ…dzmy dobrej myÅ›li ;). Jak sama nazwa wskazuje i wszelkie znaki na niebie i ziemi ta funkcja posÅ‚uży nam do wyrenderowania mapy Å›rodowiska, która potem naÅ‚ożymy na obiekt - a jak? Już analizujemy. LPDIRECT3DSURFACE8 pBackBuffer; LPDIRECT3DSURFACE8 pZBuffer; D3DXMATRIX matProjSave; D3DXMATRIX matViewSave; Ponieważ trochÄ™ bÄ™dziemy kombinować na rożnych macierzach i buforach, wiÄ™c dla bezpieczeÅ„stwa i żeby niczego po drodze sobie nie zgubić bÄ™dziemy potrzebować kilku obiektów, w których bÄ™dziemy mogli przechować jakieÅ› nasze tymczasowe wartoÅ›ci. Jak siÄ™ okazaÅ‚o po analizie przykÅ‚adów potrzebować bÄ™dziemy co nastÄ™puje - bufora z, bufora tylnego, do którego zwykle renderowana jest scena oraz macierzy projekcji i widoku. Podczas tworzenia sceny z widokiem mapy Å›rodowiska zmieniać siÄ™ nam bÄ™dÄ… głównie macierze, dlatego ich zawartość bÄ™dzie musiaÅ‚a zostać zachowana. Do tego potrzeba bÄ™dzie buforów, do których bÄ™dziemy renderować, żeby z nich pobierać gotowe obrazy. MajÄ…c gdzie skÅ‚adować dane, możemy iść dalej. pD3DDevice->GetTransform( D3DTS_VIEW, &matViewSave ); pD3DDevice->GetTransform( D3DTS_PROJECTION, &matProjSave ); No i od razu wykorzystamy sobie nasze zmienne. Nie zapominajmy w tym momencie skÄ…d przyszliÅ›my - byliÅ›my w jakimÅ› miejscu funkcji renderujÄ…cej, która miaÅ‚a w danym momencie ustawione konkretne macierze widoku, projekcji i Å›wiata w urzÄ…dzeniu. Ponieważ my zaraz te macierze zmienimy, dokonamy pewnych operacji i wyjdziemy z tej funkcji, wiÄ™c po powrocie do funkcji renderujÄ…cej macierze musza być takie same jak przed wejÅ›ciem tutaj. Zapisujemy wiÄ™c ich stan... Metoda GetTransform() robi dokÅ‚adnie odwrotnie niż doskonale nam znana metoda SetTransform(), choć parametry pobiera dokÅ‚adnie te same. Jako pierwszy parametr pobiera ona typ przeksztaÅ‚cenia jakie ma zostać zapisane, w drugim parametrze, jakim jest macierz reprezentujÄ…ca to przeksztaÅ‚cenie. Zapisujemy wiÄ™c aktualnie ustawione w urzÄ…dzeniu macierze widoku D3DTS_VIEW oraz projekcji - D3DTS_PROJECTION. D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/2, 1.0f, 1.0f, 1500.0f ); pD3DDevice->SetTransform( D3DTS_PROJECTION, &matProj ); Macierze zachowaliÅ›my, czujemy pewien komfort, czas wiÄ™c przystÄ…pić do dziaÅ‚aÅ„, żeby nie tracić darmo cennego przecież czasu - przystÄ…pmy do renderingu naszej mapy Å›rodowiska. Aby to zrobić bÄ™dziemy potrzebować dwóch rzeczy - odpowiednio musimy ustawić nasze macierze widoku i projekcji - trÄ…bimy przecież o tym już Å‚adnÄ… chwile. Na pierwszy ogieÅ„ pójdzie wiÄ™c macierz projekcji, bo z niÄ… jest Å‚atwiej. MapÄ™ Å›wiata generujemy dla obiektu wiemy w jaki sposób. Stajemy w samym jego Å›rodku, który jest wzglÄ™dny w stosunku do Å›wiata, w jakim obiekt siÄ™ znajduje i patrzÄ…c stamtÄ…d renderujemy nasz Å›wiat. Nie inaczej jest w tym przypadku. Ponieważ nasz Å›wiat jest dosyć duży i żeby można byÅ‚o zobaczyć wszystko co trzeba musimy odpowiednio dobrać kÄ…t i zasiÄ™g widzenia. W naszym przypadku sprawa jest dosyć prosta - nasz obiekt stoi na Å›rodku sceny i do każdej Å›ciany reprezentujÄ…cej Å›rodowisko mam taka sama odlegÅ‚ość. Wystarczy wiÄ™c raz ustawić macierz projekcji i bÄ™dziemy mieli spokój. OczywiÅ›cie jest ona ustawiana w taki sposób, żeby z miejsca pobytu obiektu widać byÅ‚o to, co ma siÄ™ w nim odbić (czyli nie koniecznie wszystko), choć tak jest akurat w naszym przypadku. Gdyby obiekt siÄ™ na przykÅ‚ad poruszaÅ‚ po scenie, pewnie w niektórych momentach należaÅ‚oby niektóre wartoÅ›ci, zwÅ‚aszcza zasiÄ™gu zweryfikować na bieżąco, no ale to już zależy od konkretnych przypadków. Dla potrzeb naszej nauki tyle zupeÅ‚nie nam wystarczy. D3DXMATRIX matViewDir; pD3DDevice->GetTransform( D3DTS_VIEW, &matViewDir ); matViewDir._41 = 0.0f; matViewDir._42 = 0.0f; matViewDir._43 = 0.0f; KogoÅ› mogÄ… naprawdÄ™ te linie bardzo zastanowić - po jaka znowu cholerÄ™ nam kolejna kopia naszej macierzy widoku? Przecież jedna już mamy i na razie z niej nie skorzystaliÅ›my a tu już z nastÄ™pna mieszamy? Otóż, do dziaÅ‚aÅ„ mapa Å›rodowiska bÄ™dziemy musieli modyfikować pewne wektory zwiÄ…zane z macierzÄ… widoku wÅ‚aÅ›nie, dlatego tez nie możemy sobie zmienić macierzy poprzednio zachowanej, ponieważ tamta bÄ™dziemy musieli przywrócić na samym koÅ„cu naszej tymczasowej funkcji aby po powrocie do funkcji renderujÄ…cej wszystko wróciÅ‚o do normy. A w tym akurat miejscu pobieramy jeszcze raz macierz widoku i zerujemy jej ostatni wiersz - ten, odpowiedzialny za przesuniecie kamery w Å›wiecie - po co? Już za moment wyjaÅ›nimy. pD3DDevice->GetRenderTarget( &pBackBuffer ); 7 DirectX ª% Env. Cube pD3DDevice->GetDepthStencilSurface( &pZBuffer ); Jeszcze zanim przystÄ…pi do kolejnych dziaÅ‚aÅ„ pobieramy sobie wskazniki do powierzchni, które stanowiÄ… bufory docelowe naszych operacji rysunkowych. Metoda GetRenderTarget() naszego urzÄ…dzenia powoduje pobranie wskaznika do powierzchni, na której tak naprawdÄ™ rysowane sÄ… nasze prymitywy przed przerzuceniem ich na powierzchnie przednia (ekran) podczas renderingu. To jest wÅ‚aÅ›nie ten nasz tylny bufor (tak naprawdÄ™ po prostu zwykÅ‚a powierzchnia), który w D3D jest nazywany bardzo Å‚adnie "celem renderingu". Drugie wywoÅ‚anie może siÄ™ nie skojarzyć na pierwszy rzut oka ze swoim przeznaczeniem. Metoda urzÄ…dzenia GetDepthStencilSurface() powoduje pobranie adresu bufora z naszego urzÄ…dzenia, dziÄ™ki któremu mamy bardzo uÅ‚atwione zadanie, jeÅ›li chodzi o usuwanie niewidocznych obiektów z naszych scen. Jak dziaÅ‚a bufor opisywaliÅ›my już kilkakrotnie, wiÄ™c jeÅ›li ktoÅ› nie pamiÄ™ta to niech wróci do poczÄ…tków i do lekcji bodajże o bryÅ‚ach. Te dwie zmienne spowodujÄ… to, że bÄ™dziemy mieć w rÄ™ce dostÄ™p do dwóch bardzo ważnych buforów, bez których nie zdoÅ‚amy stworzyć żadnej mapy Å›rodowiska. for( int i = 0; i < 6; i++ ) { D3DXMATRIX matView; matView = GetCubeMapViewMatrix( (D3DCUBEMAP_FACES) i ); D3DXMatrixMultiply( &matView, &matViewDir, &matView ); pD3DDevice->SetTransform( D3DTS_VIEW, &matView ); pCubeMap->GetCubeMapSurface( (D3DCUBEMAP_FACES) i, 0, &pCubeSurface ); pD3DDevice->SetRenderTarget( pCubeSurface, pZBufferSurface ); DXRELEASE( pCubeSurface ); // Render the scene (except for the teapot) pD3DDevice->BeginScene(); RenderScene( FALSE ); pD3DDevice->EndScene(); } No i można być powiedzieć, że mamy w tym momencie najważniejszÄ… pÄ™tlÄ™ w naszym programie (jeÅ›li nie liczyć pÄ™tli komunikatów aplikacji ;). Tutaj odbywa siÄ™ caÅ‚a rzecz, czyli tworzenie mapy Å›rodowiska. D3DXMATRIX matView; matView = GetCubeMapViewMatrix( (D3DCUBEMAP_FACES) i ); D3DXMatrixMultiply( &matView, &matViewDir, &matView ); pD3DDevice->SetTransform( D3DTS_VIEW, &matView ); KtoÅ› pewnie znowu przeklnie szpetnie na widok zmiennej reprezentujÄ…cej macierz i majÄ…cej w nazwie wyraz "View", ale spokojnie, tym razem nie bÄ™dziemy już pobierać kolejnej kopii chyba już znienawidzonej w tym przykÅ‚adzie, ale bardzo ważnej macierzy. Tym razem zmienna ta bÄ™dzie stanowiÅ‚a cel, w którym my taka macierz widoku (nowa!) sobie umieÅ›cimy. Zaraz potem mamy dosyć tajemnicza linie: matView = GetCubeMapViewMatrix( (D3DCUBEMAP_FACES) i ); GetCubeMapViewMatrix() to kolejna funkcja stworzona a wÅ‚aÅ›ciwie skopiowana z D3D SDK na potrzeby naszego przykÅ‚adu. W tym miejscu wypadaÅ‚oby znowu wskoczyć do nie, no ale my może pokażmy sobie tylko jej kawaÅ‚ek, żeby już caÅ‚kiem kodu sobie nie zaciemnić: D3DXMATRIX GetCubeMapViewMatrix( DWORD dwFace ) { D3DXVECTOR3 vEyePt = D3DXVECTOR3( 0.0f, 0.0f, 0.0f ); D3DXVECTOR3 vLookDir; D3DXVECTOR3 vUpDir; D3DXMATRIX matView; switch( dwFace ) { case D3DCUBEMAP_FACE_POSITIVE_X: vLookDir = D3DXVECTOR3( 1.0f, 0.0f, 0.0f ); vUpDir = D3DXVECTOR3( 0.0f, 1.0f, 0.0f ); break; case D3DCUBEMAP_FACE_NEGATIVE_X: vLookDir = D3DXVECTOR3(-1.0f, 0.0f, 0.0f ); vUpDir = D3DXVECTOR3( 0.0f, 1.0f, 0.0f ); 8 DirectX ª% Env. Cube break; ... } D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookDir, &vUpDir ); return matView; } I cóż tutaj takiego strasznego widać. Jak popatrzeć dokÅ‚adnie w jej kod, to wszystko siÄ™ wydaje dosyć banalne i takie tez jest w istocie. W zależnoÅ›ci od parametru, jaki funkcja otrzymuje wykonuje ona identyczny zestaw kroków, dla każdego przypadku ino z każdym przypadkiem dobiera inne wartoÅ›ci dla pewnych elementów. Głównym zadaniem tej funkcji jest stworzyć macierz widoku. Macierz taka ma za zadanie być najprostsza z możliwych - po prostu ma ustawić kamerÄ™ w sześć rożnych stron, taka aby objąć caÅ‚y nasz Å›wiat widokiem i żeby po ich ustawieniu można byÅ‚o wygenerować mapÄ™ Å›rodowiska. Aby stworzyć macierz widoku jak wiemy potrzebujemy trzech wektorów - poÅ‚ożenia oka, celu oraz wektora pokazujÄ…cego gore Å›wiata. W naszym przypadku upraszczamy sprawÄ™ maksymalnie jak tylko siÄ™ da. Ponieważ renderujemy z poÅ‚ożenia obiektu, wiÄ™c nasze oko zawsze bÄ™dzie w punkcie (0, 0, 0) - bo patrzmy z punktu widzenia obiektu. Cel dla każdej z szeÅ›ciu pÅ‚aszczyzn otaczajÄ…cych bÄ™dzie inny - ale z faktu, że kolejne Å›ciany sÄ… do siebie prostopadle wynika, że wektorami celu bÄ™dÄ… kolejne wersory (wektory jednostkowe) w lokalnym ukÅ‚adzie współrzÄ™dnych obiektu. Ponieważ kamera dla poszczególnych przypadków bÄ™dzie tez inaczej zorientowana trzeba tez dobrać odpowiednio zwrot wektora oznaczajÄ…cego gore Å›wiata dla kamery - na podstawie poÅ‚ożenia oka i celu możemy tego Å‚atwo dokonać. I jak widać po funkcji to wÅ‚aÅ›nie jest robione dla poszczególnych przypadków parametru dwFace, który pomimo tego, że przy wywoÅ‚aniu GetCubeMapViewMatrix() jest rzutowany na jakiÅ› tajemniczy typ i potem w kodzie jest również wykorzystywany nie jako liczba, tak naprawdÄ™ jest po prostu numer oznaczajÄ…cy kolejny element mapy Å›rodowiska. Po ustawieniu wszystkich potrzebnych wektorów nastÄ™puje wywoÅ‚anie niezastÄ…pionej jak dotychczas funkcji z biblioteki D3DX - D3DXMatrixLookAtLH(), czyli utworzenie macierzy widoku na podstawie okreÅ›lonych wczeÅ›niej wektorów. I widać w tym miejscu doskonale po co nam to wszystko - za pomocÄ… tej funkcji po prostu zmieniamy co chwila kierunek patrzenia kamery (sześć razy) no i zwracamy sobie tÄ… macierz. No a skoro taka macierz mamy, to możemy ja teraz wykorzystać. Wróćmy wiÄ™c do kawaÅ‚ku kodu, który omawialiÅ›my w przypadku funkcji RenderToCube(). D3DXMATRIX matView; matView = GetCubeMapViewMatrix( (D3DCUBEMAP_FACES) i ); D3DXMatrixMultiply( &matView, &matViewDir, &matView ); pD3DDevice->SetTransform( D3DTS_VIEW, &matView ); Po utworzeniu sobie naszej macierzy widoku dla konkretnego kierunku (okreÅ›lonego typem D3DCUBEMAP_FACES), na który jest rzutowany parametr funkcji do tworzenia macierzy widoku musimy zrobić jeszcze jedna rzecz. Ponieważ w naszym programie możemy patrzeć na obiekt z dowolnego w zasadzie punktu przestrzeni, wiÄ™c nie możemy sobie tak po prostu zrenderować teraz naszej mapy Å›rodowiska. GdybyÅ›my stali w którejÅ› z pÅ‚aszczyzn tworzÄ…cych globalny ukÅ‚ad Å›wiata to tak, ale jeÅ›li uniesiemy siÄ™ i bÄ™dziemy krążyć nad obiektem to nasza mapa renderowana na podstawie macierzy obliczonej przed momentem przestanie przedstawiać dokÅ‚adnie to, co nas otacza, tylko bÄ™dzie siÄ™ wydawać jakaÅ› przekrÄ™cona. Aby tego uniknąć mnożymy nasza Å›wieżą macierz przez macierz widoku, która zachowaliÅ›my poprzednio. PamiÄ™tajmy, że tamtej macierzy wyzerowaliÅ›my wektor odpowiedzialny za przesuniecie w Å›wiecie (nie możemy siÄ™ tutaj przesunąć, bo znowu w mapie wyjdÄ… cuda). Po przemnożeniu takich macierzy bÄ™dziemy mieć taka macierz widoku, która uwzglÄ™dni nam przy renderingu mapy Å›wiata miejsce naszego pobytu - odpowiednio wiÄ™c obróci kamerÄ™, żebyÅ›my widzieli w miarÄ™ realistycznie otoczenie. No i na koniec, majÄ…c już odpowiedniÄ… macierz widoku Å‚adujemy ja naszemu urzÄ…dzeniu. GdybyÅ›my teraz sobie wyrenderowali obraz na ekran to zobaczylibyÅ›my co? No wÅ‚aÅ›nie... nasz Å›wiat, na który patrzylibyÅ›my z punktu widzenia naszego obiektu, który bÄ™dzie odbijaÅ‚ wszystko! No ale my tego nie zrobimy, bo jest nam to do czego innego potrzebne: pCubeMap->GetCubeMapSurface( (D3DCUBEMAP_FACES) i, 0, &pCubeSurface ); pD3DDevice->SetRenderTarget( pCubeSurface, pZBufferSurface ); DXRELEASE( pCubeSurface ); Tutaj po raz pierwszy odwoÅ‚amy siÄ™ do naszego nowego obiektu, jakim jest obiekt reprezentujÄ…cy mapÄ™ szeÅ›ciennÄ…. Jest to specjalny obiekt w D3D, który przechowuje dane dla takiej mapy, zarzÄ…dza nimi i zawiera metody, które pozwalajÄ… manipulować taka mapa. My użyjemy w naszym programie tylko jednej jego metody a mianowicie GetCubeMapSurface(). Zanim jednak przejdziemy do konkretów maÅ‚e sÅ‚owo wyjaÅ›nienia jak wyglÄ…da obiekt reprezentujÄ…cy teksturÄ™ szeÅ›ciennÄ…. Jak wiemy, musimy mieć sześć tekstur reprezentujÄ…cych wszystkie Å›ciany szeÅ›cianu otaczajÄ…cego nasz obiekt odbijajÄ…cy (ale masÅ‚o maÅ›lane). Tekstury w D3D sÄ… przechowywane jak każdy inny obraz na jakiejÅ› powierzchni. Obiekt tekstury szeÅ›ciennej zawiera po prostu sześć powierzchni, na których bÄ™dzie przechowywaÅ‚ każdÄ… z map reprezentujÄ…cych Å›rodowisko. WracajÄ…c do naszego kodu i metod obiektu tekstury szeÅ›ciennej. GetCubeMapSurface() sÅ‚uży nam do tego, aby moc pobrać 9 DirectX ª% Env. Cube od naszego obiektu adres powierzchni, która przechowuje obraz. Jako pierwszy parametr dostaje ona identyfikator powierzchni, której adres ma zostać zwrócony - ten identyfikator oznacza, przypominam jakiej to Å›ciany szeÅ›cianu ta tekstura dotyczy. Jako drugi parametr przyjmuje poziom mipmapy, dla jakiej chcemy uzyskać powierzchnie. Ponieważ my o mipmapach nie wiemy jeszcze za wiele i ich nie używamy, wiÄ™c ustawmy sobie tutaj na zero, bo używamy tylko jednego poziomu szczegółowoÅ›ci tekstur. Ostatni parametr dostanie od tej metody adres powierzchni dla konkretnej mapy. I wÅ‚aÅ›nie to tutaj zrobimy - pobierzemy sobie adres kolejnej mapy w obiekcie tekstury i... pD3DDevice->SetRenderTarget( pCubeSurface, pZBufferSurface ); I co? Ano patrzmy co siÄ™ dzieje. SetRenderTarget() to metoda naszego urzÄ…dzenia. Co ona robi? Ano dostaje ona dwa wskazniki, obydwa bardzo, bardzo ważne! Pierwszy z nich jak mówi dokumentacja stanowi wskaznik do bufora koloru - po naszemu mówiÄ…c wskaznik do tylnego bufora, który my potem metoda Present() przerzucimy na ekran. Tylko, że w tym momencie my mówimy urzÄ…dzeniu co stanowi dla niego nowy bufor koloru - a co nim jest? No wÅ‚aÅ›nie! Nowym tylnym buforem dla urzÄ…dzenia jest jedna z map istniejÄ…cych w obiekcie tekstury szeÅ›ciennej. WiÄ™c wszystko, co namalujemy teraz za pomocÄ… urzÄ…dzenia pójdzie... do mapy Å›rodowiska! Drugim argumentem jest adres nowego bufora z (jego powierzchni), który bÄ™dzie wykorzystany przy renderingu sceny. DziÄ™ki niemu uzyskamy poprawna kolejność i widoczność obiektów na scenie. DXRELEASE( pCubeSurface ); Ponieważ takie wywoÅ‚anie powoduje inkrementacje licznika odwoÅ‚aÅ„ do obiektu powierzchni reprezentujÄ…cej mapÄ™ Å›rodowiska, wiÄ™c od razu, żeby nie zapomnieć, zwolnijmy ja tutaj - robi to oczywiÅ›cie doskonale znana nam metoda z obiektów COM - Release(). RenderScene( FALSE ); No i tutaj już powinniÅ›my mieć zupeÅ‚nie dosyć. BrnÄ…c przez caÅ‚Ä… funkcjÄ™ renderujÄ…cÄ… dotarliÅ›my do funkcji tworzÄ…cej Å›rodowisko aby tutaj wrócić z powrotem do niej samej? Ano tak i trzeba przyznać uczciwie, że pachnie tutaj maÅ‚Ä… rekurencja - i tak jest w istocie. Tyko tym razem spójrzmy na parametr naszej funkcji renderujÄ…cej. Jest nim wartość FALSE a co to oznacza? Wróćmy na chwile do odpowiedniego kawaÅ‚ka naszego pseudo-kodu: if( bRender ) // if not need don't render torus { RenderToCube(); // render teapot } JeÅ›li parametr bRender miaÅ‚ wartość TRUE to funkcja renderujÄ…ca przystÄ™powaÅ‚a do tworzenia mapy Å›rodowiska. W tym przypadku tak nie jest wiÄ™c nie wykona nam siÄ™ ten fragment kodu. Funkcja to ominie i zakoÅ„czy swoje dziaÅ‚anie. W ten wÅ‚aÅ›nie sposób zostanie stworzony jeden obraz reprezentujÄ…cy kawaÅ‚ek naszego Å›wiata, ale widziany już z zupeÅ‚nie innego punktu. Zauważmy, że taka na pierwszy rzut oka skomplikowana procedura odbywa siÄ™ sześć razy - dla każdego przebiegu pÄ™tli jest zmieniana macierz widoku za pomocÄ… funkcji GetCubeMapViewMatrix() a nastÄ™pnie bazujÄ…c na tej macierzy jest wywoÅ‚ywana funkcja renderujÄ…ca, która rysuje odpowiedni kawaÅ‚ek Å›wiata, ale nie na ekranie tylko na kolejnych powierzchniach zawartych w obiekcie tekstury szeÅ›ciennej - prawda jakie to wszystko proste i Å‚atwe? ;). OczywiÅ›cie w takim przypadku funkcja renderujÄ…ca nie rysuje obiektu, dla którego tworzymy mapÄ™ - dba min. o to wÅ‚aÅ›nie jej parametr a także zapobiega jej rekurencyjnemu wywoÅ‚ywaniu w nieskoÅ„czoność, co szybko doprowadziÅ‚oby nas do rozstroju nerwowego a naszego kompa do... nie wiem czego - z D3D można siÄ™ spodziewać wszystkiego ;-). pD3DDevice->SetRenderTarget( pBackBuffer, pZBuffer ); DXRELEASE( pBackBuffer ); DXRELEASE( pZBuffer ); pD3DDevice->SetTransform( D3DTS_VIEW, &matViewSave ); pD3DDevice->SetTransform( D3DTS_PROJECTION, &matProjSave ); Po szeÅ›ciu przebiegach pÄ™tli w funkcji generujÄ…cej mapÄ™ szeÅ›ciennÄ… mamy wszystkie sześć powierzchni z tej mapy zapeÅ‚nione obrazami Å›wiata, na który patrzyliÅ›my w rożnych kierunkach z poÅ‚ożenia naszego obiektu. Teraz możemy już dać sobie spokój z tym szaleÅ„stwem i powrócić do normalnoÅ›ci. Każemy wiÄ™c urzÄ…dzeniu aby z powrotem ustawiÅ‚o sobie wÅ‚aÅ›ciwe bufory (tylny i z-buffor) oraz żeby ustawiÅ‚o sobie macierze, które pozwolÄ… nam popatrzeć na Å›wiat z jakiegoÅ› normalnego punktu widzenia. Jeszcze zwalniamy przy okazji wszystkie bufory, które wykorzystaliÅ›my podczas naszych szaleÅ„stw i możemy już wracać do naszej wÅ‚aÅ›ciwej funkcji renderujÄ…cej - przypomnijmy tylko, że w miÄ™dzyczasie wywoÅ‚aliÅ›my ja sześć razy! A przy wywoÅ‚aniu funkcji tworzÄ…cej mapÄ™ szeÅ›ciennÄ… byliÅ›my akurat tutaj: if( bRender ) // if not need don't render torus { RenderToCube(); 10 DirectX ª% Env. Cube // render teapot } ... pD3DDevice->EndScene(); Ponieważ dla przypadku kiedy parametr bRender byÅ‚ TRUE generowaliÅ›my mapÄ™ Å›rodowiska tak tez i w tym samym momencie możemy narysować nasz obiekt, który tÄ™ mapÄ™ zawiera - oczywiÅ›cie po jej stworzeniu! Ustawiamy wiÄ™c naszemu obiektowi tÄ™ mapÄ™ i rysujemy go na ekranie. Ponieważ macierze mamy przywrócone z przed wywoÅ‚ania funkcji RenderToCube(), wiÄ™c z czystym sumieniem możemy sobie teraz zakoÅ„czyć rysowanie (albo i nie) no i zaprezentować nam oszaÅ‚amiajÄ…ce efekty na ekranie ;). Ufff! Jazda byÅ‚a chyba niezÅ‚a, nie sadzicie? Ja sam w pewnym momencie myÅ›laÅ‚em, że siÄ™ w tym wszystkim pogubiÄ™, ale chyba siÄ™ udaÅ‚o oddać przynajmniej istotÄ™ dziaÅ‚ania, które sÅ‚uży do renderowania obiektów z odbiciami rzeczywistymi. Na koniec musze dodać jeszcze kilka uwag, które mogÄ… wam pomoc. " Po pierwsze - jak widać w przykÅ‚adzie nasze urzÄ…dzenie jest strasznie obciążone rysowaniem - caÅ‚y Å›wiat nieomal renderuje siedem razy w jednej klatce, wiÄ™c pamiÄ™tajcie - należy ograniczać ilość rysowanej geometrii w odbiciach (mapa nie musi być super dokÅ‚adna, bo na krzywiznach i tak siÄ™ wszystko psuje). " Po drugie - pamiÄ™tajcie, że jeÅ›li obiekt odbijajÄ…cy, dla którego liczymy mapÄ™ nie stoi w Å›rodku Å›wiata tylko gdzie indziej trzeba to uwzglÄ™dnić przy tworzeniu macierzy widoku. " Po trzecie - możecie sterować wielkoÅ›ciÄ… map Å›rodowiska przechowywanych w obiekcie tekstury szeÅ›ciennej - tym tez można trochÄ™ nadrobić na wydajnoÅ›ci. " Po czwarte - mam nadzieje, że zrozumieliÅ›cie wszystko i stworzycie nowe, fascynujÄ…ce efekty, jakich Å›wiat do tej pory nie widziaÅ‚. " Po piÄ…te - mam nadzieje, że siÄ™ nimi nie omieszkacie pochwalić na stronie! No i to byÅ‚oby wszystko w tej lekcji - na pewno byÅ‚a mÄ™czÄ…ca i skomplikowana, ale efekt naprawdÄ™ jest niezÅ‚y. PamiÄ™tajcie o jednym - nie każde, zwÅ‚aszcza starsze urzÄ…dzenia posiadajÄ… wspomaganie sprzÄ™towe dla tekstur szeÅ›ciennych - na takich nie uda siÄ™ odpalić przykÅ‚adu. Sama technikÄ™ znacie i wiecie wszystko, wiÄ™c możecie siÄ™ pokusić o programowe stworzenie takiego efektu - jeÅ›li komuÅ› siÄ™ chce i uda, niech siÄ™ pochwali