Grafika 3D
 
  Zarejestruj się
::  Newsy  ::  Pliki  ::  Twoje Konto  ::  Forum  ::
Menu
· Strona główna
· Forum
· Linki
· Lista u?ytkowników
· O nas...
· Pliki
· Statystyki
· Twoje Konto
Tutoriale
· API
· Matematyka
· Teoria
· Direct3D
· OpenGL
· Techniki
Kto Jest Online
Aktualnie jest 46 gość(ci) i 0 użytkownik(ów) online.

Jesteś anonimowym użytkownikiem. Możesz się zarejestrować za darmo klikając tutaj
Tutoriale - OpenGL - Macierze

Witam wszystkich maniaków 3D - zarówno tych bardziej, jak i mniej maniakalnych. Jeśli czytacie te słowa i nie jesteście Robalem (Robalami ???) to znaczy, że artykuł przeszedł jego surową :) kontrolę i nadaje się do zamieszczenia na stronie. Mam tylko jedną prośbę - zważcie na to, iż jest to mój pierwszy w życiu artykuł, więc jeśli nie będziecie mogli czegoś zupełnie zrozumieć, to się nie denerwujcie, tylko piszcie domino1011@poczta.fm - może kiedyś będziecie mogli znaleźć ten adres na stronie głównej, ale to na razie przyszłe dzieje ;).
No więc dobrze. Dzisiaj spróbuję wam wytłumaczyć, na czym polega praca z macierzami w OpenGL. Tak po prawdzie, to część tłumaczyć zaczął już Robal w poprzednim arcie, ale ja jednak przypomnę przynajmniej część co, jak i którędy. Z rzeczy technicznych to wspomnę chyba tylko o tym, że będziemy nadal posługiwać się rzutem prostokątnym. Do rzutu perspektywicznego przejdziemy później - omawiając bryły, ale nie przejmujcie się bo przecież macierze przekształceń są zawsze takie same :).

Po pierwsze macierze przekształceń w OpenGL są zdefiniowane wewnątrz tego API, co uwalnia nas od definiowania odpowiednich zmiennych i wypełniania ich danymi. Owszem możemy przygotować własne i ich wartości przypisać do tamtych, więc jeśli ktoś woli definiować tablice 4*4 samemu to proszę bardzo ;)), myślę jednak że użycie funkcji jest dużo prostsze (a w dodatku wg dokumentacji szybsze).

Do operacji na macierzach służy kilka funkcji. Opiszę je w miarę omawiania kodu. Używam szablonu Robala, ale będę go trochę modyfikował w miarę jak będzie przybywać nowych rzeczy (zawsze będę mówić co i gdzie dodajemy).
Dodajemy jedną globalną funkcję o nazwie InitGLWindow(), której zadaniem jest ustalenie bryły widzenia i rodzaju rzutowania, jak również inicjalizacja całego OpenGL'a. Obecnie znajdują się w niej znane już funkcje gl-owskie, jednak gwoli przypomnienia wypisuję je tu wszystkie wraz z pełnioną funkcją.

glViewport(0, 0, 500, 500);

Czyli ustalenie obszaru rysowania dla OpenGL'a w obszarze klienta - u nas jest to cały obszar klienta.

glMatrixMode(GL_PROJECTION);

Ustawia bieżącą macierz na macierz rzutowania.

glLoadIdentity();

Zeruje bieżącą macierz (w tym wypadku macierz projekcji), czyli zastępuje ją macierzą jednostkową.

gluOrtho2D(-2.0f, 2.0f, -2.0f, 2.0f);

Czyli ustalenie rzutowania prostokątnego i ustalenie bryły obcinania. Definiuje płaszczyzny obcinające w kolejności lewa, prawa, dół, góra.

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

Ustawia bieżącą macierz na macierz widoku modelu i ją zeruje. Naszą funkcję inicjującą dorzucamy na końcu obsługi komunikatu WM_CREATE (zaraz przed break; ).
Mówiłem na początku, że miłośnicy definiowania własnych macierzy również mogą czuć się zadowoleni, ponieważ OpenGL dostarcza kilka funkcji służących do operacji na macierzach. Są to dwie funkcje, które w zupełności wystarczają do zrobienia wszelkich przekształceń :

glLoadMatrixf(const GLfloat* m);

Funkcja ta zastępuje obecną macierz (ustaloną za pomocą funkcji glMatrixMode()) macierzą podaną jako parametr np. zamiast pisać glLoadIdentity() piszemy :
float m[] =
{
    1.0f, 0.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 1.0f
};

glLoadMatrixf(m);
Prawda jakie to proste ;). Nie muszę chyba przypominać, że większość funkcji OpenGL'a ma kilka swoich odpowiedników różniących się parametrami wywołania, a co za tym idzie końcówką świadczącą właśnie o typie tych parametrów. Czyli mówiąc jaśniej funkcja np. glLoadMatrixf( m ) ma swój odpowiednik w postaci funkcji glLoadMatrixd(m) (która jako parametr przyjmuje wskaźnik na double).

glMultMatrix(const GLfloat* m);

Ta jest bardzo podobna, tylko że zamiast zastępować obecną macierz, mnoży ją przez macierz podaną jako parametr.
Ok. Teraz sedno tego artykułu, czyli rotacja, translacja i skala. Zaglądnijmy więc w głąb naszej funkcji renderującej. Widzimy tam kilka wywołań nowych funkcji gl-owskich. Pierwszą z nich jest glPushMatrix(), ale jej działanie omówię za chwilę. Teraz zajmijmy się następnymi funkcjami:

glTranslatef(-1.0f, 0.0f, 0.0f);

Funkcja ta dokonuje translacji (przesunięcia o wektor jakby ktoś nie wiedział :))). Jej parametry, w kolejności, to przesunięcie wzdłuż osi X, przesunięcie wzdłuż osi Y i przesunięcie wzdłuż osi Z. Tu funkcja przesuwa obiekt, a właściwie układ współrzędnych widoku modelu (GL_MODELVIEW), o 1 w prawo (czyli w dół osi X) względem układu współrzędnych obserwatora. Jak więc widać nie trzeba robić żadnych żmudnych i skomplikowanych obliczeń na macierzach, tylko wystarczy po prostu wywołać jedną funkcję (czyż to nie piękne ?!).

glRotatef(timeGetTime()/15.0f, 0.0f, 1.0f, 0.0f);

Jak zapewne nie trudno się domyślić zadaniem tej funkcji jest dokonanie rotacji względem pewnej, określonej osi. Os taka definiuje się w OpenGL w sposób następujący - punkt podany jako parametry funkcji łączymy ze środkiem układu współrzędnych tworząc wektor (lub odcinek jak kto woli). Nasza osią obrotu będzie w tym momencie prosta zawierająca ten właśnie odcinek. Łatwo zauważyć, ze jeśli zechcemy sobie obrócić nasz obiekt wokół jednej osi układu współrzędnych wystarczy jako parametr podać punkt położony na niej (najlepiej ten, definiujący wersor osi). Nie jest to składanie obrotów - trzeba to sobie uświadomić. To obrót wokół jednej, ściśle określonej osi. Jeśli chcemy poskładać sobie jakieś obroty to tradycyjnie musimy sobie poprzemnażać nasze macierze, które wcześniej musimy poobliczać.
Oczywiście maniacy tradycyjnych macierzy mogą dokonać przesunięcia i obrotu w stary dobry sposób używając do tego funkcji glMultMatrix() (nie będę pokazywał tutaj jak to się robi, bo sprowadza się to do zdefiniowania odpowiedniej tablicy, a to nie jest sednem tego artykułu - zainteresowanych przekształceniami macierzy i matematyką z tym związaną odsyłam do działu "Matematyka").

Dobrze, to teraz, kiedy już wiemy jak obracać i przesuwać obiekty mogę wytłumaczyć do czego służy funkcja glPushMatrix(), a właściwie para glPushMatrix()/glPopMatrix(). Otóż za pomocą funkcji glTranslatef() i glRotatef() dokonujemy przekształceń naszego układu, w którym umieszczone są wszystkie obiekty. Tak więc, kiedy raz wywołamy glTranslate(), czy glRotatef(), to układ ten zostanie przesunięty w nowe miejsce (o wektor, czy też kąt, jaki podaliśmy). Wszystko jest OK póki mamy jeden obiekt (jedna para glBegin()/glEnd()) problem pojawia się kiedy mamy więcej obiektów. Dlaczego ?? Otóż przyjmijmy, że środek ekranu jest środkiem naszego układu współrzędnych (nie zawsze tak musi być). Kiedy przesuwamy nasz trójkąt o 1 jednostkę w dół osi X to po wyrenderowaniu zauważymy oczywiście, że obiekt jest bliżej prawej strony okna. Teraz załóżmy, że kwadrat chcemy mieć dokładnie po drugiej stronie, czyli o jedną jednostkę w lewo (od środka). Wywołując funkcję glTranslatef(1.0f, 0.0f, 0.0f) zauważymy, że efekt nie jest zadowalający - kwadrat jest dokładnie w środku okna a nie z jego lewej strony. Dlaczego tak się dzieje ? Otóż wcześniej (przesuwając trójkąt) ustaliliśmy nowy układ współrzędnych (a w zasadzie to po prostu przemnożyliśmy macierz widoku modelu przez odpowiednie wartości), którego środek znajduje się w punkcie (-1.0f, 0.0f, 0.0f) starego (normalnego) układu. Aby przesunąć kwadrat tam gdzie chcemy możemy zrobić dwie rzeczy - albo przed wywołaniem glTranslatef() dla kwadratu wywołać glLoadIdentity() - spowoduje to przywrócenie normalnego układu współrzędnych, albo też przed jakimikolwiek przekształceniami wywołać glPushMatrix(), a na końcu glPopMatrix(). Pierwsza funkcja służy do zapamiętywania stanu macierzy (poprzez odłożenie go na stos), a druga przywraca ten zapamiętany stan ściągając go równocześnie ze stosu. Który sposób jest lepszy ?? W tym przypadku chyba łatwiej by było wywołać dwa razy glLoadIdentity(), ale odkładanie macierzy ma swoje wielkie zalety, których niestety nie doświadczymy w tak prostych przykładach. Spróbuję, chociaż mam poważne wątpliwości z jakim skutkiem, pokazać to na przykładzie. Załóżmy, że chcemy, aby kwadrat obracał się wokół osi X, ale jednocześnie obracał się wokół trójkąta (jak Księżyc wokół Ziemi), natomiast sam trójkąt też obracał by się wokół jakiegoś punktu (przyjmijmy, że wokół Słońca). Wywołując glLoadIdentity() musielibyśmy dwa razy przesuwać układ - raz dla trójkąta raz dla kwadratu i później dokonywać odpowiednich, dodatkowych przekształceń. Przy większej ilości obiektów byłaby to straszna strata czasu (a jak wiemy w grafice 3D czas jest wszystkim). Tak więc łatwiej jest wywołać kilka razy glPushMatrix() i glPopMatrix() niż wykonywać żmudne przekształcenia po kilka razy. Tym bardziej, że funkcje te można zagnieżdżać. Może ten fragment wydaje się wam niezrozumiały, ale ciężko jest to wytłumaczyć na dwu-wymiarowej kartce :))))). Tak więc zapewne wrócę jeszcze do tego tematu.

glScalef(0.8f, 0.8f, 0.8f)

Oczywiście jest to funkcja skalująca obiekt we wszystkich trzech osiach. Oczywiście każdy z parametrów (oś X, oś Y, oś Z) może przyjmować inną wartość i co jest chyba jeszcze bardziej oczywiste spowoduje to, iż obiekt będzie miał różne wymiary na poszczególnych osiach - czyli z sześcianu możemy łatwo zrobić prostopadłościan.
Na końcu funkcji Render() widać, że zmieniam kąty obrotu trójkąta i kwadratu - dzięki temu cały czas się obracają :).

Myślę, że coś się wam wyjaśniło jeśli chodzi o macierze w OpenGL. Jeśli moja współpraca z Robalem się ułoży, to możecie liczyć na kolejne arty - jakie? Jeszcze nie wiem, ale się obaczy co tam jest dalej. Tak więc żegnam i mam nadzieje, że jeszcze się zobaczymy.

Zamieszczam również screen pokazujący (no może nie do końca :))) działanie programu.



Kod źródłowy

©Copyright by Domino   



Tutoriale - OpenGL
Nasze newsy s� w RSS: backend.php
PHP-Nuke Copyright © 2005 by Francisco Burzi. This is free software, and you may redistribute it under the GPL. PHP-Nuke comes with absolutely no warranty, for details, see the license.
Tworzenie strony: 0.06 sekund

:: Layout strony został stworzony przez www.nukemods.com w oparciu o styl phpbb2 Helius, którego autorem jest Cyberalien ::