Wizualny OpenGL (III)


Przypomnijmy, że ROZMIAR_X i ROZMIAR_Y określają rozmiar mapy wysokości, a zmienna skala_xy zawiera skalę, za pomocą której przekształcamy punkty z mapy wysokości na współrzędne X i Z wierzchołków tworzących powierzchnię terenu. Wysokość poszczególnych wierzchołków została obliczona podczas wczytywania mapy i przechowywana jest w tablicy float teren[ROZMIAR_X][ROZMIAR_Y]. W poprzedniej części warsztatu trójkąty budujące powierzchnię tworzone były przy użyciu wywołania glBegin(GL_TRIANGLES), tym razem używamy wywołania glBegin(GL_TRIANGLE_STRIP). Zamiast oddzielnych figur podawane są wierzchołki tworzące "pasek" trójkątów, przy czym pierwsze trzy wierzchołki tworzą pierwszy trójkąt, wierzchołki 2., 3., 4. tworzą kolejny, wierzchołki 3., 4., 5. kolejny itd. Stosując glBegin(GL_TRIANGLE_STRIP), zmniejszamy prawie trzykrotnie liczbę wywołań funkcji glVertex3f().

W tym miejscu należy przynajmniej pobieżnie przyjrzeć się sposobowi obliczania wektorów normalnych. W naszym przypadku wektor normalny wyznaczamy jako iloczyn wektorowy dwóch wektorów opisujących trójkąt.

Załóżmy, że trójkąt opisany jest wierzchołkami A, B i C. Wtedy określamy wektory v i w:

v = [ Bx-Ax, By-Ay, Bz-Az ]

w = [ Cx-Ax, Cy-Ay, Cz-Az ]

Wektor normalny n obliczamy jako v*w, czyli:

n = [ vy*wz-vz*wy, vz*wx-vx*wz, vx*wy-vy*wx ]

OpenGL korzysta z wektorów normalnych jednostkowych, czyli o długości równej 1. Obliczony wyżej wektor n należy jeszcze znormalizować, to znaczy każdą jego współrzędną podzielić przez długość wektora (oznaczmy ją jako d):

d = ÷(nx2+ny2+nz2)

n = [ nx/d, ny/d, nz/d ]

Zakończmy te matematyczne rozważania przedstawieniem funkcji tworzącej tablicę wektorów normalnych. Współrzędne x, y, wektorów przechowywane są w trzyelementowych tablicach, a więc np. wektor v ma współrzędne v[0], v[1], v[2]. Dla punktu A odpowiadającego punktowi [i,j] na mapie terenu punkt B to [i+1,j], a punkt C [i, j+1]. Wierzchołki trójkątów wygenerowane na podstawie mapy wysokości tworzą w poziomie regularną prostokątną siatkę, stąd uproszczone obliczanie wartości współrzędnych wektorów v i w.

for(int i=0; i<ROZMIAR_X-1; i++)

for(int j=0; j<ROZMIAR_Y-1; j++)

{

v[0] = skala_xy; // [Bx-Ax]

v[1] = teren[i+1][j]-teren[i][j]; // [By-Ay]

v[2] = 0; // [Bz-Az]

w[0] = 0; // [Cx-Ax]

w[1] = teren[i][j+1]-teren[i][j]; // [Cy-Ay]

w[2] = skala_xy; // [Cz-Az]

n[0] = v[1]*w[2]-v[2]*w[2];

n[1] = v[2]*w[0]-v[0]*w[2];

n[2] = v[1]*w[0]-v[0]*w[1];

d = sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);

normalne[i][j][0] = n[0]/d;

normalne[i][j][1] = n[1]/d;

normalne[i][j][2] = n[2]/d;

}

Mgła

Wizualny OpenGL (III)

Wzbogacenie sceny o efekt mgły zdecydowanie podnosi realizm grafiki.

Aby zwiększyć nieco realizm, zastosujemy prosty efekt pogodowy - mgłę. Tworzenie mgły w OpenGL nie jest trudne. Wszystkie funkcje związane z tworzeniem mgły wywołujemy tylko raz, w funkcji InicjujScene(). Parametry mgły można zmieniać w dowolnej chwili, uzyskując ciekawe efekty wizualne, ale obecnie nie będziemy korzystać z tej możliwości.

Najpierw należy uaktywnić mechanizm mgły za pomocą wywołania glEnable(GL_FOG). Następnie za pomocą funkcji glFogi() wybieramy sposób nakładania mgły na piksele: liniowy (parametr GL_LINEAR) lub wykładniczy (parametr GL_EXP lub GL_EXP2). Pozostaje jeszcze ustalić jej gęstość i kolor. Całość zabiegów związanych z utworzeniem mgły wygląda następująco:

float KolorMgly[] = {0.3, 0.4, 0.5, 1.0};

...

glEnable(GL_FOG); // aktywacja mgły

// sposób obliczania mgły - wykładniczy

glFogi(GL_FOG_MODE, GL_EXP);

glFogf(GL_FOG_DENSITY, 0.0002); // gęstość mgły

glFogfv(GL_FOG_COLOR, KolorMgly); // kolor mgły

Jak widać, generowanie mgły jest w OpenGL bardzo proste. W naszym programie zastosowana została dość gęsta mgła, mocno ograniczająca pole widzenia, dzięki czemu mniej razi niewielki obszar gry.

Mgła, której używamy w programie, nie jest bez wad. W wielu przypadkach można zauważyć nieprawidłowe obliczanie stopnia zamglenia, nie jest również możliwe określenie różnego poziomu mgły w różnych częściach sceny 3D (to ograniczenie można częściowo obejść, dynamicznie modyfikując ustawienia mgły podczas ruchu kamery). Znacznie większe możliwości daje tzw. mgła wolumetryczna, czyli objętościowa. Efekty jej wykorzystania można obejrzeć w wielu nowych grach 3D. OpenGL nie udostępnia prostych metod generowania mgły objętościowej i trzeba ją realizować samodzielnie, np. za pomocą efektów cząsteczkowych.

W kolejnej części warsztatów powiemy, jak wykorzystać dotychczasowe narzędzia i model terenu do przygotowania prostej gry FPP, wzbogaconej o dodatkowe obiekty i efekty graficzne.