Autor Wątek: Wyliczenie kątów YAW i PITCH z wektora  (Przeczytany 1842 razy)

Offline Chimerian

  • Użytkownik
    • Profil na Warsztat.GD

# Maj 22, 2013, 14:33:59
Witam. Od pewnego czasu pracuję nad wyliczaniem kątów yaw i pitch z wektora kierunkowego.
Załóżmy że mamy pewien obiekt, który chcemy aby zawsze był skierowany przodem do gracza.

W takim wypadku muszę posiadać wektor wskazujący kierunek od obiektu do gracza:

Vector3 V = (ObjectPosition - PlayerPosition);
Powyższy wektor wysyłam do funkcji odpowiedzialnej za wyliczenie kątów yaw i pitch:

public void UpdateDirectionAndAngles(Vector3 V)
        {
            // wyliczam yaw i pitch
            float yaw = (float)Math.Atan2(V.X, -V.Z);
            float pitch = -(float)Math.Atan2(V.Y, Math.Sqrt((V.X * V.X) + (V.Z * V.Z)));

            // przechodzę z radianów na stopnie
            float yawDegrees = yaw * 180 / (float)Math.PI;
            float pitchDegrees = pitch * 180 / (float)Math.PI;

            if (Yaw != yawDegrees) // Yaw - aktualna wartość kąta obiektu
            {
                // ustalam różnicę kątów (wyliczonego i aktualnego)
                dYaw = yawDegrees - Yaw;

                // sprawdzam warunki brzegowe (z braku lepszej nazwy tak to nazywam)
                if (dYaw > 180)
                    dYaw -= 360;
                if (dYaw < -180)
                    dYaw += 360;
            }

            if (Pitch != pitchDegrees) // Pitch - aktualna wartość kąta obiektu
            {
                // ustalam różnicę kątów (wyliczonego i aktualnego)
                dPitch = pitchDegrees - Pitch;

                // sprawdzam warunki brzegowe (z braku lepszej nazwy tak to nazywam)
                if (dPitch > 180)
                    dPitch -= 360;
                if (dPitch < -180)
                    dPitch += 360;
            }
        }

Powyższa funkcja działa prawie prawidłowo. Nie mam problemów z wyliczeniem yaw (kręcę się dookoła obiektu i obiekt zawsze jest skierowany przodem do mnie). Problem pojawia się przy wyliczeniu pitch. Gdy wartość pitch osiąga wartośc +90, lub -90 stopni, wtedy yaw zmienia się o 180 stopni. Nie mam pomysłu skąd się bierze problem i jak go rozwiązać.

Technikalia: VS C# 2008 Express i XNA 3.1.

Offline Mr. Spam

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

Offline hashedone

  • Użytkownik

# Maj 22, 2013, 15:31:18
Problem jest związany z blokadą przegubową charakterystyczną dla tej reprezentacji kąta. Zauważ, że jeśli pitch wynosi +/- 90st, zmiana yaw nie ma znaczenia dla kierunku - stąd przy kierunkach dla których pitch ma 90st, yaw będzie wariować. Rozwiązanie? Rozważyć inną metodę przechowywania kierunku - np. kwaterniony (z których łatwo wyliczyć ew. pitch/yaw czy nawet roll, czy wyprodukować macierz przekształceń).

Offline koirat

  • Użytkownik

# Maj 22, 2013, 15:33:29
Ogólnie implementując look at zazwyczaj bierze się pod uwagę Wektor który ma wskazywać górę. Gdyż sam kierunek to za mało aby ustalić odpowiedni obrót dla obiektu.

D - Kierunek w którym się patrzymy. (znormalizowany)
Up - Kierunek "do góry".

R - kierunek w bok, (lewy albo prawy) zależy od przyjętego układu współrzędnych (tutaj założyłem że będzie to prawo)  patrząc wzdłuż kierunku i mając wektor Up skierowany w górę.

R = Normalize( CrossProduct(D,Up) )     znormalizowany wektor po iloczynie kartezjanskim z wektorów.

teraz należy wyznaczyć prawdziwe Up dla obiektu po przekształceniu.
PUp - prawdziwe Up
PUp = CrossProduct(R,D)

Problem pojawia się również tutaj, co zrobić w momencie gdy Up = D, należy albo ograniczyć taką możliwość albo wyznaczenie nowe Up.

Mając PUp D i R jesteśmy w stanie wyznaczyć macierz rotacji dla obiektu.

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

  • +1
# Maj 22, 2013, 15:38:14
Cytuj
Rozwiązanie? Rozważyć inną metodę przechowywania kierunku - np. kwaterniony
Kwaterniony nie przechowują kierunku, tylko orientację (plus skalę, ale tego się zwykle nie używa).

Cytuj
Problem pojawia się również tutaj, co zrobić w momencie gdy Up = D, należy albo ograniczyć taką możliwość albo wyznaczenie nowe Up.
Moje rozwiązanie to zawczasu nie dopuścić. Gracz może patrzeć góra/dół maksymalnie pod kątem 89.9 stopni i nie więcej. :)

Offline koirat

  • Użytkownik

# Maj 22, 2013, 15:59:43
Moje rozwiązanie to zawczasu nie dopuścić. Gracz może patrzeć góra/dół maksymalnie pod kątem 89.9 stopni i nie więcej. :)
Też tak robię :).

Ostatnio myślałem też nad taką metodą aby przechowywać poprzednią wartość kierunku,i w przypadku takiej sytuacji  i wyznaczać nowe Up na tej samej płaszczyźnie. Tylko jakby bardziej pochylonej w tył - że tak mało matematycznie się wyrażę

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Maj 22, 2013, 16:40:49
Cytuj
Ostatnio myślałem też nad taką metodą aby przechowywać poprzednią wartość kierunku,i w przypadku takiej sytuacji  i wyznaczać nowe Up na tej samej płaszczyźnie. Tylko jakby bardziej pochylonej w tył - że tak mało matematycznie się wyrażę
Też gdzieś tak chyba robię przy budowaniu macierzy obrotu, ale to wyszło przypadkiem. :)

Offline Chimerian

  • Użytkownik
    • Profil na Warsztat.GD

# Maj 22, 2013, 17:26:07
Kwaterniony nie przechowują kierunku, tylko orientację (plus skalę, ale tego się zwykle nie używa).
Moje rozwiązanie to zawczasu nie dopuścić. Gracz może patrzeć góra/dół maksymalnie pod kątem 89.9 stopni i nie więcej. :)

Gra, którą tworzę, rozgrywa się w przestrzeni kosmicznej, więc muszę mieć pełny obrót w każdym kierunku. W przypadku FPS Twoje rozwiązanie to dobry pomysł, ale tu się nie sprawdzi.

Problem jest związany z blokadą przegubową charakterystyczną dla tej reprezentacji kąta. Zauważ, że jeśli pitch wynosi +/- 90st, zmiana yaw nie ma znaczenia dla kierunku - stąd przy kierunkach dla których pitch ma 90st, yaw będzie wariować. Rozwiązanie? Rozważyć inną metodę przechowywania kierunku - np. kwaterniony (z których łatwo wyliczyć ew. pitch/yaw czy nawet roll, czy wyprodukować macierz przekształceń).

Identyczny problem miałem w przypadku obrotu kamery wokół gracza. I tam właśnie użyłem kwaternionów aby rozwiązać problem. W sumie po napisaniu mojego powyższego posta zacząłem się zastanawiać czy to nie ten sam problem co z kamerą.

Ogólnie implementując look at zazwyczaj bierze się pod uwagę Wektor który ma wskazywać górę. Gdyż sam kierunek to za mało aby ustalić odpowiedni obrót dla obiektu.

Ale chyba sam kierunek wystarczy do wyznaczenia yaw i pitch. Jedynie roll będzie nie wiadome, ale dla mnie może być dowolne - to czy obiekt będzie się kręcił dookoła własnej osi nie powinno mieć większego znaczenia... przynajmniej tak mi się na chwilę obecną wydaje.
« Ostatnia zmiana: Maj 22, 2013, 17:27:53 wysłana przez Chimerian »

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Maj 22, 2013, 17:35:24
Cytuj
Gra, którą tworzę, rozgrywa się w przestrzeni kosmicznej, więc muszę mieć pełny obrót w każdym kierunku. W przypadku FPS Twoje rozwiązanie to dobry pomysł, ale tu się nie sprawdzi.
No to trzeba było od razu tak mówić. :)

Generalnie to o yaw/pitch/roll radzę zapomnieć i oprzeć się na kwaternionach. Tylko warto pamiętać by od czasu do czasu po manipulacjach je normalizować ze względu na błędy numeryczne.