Lekcja 11. Efekt flagi (falująca tekstura)
Autor: Marcin 'Aklimx' Milewski
Oryginał: Flag Effect (Waving Texture) (Bosco (bosco4@home.com))
Źródła: http://nehe.gamedev.net/data/lessons/vc/lesson11.zip

Witam wszystkich. Ci którzy już teraz chcą wiedzieć co będziemy robić polecam moje demo Worthless! Jestem bosco i dam z siebie wszystko, żeby nauczyć was jak zrobić animowany obrazek. Ta lekcja bazuje na lekcji 6. i właśnie wiedzę tam zawartą powinieneś posiadać. Ściągnij źródła tamtej lekcji i umieść teksturę, którą dołączam w katalogu z kodem. Możesz też użyć własnej tekstury, o ile spełnia ona wymogi OpenGL

Zacznijmy od początku. Otwórz lekcję 6. w Visual C++ i dodaj poniższą linię pod innymi liniami include. #include <math.h> pozwala nam używać takich rzeczy jak sinus czy kosinus

#include <math.h>         // Do funkcji sin()

Użyjemy tablicy punktów do przechowania współrzędnych punktów siatki. Siatka ma wymiary 45x45 punktów, czyli 44x44 czworokąty. wiggle_count śledzi prędkość falowania tekstury. Każda klatka wygląda całkiem nieźle, a zmienna przechowuje wartość zmiennoprzecinkową, żeby wygładzić falę na fladze. Te linie dodaj pod ostatnim include'em i przed GLuint texture[1]

float points[ 45 ][ 45 ][3];         // Siatka punktów naszej flagi
int wiggle_count = 0;         // Kontroluje szybkość falowania
GLfloat hold;         // Zmienna tymczasowa typu zmiennoprzecinkowego

Przejdź do procedury LoadGLTextures(). Nazwiemy naszą teksturę Tim.bmp. Znajdź LoadBMP("Data/NeHe.bmp") i zamień na LoadBMP("Data/Tim.bmp")

    if (TextureImage[0]=LoadBMP("Data/Tim.bmp"))         // Ładuj bitmapę

Następne dwie linie dodaj do InitGL() przed return TRUE.

    glPolygonMode( GL_BACK, GL_FILL );         // Tylna ściana jest wypełniona
    glPolygonMode( GL_FRONT, GL_LINE );         // Przednia jest rysowana liniami

Oznaczają one, że chcemy mieć tylną ścianę wypełnioną całkowicie, a przednią tylko za pomocą linii. Te ustawienia zależą w większości od własnych upodobań - oriantacja wierzchołków clockwise czy counterclockwise. Więcej informacji w 'Red Book'. Pozwól, że powiem, iż to przewodnia pozycja do nauki OpenGL nie włączając strony NeHe! [od tłumacza= w Polsce taką pozycją jest OpenGL.Księga Eksperta] Wróćmy do lekcji. Za powyższym kodem i przez return TRUE dopisz następujące linie.

        // Pętla przez płaszczyznę X
    for(int x=0; x<45; x++)
    {
        // Pętla przez płaszczyznę Y
        for(int y=0; y<45; y++)
        {
        // Nałóż falę na naszą siatkę
            points[x][y][0]=float((x/5.0f)-4.5f);
            points[x][y][1]=float((y/5.0f)-4.5f);
            points[x][y][2]=float(sin((((x/5.0f)*40.0f)/360.0f)*3.141592654*2.0f));
        }
    }

Dziękuję Grahamowi Gibbons'owi za sugestię odnośnie pętli integer.

Te dwie powyższe pętle inicjują punkty na siatce. Inicjuję zmienne w pętli, żeby były lokalne - dostępne tylko w bloku pętli. Nie jestem pewien czy to koszerne rozwiązanie. Używamy wartości integer, żeby zapobiec dziwnym rzeczom, które pojawiają się kiedy liczymy na liczbach zmiennoprzecinkowych (ang. float). Dzielimy x oraz y przez 5 (np. 45/5=9) i odejmujemy 4.5 od każdej, żeby wyśrodkować falę. To samo można by osiągnąć przez translację, ale ja wolę taki sposób.

Wartość w points[x][y][2] jest wartością naszej fali. Funkcja sin() wymaga podania radianów. Bierzemy wartość w stopniach, która jest naszą float_x pomnożoną przez 40.0f. Kiedy to zrobiliśmy, żeby przekonwertować na radiany bierzemy wartość w stopniach, dzielimy ją przez 360.0f, mnożymy przez PI (albo przybliżenie) i wtedy mnożymy przez 2.0f.

Przepiszę funkcję DrawGLScene od zera, żeby wszystko było jasne.

int DrawGLScene(GLvoid)         // Narysuj scenę
{
    int x, y;         // Zmienne w pętli
        // Użyte do podzielenia flagi na małe czworokąty
    float float_x, float_y, float_xb, float_yb;

Rożne zmienne wykorzystane to kontrolowania pętli. Zobacz kolejny fragment kodu.

        // Wyczyść ekran i bufor głębi
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();         // Zresetuj aktualną macierz
    glTranslatef(0.0f,0.0f,-12.0f);         // Oddal o 12 jednostek
    glRotatef(xrot,1.0f,0.0f,0.0f);         // Obróć wokół osi X
    glRotatef(yrot,0.0f,1.0f,0.0f);         // Obróć wokół osi Y
    glRotatef(zrot,0.0f,0.0f,1.0f);         // Obróć wokół osi Z
    glBindTexture(GL_TEXTURE_2D, texture[0]);         // Wybierz teksturę

Widziałeś to już wcześniej. Kod dokładnie taki jak w lekcji 6., jednak odsunąłem scenę nieco bardziej.

    glBegin(GL_QUADS);         // Rysuj czworokąty
    for( x = 0; x < 44; x++ )         // Pętla przez płaszczyznę X (44 punkty)
    {
        for( y = 0; y < 44; y++ )         // Pętla przez płaszczyznę Y (44 punkty)
        {

Jednak zacznijmy pętlę rysującą wielokąty. Użyłem wartości integer, żeby nie musieć używać funkcji int() jak zrobiłem wcześniej, żeby dostać referencję na tablicę jako integer.

            float_x = float(x)/44.0f;         // Stwórz zmienną typu float wskazującą na X
            float_y = float(y)/44.0f;         // Stwórz zmienną typu float wskazującą na Y
            float_xb = float(x+1)/44.0f;         // Stwórz zmienną typu float wskazującą na X+0.0227f
            float_yb = float(y+1)/44.0f;         // Stwórz zmienną typu float wskazującą na Y+0.0227f

Używam czterech zmiennych jako współrzędnych tekstury. Każdy wielokąt (czworokąt w siatce) ma wymiary 1/44 (na osi X) x 1/44 (na osi Y) teksturze. Pętla określa lewy dolny wierzchołek, potem odpowiednio dodajemy, żeby otrzymać pozostałe trzy.

            glTexCoord2f( float_x, float_y);         // Pierwsza współrzędna tekstury (Lewa dolna)
            glVertex3f( points[x][y][0], points[x][y][1], points[x][y][2] );
            glTexCoord2f( float_x, float_yb );         // Druga współrzędna tekstury (Lewa górna)
            glVertex3f( points[x][y+1][0], points[x][y+1][1], points[x][y+1][2] );
            glTexCoord2f( float_xb, float_yb );         // Trzecia współrzędna tekstury (Prawa górna)
            glVertex3f( points[x+1][y+1][0], points[x+1][y+1][1], points[x+1][y+1][2] );
            glTexCoord2f( float_xb, float_y );         // Czwarta współrzędna tekstury (Prawa dolna)
            glVertex3f( points[x+1][y][0], points[x+1][y][1], points[x+1][y][2] );
        }
    }
    glEnd();         // Zakończ rysowanie czworokątów

Powyższa linia każe OpenGL przejść przez wszystkie czworokąty flagi. Cztery oddzielne komórki - każda glTexCoord2f() i glVertex3f(). Przejdźmy dalej. Zauważ, że czworokąty są rysowane zgodnie z ruchem wskazówek zegara (ang. clockwise). To znaczy, że ściana, na którą patrzysz jest tylną, a nie przednią. Tylna, jak pamiętasz, jest wypełniona. Przednia zrobiona z linii.

Jeśli rysowałeś w odwrotnym porządku (ang. counterclockwise) to zamiast wypełnionych czworokątów ujrzysz siatkę :)

    if( wiggle_count == 2 )         // Zwolnij falę (raz na dwie klatki)
    {

Jeżeli narysowaliśmy naszą scenę, to przetwarzamy jeden cykl fali

        for( y = 0; y < 45; y++ )         // Przejdź przez płaszczyznę Y
        {
            hold=points[0][y][2];         // Przechowuj aktualną wartość fali (z lewej strony)
            for( x = 0; x < 44; x++)         // Przejdź przez płaszczyznę X
            {
        // Aktualna wartość fali po prawej stronie
                points[x][y][2] = points[x+1][y][2];
            }
            points[44][y][2]=hold;         // Ostatnia wartość staje się pierwszą
        }
        wiggle_count = 0;         // Ustaw licznik na zero
    }
    wiggle_count++;         // Zwiększ licznik

Oto co tutaj robimy. Wartość każdej linie przesuwamy w lewo, co powoduje falowanie. Przechowujemy skrajną wartość, żeby zachować falę. Następnie resetujemy licznik wiggle_counter, żeby nasza animacja wciąż trwała.

    xrot+=0.3f;         // Zwiększ obrót na osi X
    yrot+=0.2f;         // Zwiększ obrót na osi Y
    zrot+=0.4f;         // Zwiększ obrót na osi Z

    return TRUE;         // Zakończ procedurę
}

Standardowe wartość obrotów. I wszystko gra. Skompiluj, powinieneś ujrzeć obracającą się falującą bitmapę. Nie wiem co jeszcze powiedzieć, whew... to było DŁUGIE! Ale mam nadzieję, że coś wynieśliście z tej lekcji. Jeśli masz jakieś pytania, chcesz żebym coś wyjaśnił, albo powiedzieć mi jak okropnie kodzę, lol, napisz do mnie.

To był czad, ale bardzo energo- i czasochłonny. Teraz doceniam NeHe bardziej niż dotychczas. Dziękuję wszystkim.