Autor Wątek: Kamera przy pomocy kątów eulera.  (Przeczytany 3113 razy)

Offline DoS

  • Użytkownik
    • Projekt ORC

# Marzec 11, 2011, 00:42:02
Hej,

Tak w sumie problem, który tutaj opiszę mam od bardzo dawna, naprawdę bardzo dawna. Chodzi mi o napisanie własnej kamery 3d. Już właśnie kiedyś się za to zabrałem jeszcze za czasów D3D, a teraz identyczny problem mam w OpenGL ale tutaj akurat biblioteka nie gra roli, czysta matematyka.
  Tak więc rozrysowałem sobie wszystko na papierze (sinusy, cosinusy i wektory) i tam wszystko jest ok. Papier wszystko przyjmie ;)
  Oczywiście po zaimplementowaniu nie jest tak kolorowo. Dopóki się nie ruszam z miejsca kamerą to działa prawidłowo, wszystkie obroty itp działają prawidłowo. Wszystko się wali jak się tylko kawałek ruszę wtedy kamera obraca się już dziwnie.

  Wszystko oparłem na gluLookAt i kątach eulera. Wektor kierunku patrzenia i wektor wskazujący górę liczę z sinusów i kosinusów. Przemieszczenie liczę za pomocą pierwszego wektora i dodaje do pozycji. Do pozycji dodaję potem pierwszy i drugi i mam punkt, na który "patrzy" kamera i "górę". Wszystko jest w teorii ok, nie ma nic skomplikowanego. No poza małym szkopułem, że właśnie się nie sprawdza.

dir i top to wektory "jednostkowe" kierunku na których operuję. pos to pozycja kamery, cen to punkt na który kamera patrzy a up wskazuje gore
pierwszy kod liczy kąt na podstawie xrel i yrel które dostaje z SDL'a jako przesunięcie myszki po ekranie, a potem wylicza wektory kierunkowe
        rotx -= (double)xrel*PI/180.0*mouseSens;
roty -= (double)yrel*PI/180.0*mouseSens;

if(roty>PI/2)
roty=PI/2;
if(roty<-PI/2)
roty=-PI/2;

dirx = sin(rotx)*cos(roty);
diry = sin(roty);
dirz = cos(rotx)*cos(roty);

topx = sin(roty)*(-sin(rotx));
topy = cos(roty);
topz = sin(roty)*(-cos(rotx));
drugi na podstawie wektorów kierunkowych liczy przesunięcie
f - to int 1 to przód -1 to tył

        posx += dirx*sensitiv*_deltaT*(float)f;
posy += diry*sensitiv*_deltaT*(float)f;
posz += dirz*sensitiv*_deltaT*(float)f;

cenx = posx+dirx;
ceny = posy+diry;
cenz = posz+dirz;

upx = posx + topx;
upy = posy + topy;
upz = posz + topz;
Nic skomplikowanego, prawda? Ale właśnie tak: Póki się nie ruszyłem kamery to wszystkie obroty wykonują się prawidłowo. Jeśli przesunę się do przodu w kierunku patrzenia to jest ok, kamera porusza się w odpowiednim kierunku. Teraz jeśli znowu chcę poruszyć myszką żeby przekręcić kamerę to nie zachowuje się już ona tak jak powinna. Kręci się jak szalona w nieprzewidywalnych kierunkach. Jeśli po tym znowu będę chciał przesunąć kamerę w kierunku patrzenia to dalej przesuwa się prawidłowo.
Próbowałem to rozpisywać na kartce i zawsze działa. Próbowałem wypisywać te kąty na konsoli żeby zauważyć jakieś dziwności. Radziłem się kumpla jednego i drugiego co z tymi wzorami jest nie tak. No i dalej nie wiem. Jeśli ktoś ma pomysł (ba! na 100%) to byłbym wdzięczny za pomoc w doprowadzeniu tego ustrojstwa do porządku.


Dziękuję
Jacek

EDIT: Aha, za każdą nową translacją ustawiam glLoadIdentity()
« Ostatnia zmiana: Marzec 11, 2011, 00:55:46 wysłana przez DoS »

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Reg

  • Administrator
    • Adam Sawicki - Home Page

# Marzec 11, 2011, 15:00:17
Dopóki się nie ruszam z miejsca kamerą to działa prawidłowo, wszystkie obroty itp działają prawidłowo. Wszystko się wali jak się tylko kawałek ruszę wtedy kamera obraca się już dziwnie.
Spróbuj zamienić miejscami glRotate z glTranslate.

Offline DoS

  • Użytkownik
    • Projekt ORC

# Marzec 11, 2011, 16:07:14
Dopóki się nie ruszam z miejsca kamerą to działa prawidłowo, wszystkie obroty itp działają prawidłowo. Wszystko się wali jak się tylko kawałek ruszę wtedy kamera obraca się już dziwnie.
Spróbuj zamienić miejscami glRotate z glTranslate.
No niestety nie w tym problem, bo jak napisałem, korzystam z gluLookAt()


Offline Liosan

  • Redaktor

# Marzec 11, 2011, 16:20:03
upx = posx + topx;
upy = posy + topy;
upz = posz + topz;
Up to nie był znormalizowany wektor kierunku, niezależny od pozycji?

Liosan

Offline DoS

  • Użytkownik
    • Projekt ORC

# Marzec 11, 2011, 16:21:00
upx = posx + topx;
upy = posy + topy;
upz = posz + topz;
Up to nie był znormalizowany wektor kierunku, niezależny od pozycji?

Liosan

Nie, to był top : )

Offline Liosan

  • Redaktor

# Marzec 11, 2011, 16:23:34
A co przekazujesz do gluLookAt? Bo nie pokazałeś wywołania, więc uznałem że ostatnie trzy wyliczone punkty. Nazwy były w miarę pasujące :) A ja jestem przekonany, że gluLookAt chce znormalizowany kierunek...

Liosan

Offline Dab

  • Redaktor
    • blog

# Marzec 11, 2011, 16:54:38
Nie musisz liczyć wektora up. Wystarczy podać (0,1,0) jako stały wektor up.

Offline Oti

  • Użytkownik

# Marzec 11, 2011, 16:57:49
Nie musisz liczyć wektora up. Wystarczy podać (0,1,0) jako stały wektor up.
Rozumiem, że mówisz o globalnym wektorze UP świata, tak? Bo ten, który przekazujemy do gluLookAt trzeba policzyć.

Offline DoS

  • Użytkownik
    • Projekt ORC

# Marzec 11, 2011, 17:00:42
że gluLookAt chce znormalizowany kierunek...

ZABIJCIE MNIE! PROSZĘ, ZABIJCIE MNIE W TEJ CHWILI.

Nie miałem pojęcia, że potrzebuje znormalizowany. W dokumentacji jest najpierw, że pozycja, punkt na który patrzy i punkt który wskazuje górę więc..... a zresztą, po prostu jestem żałosny. :)

Dzięki wielkie, w tej chwili moje życie stało się lepsze.

Offline Dab

  • Redaktor
    • blog

# Marzec 11, 2011, 18:52:25
Nie musisz liczyć wektora up. Wystarczy podać (0,1,0) jako stały wektor up.
Rozumiem, że mówisz o globalnym wektorze UP świata, tak? Bo ten, który przekazujemy do gluLookAt trzeba policzyć.
Nie trzeba. gluLookAt najpierw liczy wektor right jako cross(front, up) a potem "prawdziwy" up jako cross(front, right).

Offline Oti

  • Użytkownik

# Marzec 11, 2011, 19:52:13
Nie musisz liczyć wektora up. Wystarczy podać (0,1,0) jako stały wektor up.
Rozumiem, że mówisz o globalnym wektorze UP świata, tak? Bo ten, który przekazujemy do gluLookAt trzeba policzyć.
Nie trzeba. gluLookAt najpierw liczy wektor right jako cross(front, up) a potem "prawdziwy" up jako cross(front, right).
A, no chyba, że tak. :) Ale kiedyś pisząc w którejś starej wersji ogl miałem problem przy patrzeniu równolegle do up podanego do gluLookAt. Potem, pisząc w DX miałem taki sam problem i udało mi się go rozwiązać właśnie przez policzenie up vectora z crossprodukctu i wywnioskowałem, że to dokładnie identyczne problemy. No ale może w jakiejś późniejszej wersji gluLookAt został usprawniony właśnie o liczenie up vectora, sam nie wiem. W takim razie sorry za offtop. :p

Offline Dab

  • Redaktor
    • blog

# Marzec 11, 2011, 20:02:24
Nie sądzę żeby działanie gluLookAt zmieniło się kiedykolwiek. Zawsze przyjmował dwa punkty (pozycja kamery i pozycja punktu na który patrzymy) + znormalizowany kierunek "góry", który w przypadku kamery FPP jest zwykle równy (0,1,0) (na podstawie dwóch kątów i tak nie ma możliwości jego wyliczenia). Patrząc w kierunku "up" natrafiasz na zjawisko zwane gimbal lock i jest to jak najbardziej "poprawne" zachowanie.

Offline voytech

  • Użytkownik

# Marzec 11, 2011, 21:31:06
@Dab: gimbal lock to chyba występuje przy rotacjach opartych o kątach eulera, np. jak posługujemy się trzeba kątami obrotu, to taki kod:
glRotatef(katx, 1, 0, 0);
glRotatef(katy, 0, 1, 0);
glRotatef(katz, 0, 0, 1);

powoduje, że przy pewnych kątach pojawi się to zjawisko. Oczywiście można kombinować z kolejnością tymi trzema linijkami kodu, żeby "odroczyć" blokadę obrotu ale ona ciągle jest.

W gluLookAt jest tylko taki wymóg, żeby 'UP' nie był równoległy do kierunku patrzenia, bo jak napisałeś, boczny wektor liczony jest iloczynu wektorowego, więc jak będą równoległe to długość wyniesie 0 i będzie dzielenie przez 0 (gluLookAt normalizuje je przed zbudowaniem macierzy) przez co program się wysypie, a w najlepszym przypadku macierz modelview nie będzie się wogóle nadawać do użytku. Problemem nie będzie więc gimbal lock tylko zwykłe dzielenie przez 0.

@DoS: lepiej jest kamerę samemu budować i trzymać cały czas trzy wektory (przód, góra i bok), a przy obrotach o daną oś przeliczać pozostałe dwie. Jak chcesz używać gluLookAt to w miejscu sprawdzania obrotu góra/dół trzeba sprawdzać czy kąt między kierunkiem patrzenia a wektorem "UP" nie jest mniejszy niż powiedzmy 5 stopni. Wtedy można zablokować dalszy obrót i uniknąć wysypania się gluLookAt.

Jak chcesz naprawdę patrzeć pionowo do góry to niestety trzeba odchylić wektor "UP", innego wyjścia nie ma. Poza tym jak będziesz przechowywać trzy wektory to od razu będziesz mógł przesuwać kamerę do przodu, na boki i w górę, łatwo będzie to policzyć.

Offline DoS

  • Użytkownik
    • Projekt ORC

# Marzec 11, 2011, 23:51:47
@DoS: lepiej jest kamerę samemu budować i trzymać cały czas trzy wektory (przód, góra i bok), a przy obrotach o daną oś przeliczać pozostałe dwie. Jak chcesz używać gluLookAt to w miejscu sprawdzania obrotu góra/dół trzeba sprawdzać czy kąt między kierunkiem patrzenia a wektorem "UP" nie jest mniejszy niż powiedzmy 5 stopni. Wtedy mA weź przebuduj ten "projekt" na którym to magicznie nie działało vbo. I jak chcesz porządne makefile to zajrzyj na wiki.ożna zablokować dalszy obrót i uniknąć wysypania się gluLookAt.

Jak chcesz naprawdę patrzeć pionowo do góry to niestety trzeba odchylić wektor "UP", innego wyjścia nie ma. Poza tym jak będziesz przechowywać trzy wektory to od razu będziesz mógł przesuwać kamerę do przodu, na boki i w górę, łatwo będzie to policzyć.

Nie no, oczywiście. Ja tutaj akurat nie mam z tym żadnego problemu. Up liczę sobie na bieżąco za pomocą wzoru:
upx = sin(roty)*(-sin(rotx));
upy = cos(roty);
upz = sin(roty)*(-cos(rotx));
Gdzie rotx to poruszenie się myszki na boki a roty to przód/tył.
Mnie tam gimbal lock nie martwi i nie przeszkadza. Jeśli kiedyś będę chciał zrobić grę osadzoną w kosmosie to skorzystam z kwaternionów  ale póki nie muszę to odroczę nauczenie się ich :)

Moim jedynym problemem było to, że myślałem, iż wektor up ma wskazywać tzw górę ale od aktualnej pozycji kamery. Czyli nic dziwnego, że jak się ruszyłem to fiksowało.

Offline Avaj

  • Użytkownik

# Marzec 12, 2011, 10:30:30
O ile się nie mylę, to gimbal lock polega na tym, że mamy osie obrotu w układzie świata a nie kamery, przez co np. w fpsie jak spojrzymy w górę to bardzo ogranicza się nam możliwość obrotu i kręcimy się "dziwnie".

Jeśli chodzi o mnie to ja proponuję takie rozwiązanie:

generalnie jak się robi kamerę jak statek kosmiczny albo samolot to lepiej użyć kwaternionów (albo axis-angle, ale trudniej się używa). Do FPSa lepiej trzymać yaw i pitch i z nich co klatkę budować od zera macierz widoku (przemnożyć je przez siebie i przemnożyć wynikową przez wektor 0,0,-1 i wstawić do look at najprostsze rozwiązanie).