Witaj na dwudziestej lekcji. Format bitmap jest obsługiwany przez prawie każdy system operacyjny. Łatwo załadować bitmapę i wykorzystać ją jako teksturę. Do tej pory, używaliśmy mieszania kolorów, aby umieścić tekst i inne obrazki na ekranie, bez wymazywania tego, co jest za nimi. Jest to efektywne, ale wynik nie zawsze jest zadowalający.
W większości wypadków tekstura, dodana poprzez mieszanie kolorów, zbyt jest bardzo lub niedostatecznie widoczna. Gdy tworzysz grę, wykorzystującą sprity, raczej nie chcesz aby scena za postacią prześwitywała przez jej ciało. Gdy wypisujesz tekst na ekran, chcesz aby był jednolity i łatwy do odczytania.
W tym momencie wygodne staje się maskowanie. Jest to proces składający się z dwóch kroków. Najpierw umieszcza się czarnobiały obraz tekstury na scenie (maska). Biały reprezentuje część przezroczystą, czarny nieprzezroczystą. Wykorzystujemy odpowiedni tryb mieszania kolorów, tak aby pokazał się tylko czarny kolor. Następnie zmieniamy tryb mieszania kolorów i mapujemy teksturę na czarny kawałek na scenie. Wykorzystujemy odpowiedni tryb mieszania kolorów tak aby tekstura pojawiła się tylko tam, gdzie obecnie jest czarny kolor. Ogólnie, tam gdzie nałożymy maskę będzie czarno (pozostała część tła zostanie nie zmieniona). Przy nakładaniu tekstury, robimy to tak, że pojawi się tylko w miejscach gdzie jest czarno.
Umieszczę kod programu z tej lekcji w tekscie. Wyjątekiem będą części, które się nie zmieniły. Jeżeli jesteś gotowy nauczyć się czegoś nowego to... do dzieła.
Skorzystamy z siedmiu zmiennych globalnych. masking to wartość boolowska (PRAWDA/FAŁSZ), pamiętająca czy maskowanie jest włączone, czy nie. Zmienna mp jest wykorzystywana do oznaczania, że klawisz M jest trzymany. Podobnie sp - oznacza, że spacja jest wciśnięta. Zmienna scene pamięta czy rysujemy pierwszą czy drugą scenę.
Rezerwujemy także miejsce dla pięciu tekstur w tablicy texture. loop to licznik pętli. Skorzystamy z niego kilka razy w programie. Jest też zmienna roll. Przyda się przy przewijaniu tekstur przez ekran. Takie przewijanie daje dość ciekawy efekt. Skorzystamy z niej także, aby obracać obiekt w scenie drugiej.
Kod odpowiedzialny za załadowanie bitmapy się nie zmienił. Jest taki sam jak np. w lekcji 6.
W poniższym kodzie tworzymy miejsce na pięć obrazków. Czyścimy pamięć i ładujemy pięć bitmap. Przechodzimy w pętli po wszystkich obrazkach i konwertujemy je do tekstur. Tekstury są zapamiętane w texture[0-4].
Kod ReSizeGLScene() nie uległ zmianie, więc pozwolę go sobie pominąć. Kod inicjalizujący jest prosty jak drut. Ładujemy tekstury, ustawiamy kolor, ustawiamy bufor głębokości, włączamy ładne cienie oraz teksturowanie. Prosty program. Nie ma potrzeby tworzyć skomplikowanej funkcji init ;-)
Teraz ciekawa część. Kod rysujący. Zaczynamy tak jak zawsze. Czyścimy ekran i bufor głębi. Następnie resetujemy macierz modelview i przesuwamy się w stronę sceny o dwie jednostki, dzięki czemu możemy zobaczyć scenę.
Pierwsza linia poniżej wybiera teksturę 'logo'. Wyświetlimy tę teksturę na ekran wykorzystując czworokąt. Specyfikujemy cztery koordynaty tekstury razem z wierzchołkami.
Uzupełnienie, której autorem jest Jonathan Roy: Pamiętaj, że OpenGL ma system grafiki oparty na wierzchołkach. Większość ustawianych parametrów jest zapamiętywana jako atrybuty wierzchołka. Koordynaty tekstury to jeden z nich. Po prostu specyfikujesz koordynaty tekstury dla każdego wierzchołka wielokąta, a OpenGL automatycznie wypełnia powierzchnię, między wierzchołkami, teksturą. Ten proces jest znany jako interpolacja. Interpolacja to standardowa technika, która pozwala OpenGL określić jak zadany parametr zmienia się pomiędzy wierzchołkami (wartości są znane tylko w wierzchołkach).
Tak, jak w poprzednich lekcjach udajemy, że widzimy czworokąt od przodu i przypisujemy koordynaty tekstury następująco: (0.0, 0.0) do lewego, dolnego rogu, (0.0, 1.0) do lewego górnego rogu, (1.0, 0.0) do prawego, dolnego i (1.0, 1.0) do prawego, górnego. Teraz, gdy ustawiłeś te wartości, możesz powiedzieć jakie koordynaty powinny być przypisane do środka czworokąta. Tak!! (0.5, 0.5). Nigdzie, w kodzie, nie wyspecyfikowałeś tej wartości, nieprawdaż? Gdy OpenGL rysuje czworokąt, to oblicza tę wartość. To magia, bo OpenGL potrafi obliczyć te wartości niezależnie od kształtu, wielkości czy orientacji wielokąta.
W tej lekcji zrobimy sztuczkę. Zadamy koordynaty tekstury inne niż 0.0 czy 1.0. Koordynaty tekstury powinny być znormalizowane. Wartość 0.0 odpowiada jednemu krańcowi tekstury, podczas gdy 1.0 przeciwległemu. Powyżej 1.0, odwzorowanie zawija się wokół krawędzi i tekstura się powtarza. Innymi słowy. Koordynaty (0.3, 0.5) oznaczają dokładnie ten sam piksel obrazka tekstury, co (1.3, 0.5), albo (12.3, -2.5). W tej lekcji, uzyskamy efekt kaflowania, poprzez zadanie wartości 3.0 zamiast 1.0. W ten sposób łatwo powtarzamy teksturę 9 razy (3x3) na powierzchni czworokąta.
Dodatkowo, użyjemy zmiennej roll aby przesuwać teksturę po powierzchni czworokąta. Wartość 0.0 roll, która jest dodawana do pionowych koordynatów, oznacza, że tekstura przy dolnej krawędzi czworokąta zaczyna się dolną krawędzią obrazka tekstury. Gdy roll równa się 0.5, to mapowanie dolnej grawędzi czworokąta zaczyna się pół obrazka wyżej. Przesuwanie tekstur może służyć do generowania ciekawych efektów, takich jak poruszające się chmury czy słowa obracające się wokół obiektu.
OK, wróćmy do rzeczywistości. Teraz włączamy mieszanie kolorów. Aby ten efekt zadziałał, musimy wyłączyć testowanie głębi. To bardzo ważne. Jeżeli tego nie wyłączysz, to prawdopodobnie nic nie zobaczysz. Cały obrazek zniknie!
Pierwszą rzeczą, którą należy teraz zrobić, jest sprawdzenie czy będziemy maskować obraz czy mieszać go tak jak dawniej. Poniższa linia kodu sprawdza czy maskowanie jest ustawione na TRUE. Jeżeli tak, to ustawiamy mieszanie kolorów tak, aby maska była właściwie wyświetlana na ekranie.
Jeżeli masking jest ustawione na TRUE, to linia poniżej ustawi funkcję mieszającą dla naszej maski. Maska to kopia tekstury, którą chcemy narysować na ekranie. Tyle, że czarnobiała. Każdy obszar maski, który jest biały oznacza przezroczystość. Każdy obszar czarny, brak przezroczystości.
Komenda blend zachowuje się następująco: Kolor docelowy (kolor ekranu) będzie ustawiony na czarny jeżeli kopiowany obszar maski jest czarny. To oznacza, że obszary ekranu, które przykrywa czarna maska, będą czarne. Wszystko co było w tym miejscu pod maską zostanie wyczyszczone do koloru czarnego. Część maski, o białym kolorze, nie powoduje zmiany na ekranie.
Teraz sprawdzamy, którą scenę narysować. Jeżeli scene jest TRUE to rysujemy drugą, w przeciwnym wypadku pierwszą scenę.
Nie chcemy, aby wszystko było duże, dlatego przesuwamy się jeszcze jedną jednostkę do sceny. To zmniejsza wielkość obiektów.
Po przesunięci, obrazamy scenę z 0-360 stopni zależnie od wartości roll. Jeżeli roll jest 0.0 będziemy obracać o 0 stopni. Jeżeli 1.0 to o 360. Szybka rotacja, ale nie chciało mi się tworzyć kolejnej zmiennej tylko po to, aby obrócić obrazek na środku ekranu ;-)
Mamy przesuwające się logo. Scena obraca się wokół osi Z, powodując rotację każdego z rysowanych obiektów przeciwnie do ruchu wskazówek zegara. Teraz wystarczy sprawdzić czy maskowanie jest włączone. Jeżeli tak, to narysujemy maskę, a następnie nasz obiekt. Jeżeli maskowanie jest wyłączone, to po prostu narysujemy obiekt.
Jeżeli masking jest TRUE, to kod poniżej narysuje maskę na ekran. Nasz tryb mieszania kolorów powinien być ustawiony odpowiednio, ponieważ już raz sprawdzaliśmy maskowanie. Teraz wystarczy tylko narysować maskę. Wybieramy maskę 2 (bo to jest druga scena). Po wybraniu maski, teksturujemy czworokąt. Wielokąt jest 1.1 jednostki w lewo i w prawo, tak aby wypełniał ekran trochę dalej. Chcemy aby tylko jedna tekstura się pojawiła, więc koordynaty są od 0.0 do 1.0.
Po narysowaniu maski na ekran, nieprzezroczysta kopia tekstury pojawi się na ekranie. Ostatecznie scena wygląda jakby ktoś wziąć foremkę do ciastek i wyciął figurę tekstury na scenie, pozostawiając czarne miejsce.
Teraz, gdy ustawiliśmy maskę, przyszedł czas aby ponownie zmienić tryb mieszania kolorów. Tym razem powiemy OpenGL aby kopiował część naszej pokolorowanej tekstury, która nie jest czarna na ekran. Ponieważ końcowa tekstura jest dokładną kopią maski, ale z kolorem, to jedyne części, które zostaną narysowane, to te, które lądują na czarnym kawałku maski. Ponieważ maska jest czarna, nic z ekranu nie będzie prześwitywało przez naszą teksturę. To pozostawia nas z nieprzezroczystą teksturą unoszącą się nad ekranem.
Zauważ, że wybieramy drugi obraz po wybraniu ostatecznego trybu mieszania kolorów. To wybiera pokolorowany obraz (obraz, na którym bazuje druga maska). Zauważ także, że rysujemy ten obraz zaraz na masce. Te same koordynaty tekstury, te same wierzchołki.
Jeżeli nie położylibyśmy wcześniej maski, to obraz nadal były kopiowany na ekran. Tyle tylko, że prześwitywała by przez niego scena.
Jeżeli scene jest FALSE, to rysujemy pierwszą scenę (moja ulubiona).
Zaczynamy, sprawdzając czy masking jest TRUE czy FALSE. Tak jak wcześniej.
Jeżeli masking jest TRUE to rysujemy maskę 1. Zauważ, że tekstura przesuwa się z prawej do lewej (roll jest dodawane do koordynatów poziomych). Chcemy aby tekstura wypełniła cały ekran, dlatego nie przesuwany się do sceny.
Ponownie włączamy mieszanie kolorów i wybieramy teksturę dla sceny 1. Nakładamy teksturę na maskę. Zauważ, że przesuwamy także tę teksturę. Gdybyśmy tak nie zrobili, to maska nie zgadzałaby się z obrazkiem.
Następnie włączmy test głębokości i wyłączamy mieszanie kolorów. Dzięki temu nie będą się działy dziwne rzeczy w dalszej części programu ;-)
Zostało tylko zwiększenie wartości roll. Jeżeli roll przekracza 1.0 to odejmujemy 1.0. To zapobiega zbyt szybkiemu wzrostowi wartości roll.
KillGLWindow(), CreateGLWindow() i WNDProc() się nie zmieniły, więc pomijamy.
W WinMain zmienił się tytuł okna. Teraz nazywa się "NeHe's Masking Tutorial". Zmień tytuł na jaki chcesz :-)
Teraz do kodu obsługujące klawiaturę. Sprawdzamy czy wciśnięto spację. Jeżeli tak to ustawiamy zmienną sp. Jeżeli sp jest TRUE, to poniższy kod nie zostanie przetworzony drugi raz aż do czasu zwolniania spacji. Dzięki temu program nie będzie szybko przeskakiwał ze sceny do sceny. Po ustawieniu sp na TRUE, zmieniamy wyświetlaną scenę (zmienna scene). Jeżeli było TRUE, to staje się FALSE, jeżeli było FALSE to staje się TRUE. W powyższym kodzie, jeżeli scene jest FALSE to jest rysowana scena numer jeden. Jeżeli scene jest TRUE to jest rysowana druga scena. [translator]Takie podejście nie jest zbyt dobre. Gdyby była potrzeba dodania trzeciej sceny to programista miałby sporo pracy. Lepiej zrobić zmienną typu int. W poważniejszym projekcie należałoby wydzielić sceny do osobnych klas i zdać się na mechanizm polimorfizmu.[/translator].
Poniższy kod sprawdza czy spacja została puszczona. Jeżeli tak to ustawiamy sp na FALSE, dzięki czemu program wie, że spacja nie jest przytrzymana. Ustawiając sp na FALSE, powyższy kod sprawdzi czy spacja została wciśnięta ponownie. Jeżeli tak to przełączy stan sp. I tak w kółko.
Kolejna część kodu jest taka sama. Spacja została zamieniona na M, ale zasada działania jest ta sama. Klawisz m odpowiada za sterowanie zmienną masking, która określa czy maskowanie jest włączone czy nie.
Tak jak w poprzednich lekcjach, sprawdź czy tytuł okna został poprawnie ustawiony.
Tworzenie maski nie jest trudne, ale zajmuje trochę czasu. Najlepszym sposobem jest, mając gotowy obrazek, załadować go do programu graficznego (np. irfanview) i zredukować go do odcieni szarości. Następnie zmień kontrast tak, aby szare piksele stały się czarne. Jeżeli stworzysz maskę i zobaczysz kwadratowe kształty wokół tekstury, to oznacza, że biel w masce nie jest wystarczająco biała (255 lub FFFFFF), albo czerń nie jest prawdziwie czarna (0 lub 000000). Poniżej możesz zobaczyć przykład maski i obrazka, który zostanie położony na maskę. Obraz może być dowolnego koloru, dopóki jego tło jest czarne. Maska musi mieć białe tło i czarną kopię obrazka.
To jest maska -> Maska To jest obraz -> Obraz
Eric Desrosiers wskazał, że możesz sprawdzić wartość każdego piksela w bitmapie podczas ładowania. Jeżeli chcesz aby piksel był przezroczysty daj mu wartość alpha równą 0. Dla pozostałych kolorów wystarczy nadać im wartość alpha równą 255. Ta metoda również działa, ale wymaga trochę dodatkowego pisania. Aktualna wersja lekcji jest prosta i wymaga niewiele pisania. Nie jestem ślepy na inne techniki, ale gdy piszę lekcje, staram się robić to tak, aby były łatwe do napisania i zrozumienia. Chciałem tylko zwrócić Twoją uwagę, że jest inna metoda osiągnięcia tego samego efektu.
W tej lekcji pokazałem Ci prostą, ale efektywną metodę rysowania części tekstury, bez użycia kanału alpha. Zwykłe mieszanie kolorów zazwyczaj wygląda źle (tekstury są przezroczyste lub nie). Teksturowanie z użyciem kanału alpha wymaga, aby obrazki obsługiwały ten kanał. Bitmapy są bardzo wygodne w użyciu, ale nie obsługują kanału alpha. Ten program pokazuje jak obejść te ograniczenia.
Rob Santa! Dzięki za pomysł i przykładowy kod. Nigdy nie słyszałem o tej sztuczce, aż do czasu, kiedy mi ją pokazałeś. Chciał, abym zwrócił uwagę, że mimo iż trik działa, to potrzebuje dwóch przejść. Jest to cios wymierzony w wydajność. Rob zaznacza, że dla skomplikowanych scen lepiej użyć tekstur z kanałem alpha.
Mam nadzieję, że ten tutorial Ci się podobał. Jeżeli masz jakieś problemy ze zrozumieniem lub znalazłeś błąd, to pisz do mnie śmiało [translator]Do autora należy pisać w języku angielskim. W przypadku błędów językowych należy pisać na tej stronie, w dziale służącym do zgłaszania błędów.[/translator]. Staram się tworzyć najlepsze, dostępne lekcje. Odzew z Twojej strony jest dla mnie niezwykle ważny.