Mipmapy
Transkrypt
Mipmapy
Mipmapy W momencie, gdy oglądami pewien obiekt z nałożoną teksturą możemy zaobserwować wiele jego szczegółów. Jednak, gdy jest on daleko od nas, nie widzimy już go tak dokładnie. Gdyby wtedy był wyświetlany z mniejszą liczbą szczegółów zapewne byśmy tego nie zauważyli. Jak wiadomo tekstury bardziej szczegółowe, a przez to o większych rozmiarach zmuszają procesor do większej liczby obliczeń. Możemy uniknąć tego problemu stosując tak zwane mipmapy. Mipmapa jest powierzchnią złożoną. Zawiera ona w sobie inne powierzchnie, mniejsze, z mniejszymi obrazkami. Owe powierzchnie są nazywane mipmapami o określonym poziomie. Mipmapa najbardziej szczegółowa (czyli o najwyższych rozmiarach) ma poziom 0. Mipmapa mniejsz szczegółowa ma poziom 1 itd. Zatem większa liczba opisująca poziom oznacza mipmapę mniej szczegółową. Na rozmiar mipmap jest narzucona pewna zasada: każda kolejna mipmapa musi być dwa razy mniejsza od mipmapy o poziomie o jeden niższym. Dodatkowo rozmiary mipmap powinny być potęgami liczby 2, np.: Poziom 0 - 256x256 Poziom 1 - 128x128 Poziom 2 - 64x64 Poziom 3 - 32x32 Itd… Na poniższym rysunku przedstawiono prostokąt pokryty teksturą z mipmapami widziany w róznych odległościach: Dość trudno zauważyć zmnianę szegółowości tekstury (może wrzuciłem zbyt małe obrazki). Odpowiednia mipmapa jest wybierana utomatycznie. Mamy kilka jej wyboru. Należy w tym celu ustawić odpowiedni filtr za pomocą polecenia device.SetTextureStageState 0, D3DTSS_MIPFILTER, value As Long Jako wartość value przekazujemy jedną z możliwych opcji: D3DTFP_NONE - mipmaping jest wyłączony D3DTFP_POINT - wybierana jest mipmapa rozmiarach najbardziej zbliżonych do rozmiarów wielokąta D3DTFP_LINEAR - wybierane są dwie mipmapy o rozmiarach najbardziej zbliżonych do rozmiarów wielokąta, po czym dokonywana jest ich interpolacja i wynik jest nakładany na wielokąt. Poniżej pokazano wynik zastosowania tego filtru: Jak widać została dokonana interpolacja między mipmapą o poziomie 0 a 1. Jak łatwo można się domyśleć ostatnia opcja jest najbardziej pracochłonna dla procesora. Teskturę zawierającą mipmapy ustawia się tak samo jak zwykłą teksturę: Device.SetTexture 0, DDMipMap Tworzenie tekstury z mipmapami Napiszemy teraz procedurę tworzącą teksturę z mipmapami: Public Sub Create_Mipmaps(file As String, ByRef MipMap As DirectDrawSurface7, num As Byte, width As Single, height As Single) '…. '….. Edn Sub file - to fragment nazwy pliku. Będziemy używać podobnych do siebie nazw plików, np. brick0.bmp, brick1.bmp, brick2.bmp itd. To ułatwi nam wybór odpowiedniej bitmapy. MipMap - to tworzona tekstura z mipmapami. Num - liczba mipmap w tej teksturze Width, height - rozmiary mipmapy o poziomie 0 W funkcji zadeklarujemy kilka zmiennych: Dim MipMapDesc As DDSURFACEDESC2 'opis powierzchni zawierającej mipmapy, konieczny do jej utworzenia Dim Level As DirectDrawSurface7 ' mipmapa o określnym poziomie szczegółowości Dim NextLevelDesc As DDSCAPS2 'opis następnej mipmapy o danym poziomie Dim i As Integer 'do określenia poziomu mipmapy Przechodzimy teraz do utworzenia tekstury z mipmapami. Musimy wcześniej przygotować dla niej opis: With MipMapDesc 'Takie pola zostaną za chwilę opisane .lFlags = DDSD_CAPS Or DDSD_MIPMAPCOUNT Or DDSD_WIDTH Or DDSD_HEIGHT .lMipMapCount = num 'określamy ilość mipmap w teksturze 'Tworzymy złożoną powierzchnię (complex), bo tworzymy jeszcze w niej mipmpy 'Powierzchnia jest teksturą i jednocześnie mipmapą .ddsCaps.lCaps = DDSCAPS_TEXTURE Or DDSCAPS_MIPMAP Or DDSCAPS_COMPLEX .ddsCaps.lCaps2 = DDSCAPS2_D3DTEXTUREMANAGE .lWidth = width 'rozmiary pierwszej mipmapy o poziomie 0 .lHeight = height End With Flaga DDSCAPS2_D3DTEXTUREMANAGE - zarządzaniem teksturami zajmuje się Direct3D. Teraz już można stworzyć odpowiednią powierzchnię: Set MipMap = DD.CreateSurface(MipMapDesc) Ładujemy do niej obrazek. Funkcja PutImageInExistSurface ładuje obrazek bitmapy do istniejącej już powierzchni. (zostanie omówiona później). PutImageInExistSurface MipMap, file & Trim(Str(0)) & ".bmp" Wybraliśmy dla niej plik z numerem 0. Gdybyśmy jako file do funkcji przekazali: App.path & "\brick" to zostanie stworzona nazwa App.path & "\brick0.bmp". Utworzona powierzchnia zawiera w sobie mipmapy o wyższych poziomach i jest jednocześnie mipmapą o poziomie 0. Wiemy, że mipmapa o poziomie 0 zawiera mipmapę o poziomie 1. Z kolei mipmapa o poziomie 1 zawiera mipmapę o poziomie 2 itd… Jeśli chcemy uzyskać dostęp do jakiejś powierzchni, która jest zawarta w innej powiwrzchni, to należy użyć metody GetAttachedSurface. W ten sposób utworzymy mipmapę o poziomie 1. Wcześniej jednak ustawimy odpowiedni opis. Należy zaznaczyć że jest to tekstura - mipmapa: NextLevelDesc.lCaps = DDSCAPS_MIPMAP Or DDSCAPS_TEXTURE Set Level = MipMap.GetAttachedSurface(NextLevelDesc) Utworzona mipmapa level nie jest jakąś odrębną strukturą. Jest ona w rzeczywistości zawarta w mipmapie o poziomie niższym MipMap. Za pomocą obiektu level jedkan możemy w jakiś sposób dostać się do tej wewnętrznej powierzchni i zmodyfikować ją. Ładowanie dalszych obrazków i tworzenie kolejnych mipmap zrobimy w pętli: On Local Error Resume Next i=1 While Err.Number = DD_OK 'tworzymy kolejne mipmapy, "wyciągając" je z mipmap o poziomie niższym PutImageInExistSurface Level, file & Trim(Str(i)) & ".bmp" 'umieszczamy obrazek w ostatnio utworzonej mipmapie i=i+1 Set Level = Level.GetAttachedSurface(NextLevelDesc) Wend W przypadku błędu (obsłużymy wszystkie mipmapy, a wywołanie GetAttachedSurface nie zostanie wykonane i sposwoduje błąd), pętla jest kończona. W pętli umieszczamy obrazek na ostatnio wydobytej mipmapie. Przy pierwszym wejściu w pętlę będzie to mipmapa o poziomie 1. Następnie z mipmapy o poziomie 1 wydobywamy mipmapę o poziomie 2. Zaczynamy pętlę od początku i umieszczamy na niej obrazek itd… Po wyjściu z pętli usuwamy tymczasową powierzchnię: Set Level = Nothing Usuniemy tylko obiekt level, mipmapy zawarte wewnątrz powierzchni MipMap nie będą usunięte. Do omówienie pozostała funkcja PutImageInExistSurface umieszczająca obrazek na stworzonej wcześniej powierzchni. Public Sub PutImageInExistSurface(ByRef destSurf As DirectDrawSurface7, file As String) Dim PicSurf As DirectDrawSurface7 'opis tymczasowj powierzchni z obrazkiem Dim Desc As DDSURFACEDESC2 'opis powierzchni Dim SrcRect As RECT 'obdzas źródłowy kopiowania, czyli rozmiar powierzchni z obrazkiem Dim DestRect As RECT 'obszar docelowy Desc.lFlags = DDSD_CAPS 'to pole będzie opisywane Desc.ddsCaps.lCaps = DDSCAPS_OFFSCREENPLAIN Or DDSCAPS_VIDEOMEMORY 'powierzchnia niewidoczna, w pamięci karty graficznej Set PicSurf = DD.CreateSurfaceFromFile(file, Desc) 'tworzymy powierzchnię z obrazkiem PicSurf.GetSurfaceDesc Desc 'pobieramy jej opis, aby poznać jej rozmiary 'Ustalamy obszar źródłowy kopiowania SrcRect.Left = 0 SrcRect.Top = 0 SrcRect.Right = Desc.lWidth SrcRect.Bottom = Desc.lHeight destSurf.GetSurfaceDesc Desc 'pobieramy opis powierzchni docelowej 'Ustalamy obszar docelowy kopiowania DestRect.Left = 0 DestRect.Top = 0 DestRect.Right = Desc.lWidth DestRect.Bottom = Desc.lHeight destSurf.Blt DestRect, PicSurf, SrcRect, DDBLT_WAIT 'kopiujemy obrazek Set PicSurf = Nothing End Sub Możemy tworzyć powierzchnie, które zawierają obrazek za pomocą metody CreateSurfaceFromFile. Jednak nie możemy umieścić w ten sposób obrazka na istniejącej już powierzchni. Dlatego tworzymy dodatkową powierzchnię PicSurf, która będzie zawierała obrazek. Umieszczamy go tam w momencie jej tworzenia. Jej rozmiary są wybierane automatycznie i są to rozmiary obrazka. Na właściwą powierzchnię destSurf obrazek po prostu skopiujemy z powierzchni PicSurf. W tym celu musimy ustalić obszar źródłowy i docelowy kopiowania. Można je uzyskać z opisów tych dwóch powierzchni. Najpierw pobieramy opis powierzchni PicSurf, uzupełniamy strukturę SrcRect. W podobny sposób robimy ze struktórą DestRect. Na koniec pozostało kopiowanie: destSurf.Blt DestRect, PicSurf, SrcRect, DDBLT_WAIT