Wykorzystując biblioteki OpenGL i GLUT napisać program przedstawiający perspektywiczny obraz elipsoidy, na który została odwzorowana dwuwymiarowa tekstura RGBA zdefiniowana wzorcem:

schemat tekstury

Obiekt oświetlony jest białym światłem ze źródła reflektorowego (spot), a parametry jego materiału mają wartości domyślne, z wyjątkiem GL_SPECULAR, który ma przyjmować wartość (1.0, 1.0, 1.0, 1.0). Użytkownik powinien mieć możliwość:

  1. Zmiany rozmiarów tekstury w zakresie od 4×4 do 128×128 tekseli z zachowaniem proporcji wzorca.
  2. Powielania tekstury w zakresie od 1 do 5 niezależnie w kierunku poziomym i pionowym (GL_REPEAT).
  3. Zmiany metody filtrowania tekstury (GL_NEAREST, GL_LINEAR)
  4. Zmiany trybu teksturowania (GL_DECAL, GL_MODULATE, GL_BLEND)
  5. Zmiany położenia źródła światła.
  6. Zmiany położenia obserwatora poprzez podanie następujących parametrów:

    - odległości obserwatora od środka układu współrzędnych sceny,

    - wysokości względem płaszczyzny XZ,

    - kąta obrotu wokół osi OY w zakresie [0o, 360o] z krokiem 1o.

Oświetlony obiekt powinien zawsze znajdować się w centralnej części okna.

Zadanie rozpocząłem od zamodelowania elipsoidy.

x=2*r*cos(DEGRAD(v))*cos(DEGRAD(u));
y=r*sin(DEGRAD(v));
z=r*cos(DEGRAD(v))*sin(DEGRAD(u));
wejscie[0][0]=x;
wejscie[0][1]=y;
wejscie[0][2]=z;

x=2*r*cos(DEGRAD(v+f))*cos(DEGRAD(u));
y=r*sin(DEGRAD(v+f));
z=r*cos(DEGRAD(v+f))*sin(DEGRAD(u));
wejscie[1][0]=x;
wejscie[1][1]=y;
wejscie[1][2]=z;

x=2*r*cos(DEGRAD(v+f))*cos(DEGRAD(u+e));
y=r*sin(DEGRAD(v+f));
z=r*cos(DEGRAD(v+f))*sin(DEGRAD(u+e));
wejscie[2][0]=x;
wejscie[2][1]=y;
wejscie[2][2]=z;

x=2*r*cos(DEGRAD(v))*cos(DEGRAD(u+e));
y=r*sin(DEGRAD(v));
z=r*cos(DEGRAD(v))*sin(DEGRAD(u+e));
wejscie[3][0]=x;
wejscie[3][1]=y;
wejscie[3][2]=z;

Tablica dwuwymiarowa wejście zbiera współrzędne punktów do utworzenia wektora normalnego. Następnie musimy przekazać te parametry do funkcji glVertex, zanim jednak to zrobimy należy podać listy wierzchołków obiektu oraz odpowiednio dobrane koordynaty tekstury. Ma to na celu odpowiednie nałożenie tekstury na obiekt, tzn. prawa górna część obiektu powinna odpowiadać prawej górnej części tekstury itd. Służy do tego funkcja GLtexcoord2f. Wywołuje się ją przed wywołaniem funkcji rysującej każdy wierzchołek obiektu. Przyjmuje ona dwa parametry w zakresie od 0.0 do 1.0. Pierwszy parametr określa składową x. Podanie wartości 0.0 jako pierwszego parametru oznacza skrajnie lewą stronę tekstury, natomiast 1.0 wyznacza prawą część tekstury. Analogicznie należy postępować podając wartości w drugim parametrze. Wartość 0.0 oznacza dolną część tekstury, a 1.0 górną. Łącząc odpowiednie części tekstury z odpowiednimi częściami obiektu uzyskuje się optymalny obraz obiektu. Realizuje to kod:

ObliczanieWektora(wejscie,wektor);
glNormal3f(wektor[0],wektor[1],wektor[2]);
glTexCoord2f(0.0, 0.0);
glVertex3fv(wejscie[0]);
glTexCoord2f(0.0, 1.0);
glVertex3fv(wejscie[1]);
glTexCoord2f(1.0, 1.0);
glVertex3fv(wejscie[2]);
glTexCoord2f(1.0, 0.0);
glVertex3fv(wejscie[3]);

Następnym krokiem będzie zdefiniowanie tekstury. W tym celu tworzę teksturę, która rozumiana jest jako prostokątna tablica zawierająca w każdym elemencie wartości składowe koloru R, G, B, A lub jasności.

float kolor1[4]={1.0,0.0,0.0,1.0};
float kolor2[4]={1.0,1.0,1.0,0.9};
for(i=0;i=(size-ctw)){
*(wsk++) = kolor1[0];
*(wsk++) = kolor1[1];
*(wsk++) = kolor1[2];
*(wsk++) = kolor1[3];
}
else {
*(wsk++) = kolor2[0];
*(wsk++) = kolor2[1];
*(wsk++) = kolor2[2];
*(wsk++) = kolor2[3];
}
}
for(i=0;i=(size-ptw)){
*(wsk++) = kolor1[0];
*(wsk++) = kolor1[1];
*(wsk++) = kolor1[2];
*(wsk++) = kolor1[3];
}
else {
*(wsk++) = kolor2[0];
*(wsk++) = kolor2[1];
*(wsk++) = kolor2[2];
*(wsk++) = kolor2[3];
}
}
glTexImage2D(GL_TEXTURE_2D,0,4,size,size,0,GL_RGBA,GL_FLOAT,tekstura);

W pierwszych dwóch tablicach float zostały zdefiniowane dwa kolory – czerwony i biały. Parametr alpha został wstępnie ustawiony na 0.9 aby było widać ustawioną teksturę. Kiedy tekstura (tablica) jest już utworzona można ją zdefiniować. Do definiowania dwuwymiarowej tekstury służy funkcja

glTexImage2D(rodzaj, poziom, kolory, szerokość, wysokość, ramka, format, typ, punkty);

rodzaj – rodzaj tekstury; powinno wynosić GL_TEXTURE_2D
poziom – poziom szczegółów mipmapy; jeśli mipmapy nie są używane wynosi 0
kolory – ilość komponentów koloru; może wynosić od 1 do 4
szerokość – szerokość obrazu tekstury; musi być potęgą liczby 2 powiększoną ewentualnie o szerokość ramki wokół tekstury
wysokość – wysokość obrazu tekstury; musi być potęgą liczby 2 powiększoną ewentualnie o wysokość ramki wokół tekstury
ramka – rozmiar ramki wokół obrazu tekstury; może wynosić 0, 1 lub 2
format – format danych (możliwe wartości podano w tabeli poniżej)
typ – typ danych dla wartości punktów (możliwe wartości podano w tabeli poniżej)
punkty – tablica o rozmiarze szerokość x wysokość x kolory zawierająca dane

Parametry tekstury ustawiam za pomocą funkcji:

glTexImage2D(GL_TEXTURE_2D,0,4,size,size,0,GL_RGBA,GL_FLOAT,tekstura);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,filtr);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,filtr);
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,tryb);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
if(tryb==GL_BLEND)
glTexEnvfv(GL_TEXTURE_ENV,GL_TEXTURE_ENV_COLOR,kolor);

glTexParameteri(tekstura, parametr, wartość);

Umożliwia ona między innymi ustalenie w jaki sposób będą przeprowadzane operacje związane z obliczaniem wartości kolorów brakujących pikseli w przypadku nakładania tekstury na obiekt mniejszy od rozmiaru tekstury (czyli przy powiększaniu), czy też jak ma przebiegać ukrywanie pikseli przy zmniejszaniu tekstury. Danymi wejściowymi funkcji są:

tekstura – rodzaj tekstury: GL_TEXTURE_1D lub GL_TEXTURE_2D
parametr – jeden z parametrów z poniższej tabeli
wartość – wartość dla parametru

Parametry wejściowe:

GL_TEXTURE_MIN_FILTE – Określa sposób zmniejszania tekstury; możliwe wartości:

  • GL_NEAREST – brakujący punkt przyjmuje kolor sąsiedniego
  • GL_LINEAR – liniowa interpolacja pikseli
  • GL_NEAREST_MIPMAP_NEAREST – przyjmowana jest mipmapa najbliższa rozmiarem do pokrywanego wielokąta; przy teksturowaniu brakujące punkty przyjmują kolor najbliższych
  • GL_NEAREST_MIPMAP_LINEAR – przyjmowana jest mipmapa najbliższa rozmiarem do pokrywanego wielokąta; przy teksturowaniu wykorzystywana jest liniowa interpolacja
  • GL_LINEAR_ MIPMAP_NEAREST – interpolowane są dwie mipmapy najbliższe rozmiarowi pokrywanego wielokąta; przy teksturowaniu brakujące punkty przyjmują kolor najbliższych
  • GL_LINEAR_ MIPMAP_LINEAR – interpolowane są dwie mipmapy najbliższe rozmiarowi pokrywanego wielokąta; przy teksturowaniu wykorzystywana jest liniowa interpolacja

GL_TEXTURE_MAX_FILTER – Określa sposób powiększania tekstury; możliwe wartości:

  • GL_NEAREST – brakujący punkt przyjmuje kolor sąsiedniego
  • GL_LINEAR – liniowa interpolacja pikseli

GL_TEXTURE_WRAP_S – Określa sposób traktowania współrzędnej S tekstury poza zakresem od 0.0 do 1.0; możliwe wartości:

  • GL_CLAMP – poza zakresem używany jest kolor ramki tekstury lub stały kolor
  • GL_REPEAT – tekstura jest powtarzana na całej powierzchni wielokąta

GL_TEXTURE_WRAP_T – określa sposób traktowania współrzędnej T tekstury poza zakresem od 0.0 do 1.0; możliwe wartości:

  • GL_CLAMP – poza zakresem używany jest kolor ramki tekstury lub stały kolor
  • GL_REPEAT – tekstura jest powtarzana na całej powierzchni wielokąta

GL_BORDER_COLOR – Określa stały kolor ramki w przypadku, gdy tekstura nie posiada zdefiniowanej ramki; wartość stanowi wskaźnik do tablicy zawierającej składowe RGB koloru

Na koniec włączam teksturowanie dwuwymiarowe oraz wyłączam teksturowanie jednowymiarowe następującymi fukcjami:

glDisable(GL_TEXTURE_1D);
glEnable(GL_TEXTURE_2D);

Tekstura została zamodelowana i nałożona na obiekt. Sprawdźmy więc jak wygląda nasza elipsoida przed oświetleniem. Aby sprawdzić czy prawidłowo zaprojektowaliśmy teksturę ustawimy parametr alpha koloru2 (białego) na 0.9.

elipsoida1

Jak widać tekstura została prawidłowo zamodelowana. Kolor biały jest lekko prześwitujący. Ustawmy więc jego intensywność alpha na 0.3. Widok perspektywiczny.

elipsoida2

Teraz włączymy oświetlenie. Oświetlanie brył zrealizowałem w sprawozdaniu do laboratorium nr 3, tak więc nie będę rozpisywał się w tym temacie. Kod realizujący oświetlenie bryły.

Parametry światła:

GLfloat swiatlo[10][4] = {{1.0,1.0,1.0,1.0},//[0] swiatlo otoczenia
{1.0,1.0,1.0,1.0},//[1]swiatlo rozproszone
{1.0,1.0,1.0,1.0},//[2] swiatlo zwierciadlane
{4.0,-1.0,4.0,1.0},//[3] polozenie w=0 to światło kierunkowe
{-4.0,1.0,-4.0,1.0},//[4] kierunek swieceniaGL_SPOT_DIRECTION
{90.0,0.0,0.0,0.0},//[5] kąt odcięcia GL_SPOT_CUTOFF
{10.0,0.0,0.0,0.0},//[6]tlumienie kątowe światła GL_SPOT_EXPONENT

Ustawienie parametrów materiału:

float material[5][4]={{1.0,0.0,0.8,1.0},//[0]wspolczynnik odbicia siwatłaotoczenia
{1.0,0.0,0.8,1.0},//[1]wspolczynik odbicia swiatla rozpraszanego
{1.0,1.0,1.0,1.0},//[2]wspolczynnik odbicia swiatla lustrzanego
{10.0,0.0,0.0,1.0},//[3] polysk jak widziany
{0.0,0.0,0.0,0.0}};//[4]kolor swiatla emitowanego
glMaterialfv(GL_FRONT,GL_AMBIENT,material[0]);
glMaterialfv(GL_FRONT,GL_DIFFUSE,material[1]);
glMaterialfv(GL_FRONT,GL_SPECULAR,material[2]);
glMaterialfv(GL_FRONT,GL_SHININESS,material[3]);
glMaterialfv(GL_FRONT,GL_EMISSION,material[4]);
}

Teraz włączamy oświetlenie:

glEnable(GL_LIGHTING);
glLightfv(GL_LIGHT1,GL_AMBIENT,swiatlo[0]);
glLightfv(GL_LIGHT1,GL_DIFFUSE,swiatlo[1]);
glLightfv(GL_LIGHT1,GL_SPECULAR,swiatlo[2]);
glLightfv(GL_LIGHT1,GL_POSITION,swiatlo[3]);
glLightfv(GL_LIGHT1,GL_SPOT_DIRECTION,swiatlo[4]);
glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, swiatlo[6][0]);
glLightfv(GL_LIGHT1,GL_SPOT_CUTOFF,swiatlo[5]);
glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, swiatlo[7][0]);
glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, swiatlo[8][0]);
glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, swiatlo[9][0]);
glEnable(GL_LIGHT1);
glPushAttrib(GL_LIGHTING_BIT);
glPushMatrix();
glDisable(GL_LIGHTING);
glTranslatef(swiatlo[3][0],swiatlo[3][1],swiatlo[3][2]);
glColor3f(1.0,1.0,1.0);
glutSolidSphere(0.3,20,20);
glPopMatrix();
glPopAttrib();

Światło zostało włączone, oraz utworzony reflektor (sfera o promieniu 0.3). Efekt w programie.

TRYB GL_MODULATE (zmiana za pomocą klawisza M – shift+m)

elipsoida3

TRYB GL_BLEND (zmiana za pomocą klawisza B – shift+b)

elipsoida4

TRYB GL_DECAL (zmiana za pomocą klawisza D – shift+d)

elipsoida5

Dlaczego uzyskaliśmy takie efekty w każdym z tych trybów?

Scharakteryzuję po krótce każdy z tych trybów:

GL_DECAL

Tryb zdefiniowany jest tylko dla wartości parametri internationalformat równej trzy lub cztery. Gdy liczba składowych jest równa trzy, pierwotny kolor obiektu jest zastępowany kolorem tekstury, wartość składowej alfa fragmentu pozostaje bez zmian. Przy czterech składowych natomiast pierwotny kolor obiektu jest mieszany z kolorem tekstury w proporcji uzależnionej od wartości składowej tekstury, wartość składowej alfa fragmentu pozostaje bez zmian.

GL_MODULATE

Tryb zdefiniowany jest dla wszystkich możliwych wartości internalformat. Przy liczbie składowych równej jeden lub dwa, pierwotny kolor obiektu mnożony jest przez liczbę określającą jasność tekstury, tak więc kolor obiektu jest modulowany jasnością tekstury. Wartość koloru obiektu zmienia się pierwotnego koloru (jasność równa 1.0) do koloru czarnego (jasność równa 0.0). Gdy liczba składowych jest równa trzy lub cztery, wówczas składowe RGB pierwotnego koloru obiektu mnożone są przez odpowiadające składowe koloru tekstury. Gdy wartość alfa występuje w teksturze (internalformat równy dwa lub cztery), wówczas wynikowa wartość alfa obliczana jest jako iloczyn składowej obiektu oraz tekstury. W przeciwnym razie pozostawiana jest bez zmian.

GL_BLEND

Tryb określany jest dla liczby składowych równej jeden oraz dwa. Wartość koloru wyznaczana jest poprzez zmieszanie pierwotnego koloru obiektu z kolorem ustalony przez GL_TEXTURE_ENV_COLOR. Proporcja w jakiej ma miejsce zmieszanie kolorów jest ustalona na podstawie jasności tekstury. Gdy liczba składowych równa jest jeden wartość alfa obiektu pozostanie bez zmian, natomiast gdy równa jest dwa wartość alfa ustalana jest przez pomnożenie składowych alfa tekstury oraz obiektu.

Kilka screenshotów z działania programu pokazujących obiekt w różnych perspektywach oraz przy różnym zagęszczeniu tekstury.

screenshot1

screenshot2

Dołączam kod do przeanalizowania programu: