Tekstury w OpenGL

W dwóch poprzednich częściach kursu OpenGL dla programistów C++Buildera i Delphi opracowaliśmy zestaw funkcji, które zawierają algorytmy rozpoznania i inicjowania aparatu graficznego, rozstawienia świateł oraz zabudowy sceny różnymi kształtami. Dzisiaj poznamy kolejną technikę - tzw. teksturowanie obiektów, czyli oblewanie ich mapami bitowymi.

W dwóch poprzednich częściach kursu OpenGL dla programistów C++Buildera i Delphi opracowaliśmy zestaw funkcji, które zawierają algorytmy rozpoznania i inicjowania aparatu graficznego, rozstawienia świateł oraz zabudowy sceny różnymi kształtami. Dzisiaj poznamy kolejną technikę - tzw. teksturowanie obiektów, czyli oblewanie ich mapami bitowymi.

Światło ma decydujący wpływ na postrzeganie obiektów. Potrafimy definiować źródła światła, dowolnie je rozstawiać, a także określać optyczne właściwości materiałów. Jednak naszym obiektom ciągle czegoś brakuje - mimo zdefiniowania kolorów, rozproszeń, odblasków, nie umiemy stwierdzić, czy obracający się sześcian wykonany jest z drewna, metalu czy może ze szkła. W realnym świecie powierzchnia obiektów ma fakturę. W OpenGL mechanizm pozwalający nadać obiektom fakturę nazywa się teksturowaniem.

Podstawy

Tekstura jest plikiem graficznym, którego zawartość zostaje nałożona na ścianki obiektów zabudowujących scenę. Skoncentrujemy się na teksturach dwuwymiarowych, choć OpenGL umożliwia też operowanie innymi teksturami.

Tekstura musi spełniać ważny i nietrudny do zrealizowania warunek - jej rozmiar w pikselach musi być potęgą dwójki. Oznacza to, że wysokość i szerokość pliku z teksturą może być jedną z następujących wartości: 2, 4, 8 i tak dalej, aż do 2048. Ograniczenie to zostało wprowadzone przez projektantów kart graficznych i w praktyce nie stanowi żadnego utrudnienia.

Pierwsza tekstura

W przykładach zastosujemy tekstury wczytywane z plików BMP lub RAW. Pliki BMP są powszechnie znane, ale za to pliki RAW mają nieco dogodniejszą strukturę. W pierwszym ćwiczeniu zaimplementujemy odczyt pliku RAW, a do jego uzyskania wykorzystamy dowolne narzędzie graficzne, np. przeglądarkę IrfanView (rys. 1).

Na początek utwórzmy dowolny plik graficzny o rozmiarze 256x256 pikseli i zapiszmy go w formacie RAW (gotowe pliki o odpowiednich parametrach znajdują się na płycie CD) w pliku o nazwie TEKSTURA.RAW. Zacznijmy od napisania funkcji, która odczyta plik z mapą bitową i na jego podstawie utworzy teksturę:

procedure TForm1.wczytaj_teksture;

const

ROZMIAR_MAPY = 256; //uzgodnione z rozmiarem var //plików graficznych

FileHandle:Integer;

dane:array[1..ROZMIAR_MAPY*ROZMIAR_MAPY*3] of glUByte;

nr_tekstury:glUint;

begin

FileHandle:=

FileOpen('tekstury/tekstura.raw',fmOpenRead);

FileRead(FileHandle,dane,ROZMIAR_MAPY*ROZMIAR_MAPY*3);

FileClose(FileHandle);

glGenTextures(1,nr_tekstury);

glBindTexture(GL_TEXTURE_2D,nr_tekstury);

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

glTexImage2D(GL_TEXTURE_2D,0,3,ROZMIAR_MAPY,ROZMIAR_MAPY,0,GL_RGB,GL_UNSIGNED_BYTE,@dane);

end;

Rysunek 1. Format RAW uzyskamy z dowolnego obrazu na przykład za pomocą przeglądarki IrfanView. Bardzo prosty format RAW nawet nie jest tutaj nazywany obrazem - to najprostsza sekwencja danych RGB, RGB,... składająca się na opis amplitud koloru każdego piksela.

Rysunek 1. Format RAW uzyskamy z dowolnego obrazu na przykład za pomocą przeglądarki IrfanView. Bardzo prosty format RAW nawet nie jest tutaj nazywany obrazem - to najprostsza sekwencja danych RGB, RGB,... składająca się na opis amplitud koloru każdego piksela.

Plik RAW jest prostą sekwencją jednobajtowych składowych RGB kolorów każdego piksela, zatem tutaj ma długość 256x256x3 bajty. Przygotowujemy tablicę o niezbędnej wielkości, wczytujemy do niej dane z pliku RAW i w ostatnim wierszu algorytmu podajemy ich adres bibliotece OpenGL.

Polecenie glGenTextures() powoduje wygenerowanie pustej jeszcze tekstury i przypisanie jej numeru, przekazanego jako drugi parametr. Numery tekstur będą miały najważniejsze znaczenie podczas dynamicznego ich wymieniania.

Następna funkcja, glBindTexture(), pobiera dwa parametry. Pierwszy określa, jakiego rodzaju tekstury zamierzamy stosować. Podanie wartości GL_TEXTURE_2D oznacza, że pracujemy z teksturami dwuwymiarowymi. Drugi parametr określa wcześniej uzyskany numer tekstury. Funkcja ta będzie jeszcze wielokrotnie wywoływana do zmieniania tekstur - oczywiście dopiero wtedy, gdy będziemy mieli cały ich zestaw, a nie tylko jedną, jak obecnie.

Można tylko pomarzyć, aby mapa bitowa swym rozmiarem w pikselach dokładnie odpowiadała rozmiarowi wykreślanego obiektu. Funkcja glTexParameteri() definiuje sposób dopasowywania tekstury do rozmiarów obiektu, gdy należy ją powiększyć (GL_TEXTURE_MAG_FILTER) lub pomniejszyć (GL_TEXTURE_MIN_FILTER).

Ta tekstura jest skalowana liniowo (GL_LINEAR) zarówno do pomniejszania, jak i powiększania. Jest to najczęściej stosowana metoda rozciągania i ściskania tekstur, choć są inne techniki.

Ostatnia, najważniejsza funkcja tworzy określoną teksturę.

glTexImage2D(GL_TEXTURE_2D, 0, liczba_kolorów,dx, dy, 0, format, rozmiar_danej, adres_danych)

Oto opis jej kluczowych parametrów:

liczba_kolorów - ponieważ nasza bitmapa zawiera składowe RGB, ustawiamy na 3

dx, dy - szerokość i wysokość tekstury

format - struktura piksela mapy bitowej; wpisujemy tutaj GL_RGB

rozmiar_danej - informacja o głębi koloru, zazwyczaj GL_UNSIGNED_BYTE

adres_danych - wskaźnik na początek tablicy z danymi RGB.

Wywołanie funkcji wczytaj_teksture() zostanie zrealizowane w procedurze obsługi zdarzenia OnCreate, która teraz przyjmie taką postać:

void__fastcall TForm1::FormCreate(TObject *Sender)

{

otworz_OpenGL();

wczytaj_teksture();

}


Zobacz również