Lekcja 4. Obroty
Autor: Marcin 'Aklimx' Milewski
Oryginał: Rotation (Jeff 'NeHe' Molofee)
Źródła: http://nehe.gamedev.net/data/lessons/vc/lesson04.zip

Podczas ostatniej lekcji powiedziałem Ci jak dodawać kolory do trójkątów czy prostokątów. Na tej lekcji dowiesz się jak obracać te kolorowe obiekty.

Wykorzystamy kod z poprzedniej lekcji, będziemy go rozszerzać w pewnych miejscach. Przepiszę całe fragmenty kodu, żebyś łatwo dostrzegł co dodałem, a co uległo zmianie.

Zaczniemy od dodania dwóch zmiennych śledzących obrót każdego z elementów. Robimy to na początku naszego programu, pod innymi zmiennymi.Zapewne zauważyłeś dwie nowe linie po 'bool fullscreen=TRUE;'. Są to dwie zmienne typu zmiennoprzecinkowego, których użyjemy do obracania obiektów z bardzo dużą dokładnością. Typ zmiennoprzecinkowy dopuszcza liczby z przecinkiem. Oznacza to że nie musimy używać wartości 1, 2, 3 dla kątów. Możemy na przykład użyć 1.1, 1.7, 2.3 lub nawet 1,015 dla większej precycji. Przekonasz się, że liczby zmiennoprzecinkowe są niezbędne do programowania w OpenGL. Nowe zmienne zostały nazwane rtri (obrót trójkąta) oraz rquad (obrót prostokąta).

#include <windows.h>         // Nagłówek Windows
#include <gl\gl.h>         // Nagłówek OpenGL32
#include <gl\glu.h>         // Nagłówek GLu32
#include <gl\glaux.h>         // Nagłówek GLaux

HDC hDC=NULL;         // Prywatny kontekst użądzenia GDI
HGLRC hRC=NULL;         // Kontekst rysujący
HWND hWnd=NULL;         // Uchwyt naszego okna
HINSTANCE hInstance;         // Instancja aplikacji

bool keys[256];         // Tablica klawiszy - wciśnięty czy nie
bool active=TRUE;         // Flaga - czy okno jest aktywne?
bool fullscreen=TRUE;         // Uruchom aplikacje na pełnym ekranie

GLfloat rtri;         // Kąt obrotu trójkąta ( NOWE )
GLfloat rquad;         // Kąt obroty czworokąta ( NOWE )                                 

Teraz zmodyfikujmy kod DrawGLScene(). Przepiszę całą procedurę. Powinno to ułatwić Ci dostrzeżenie zmian jakie zaszły. Wyjaśnię dlaczego kod się zmienił i co właściwie robią nowe linijki. Następny fragment kodu jest dokładnie taki sam jak w poprzednij lekcji.

int DrawGLScene(GLvoid)         // Tutaj wszystko rysujemy
{
        // Wyczyść ekran i bufor głębi
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();         // Zresetuj widok
    glTranslatef(-1.5f,0.0f,-6.0f);         // Przesuń w ekran (oddalenie) i w lewo                                

Następna linijka kodu jest nowa. glRotatef(Angle, wektorX, wektorY, wektorZ) odpowiada za obracanie obiektu wokół osi. To niezwykle użyteczne polecenie. Angle (kąt) jest pewną liczbą, która pokazuje o ile ma się obrócić obiekt. Parametry wektorX, wektorY, i wektorZ reprezentują składowe wektora, wokół którego nastąpi obrót. Jeśli wpiszesz (1,0,0) to opisujesz wektor o długości 1 w kierunku osi x (czyli w prawo). Wartości (-1,0,0) opisują wektor o długości 1 wzdłuż ujemnej części osi x (czyli w lewo).

D. Michael Traub wyjaśnia, dla lepszego zrozumienia obrotów:

Oś X - pracujesz przy pile stołowej. Kratka przechodząca przez środek ostrza obraca się z lewej na prawą (jak oś x w OpenGL). Ostre zęby obracają się wokół osi x(blokada przechodząca przez środek ostrza), i tną do lub od ciebie (zależy z której strony ostrza się znajdujesz). Kiedy obracamy coś wokół osi x w OpenGL, to obraca się tak samo.

Oś Y - Wyobraź sobie, że stoisz na środku pola. Przed tobą jest duże tornado. Jego środek przemieszcza się od nieba ku ziemi (góra i dół, tak jak oś Y w OpenGL). Brud i szczątku w tornadzie obracają się wokół jego środka (wokół osi Y) z lewej na prawą lub odwrotnie. Kiedy obracasz coś wokół osi Y w OpenGL, to obraca się tak samo.

Oś Z - Patrzysz na wiatrak od przodu. Jego środek jest wycelowany w Ciebie (tak jak oś Z w OpenGL). Płaty wiatraka obracają się wokół jego środka zgodnie z ruchem wskazówek zegara lub przeciwnie do niego. Kiedy obracasz coś wokół osi Z w OpenGL, to obraca się tak samo.

Dobra, w następnej linijeczce kodu, gdyby rtri było równe 7, to obrócilibyśmy obiekt o 7 wokół osi Y (z lewej na prawą). Poeksperymentuj z wartościami.

Muszę jeszcze dodać, że obroty są wykonywane w stopniach (nie np. w radianach). Gdyby rtri miało wartość 10, obrócilibyśmy obiekt o 10 stopni wokół osi Y.

    glRotatef(rtri,0.0f,1.0f,0.0f);         // Obróć trójkąt ( NOWE )

Następny kawałek kodu nie uległ zmianie. Rysuje on kolorowy trójkąt w lewej części ekranu, który obraca się wokół osi Y (z lewej na prawą).

    glBegin(GL_TRIANGLES);         // Zacznij rysowanie trójkąta
         glColor3f(1.0f,0.0f,0.0f);         // Kolor górnego punktu ustaw na czerwony
         glVertex3f( 0.0f, 1.0f, 0.0f);         // Pierwszy punkt trójkąta
         glColor3f(0.0f,1.0f,0.0f);         // Kolor lewego punktu ustaw na zielony
         glVertex3f(-1.0f,-1.0f, 0.0f);         // Drugi punkt trójkąta
         glColor3f(0.0f,0.0f,1.0f);         // Kolor prawego punktu ustaw na niebieski
         glVertex3f( 1.0f,-1.0f, 0.0f);         // Trzeci punkt trójkąta
    glEnd();         // Zakończ rysowanie trójkąta

W poniższym kodzie zauważysz, że dodaliśmy jeszcze jedno polecenie glLoadIdentity(). Robimy tak, gdyż chcemy zresetować widok. Co gdybyśmy tego nie zrobili? Przesuwając obiekt po tym jak został obrócony, otrzymalibyśmy nieoczekiwane wyniki. Ponieważ oś została obrócona, może nie wskazywać w kierunku o którym myślimy. Więc, jeżeli przesuniemy oś X w lewo, możemy zobaczyć, że nasz obiekt przesunął się np. w górę (zależy o jaki kąt wcześniej obróciliśmy). Spróbuj zakomentować polecenie glLoadIdentity(), to zobaczysz o czym mówie.

Kiedy scena została zresetowana, oś X biegnie z lewej na prawą, oś Y z dołu do góry, a oś Z przez ekran. Zauważysz, że przesuwamy się tylko 1.5 w prawo zamiast 3.0 - tak jak w poprzedniej lekcji. Kiedy zresetujemy widok, jesteśmy znów na środku ekranu. Więc, żeby być 1.5 na prawo, nie musimy przesuwać się o 3.0, ale o 1.5, bo przesuwamy się od środka, nie od poprzedniej pozycji!

Kiedy już przesuneliśmy się do nowej pozycji (w prawą część ekranu), obracamy czworokąt wokół osi X. Spowoduje to obrót czworokąta góra-dół.

    glLoadIdentity();         // Zresetuj macierz modelowania
    glTranslatef(1.5f,0.0f,-6.0f);         // Przesuń 1.5 jednostki w prawo i 6.0j w ekran
    glRotatef(rquad,1.0f,0.0f,0.0f);         // Obróć czworokąt ( NOWE )

Ten kod rysuje niebieski czworokąt w prawej części ekranu w obróconej pozycji.

    
    glColor3f(0.5f,0.5f,1.0f);         // Niebieski kolor
    glBegin(GL_QUADS);         // Zacznij rysowanie czworokąta
        glVertex3f(-1.0f, 1.0f, 0.0f);         // Lewy Górny wierzchołek
        glVertex3f( 1.0f, 1.0f, 0.0f);         // Prawy Górny wierzchołek
        glVertex3f( 1.0f,-1.0f, 0.0f);         // Prawy Dolny wierzchołek
        glVertex3f(-1.0f,-1.0f, 0.0f);         // Lewy Dolny wierzchołek
    glEnd();         // Zakończ rysowanie czworokąta

Kolejne dwie nowe linie. Pomyśl o rtri i o rquad jako o pojemnikach. Kiedy je tworzymy (GLfloat rtri, GLfloat rquad), są puste. Pierwsza linia poniżej dodaje 0.2 to tego kontenera. Pojemnik rquad zmniejsza się o 0.15, co powoduje że obiekt będzie się obracał w przeciwnym kierunku.

Spróbuj pozmieniać znaki, a zabaczysz jak zmienia się kierunek obrotu. Żeby obiekt obracał się szybciej zmień 0.2 na 1.0 Im większa wartość tym obiekt szybciej będzie się obraca!

    rtri+=0.2f;         // Zwiększ kąt obrotu trójkąta ( NOWE )
    rquad-=0.15f;         // Zmniejsz kąta obrotu czworokąta ( NOWE )
    return TRUE;         // Wszytko poszło ok
}

Na zakończenie zmodyfikuj kod zmieniający tryb wyświetlania, żeby zawierał odpowiedni nagłówek.

            if (keys[VK_F1])         // czy nasićnięto F1 ?
            {
                keys[VK_F1]=FALSE;         // Jeżeli tak to zwolnij klawisz
                KillGLWindow();         // Zabij nasze okno
                fullscreen=!fullscreen;         // Zmień tryb wyświetlania okna
        // Utwórz ponownie okno dla OpenGL ( ZMIENIONE )
                if (!CreateGLWindow("NeHe Tutorial::04 Obroty", 640, 480, 16, fullscreen))
                {
                    return 0;         // Wyjdź jeżeli okno nie zostało utworzone
                }
            }

Podczas tej lekcji, starałem się wyjaśnić tyle szczegółów ile tylko dało radę. Wiesz już jak obracać obiekty wokół osi, poeksperymentuj trochę z kodem - dobre zrozumienie tej lekcji jest bardzo ważne. Jeżeli masz jakiś komentarz pisz do mnie. Daj znać, jeżeli coś źle wyjaśniłem lub możnabyło lepiej. Chcę tworzyć tak dobre kursy jak to tylko możliwe...