Jak wdrożyć kamerę opartą na ćwiartce?

14

AKTUALIZACJA Błąd tutaj był dość prosty. Przegapiłem konwersję radianów na stopnie. Nie musisz czytać wszystkiego, jeśli masz jakiś inny problem.

Przejrzałem kilka samouczków na ten temat i kiedy pomyślałem, że rozumiem, próbowałem wdrożyć kamerę opartą na czwartorzędu. Problem polega na tym, że nie działa poprawnie po obróceniu o ok. 10 stopni przeskakuje z powrotem do -10 stopni. Nie mam pojęcia, co jest nie tak. Używam openTK i ma już klasę czwartorzędu. Jestem noobem w Opengl, robię to dla zabawy i tak naprawdę nie rozumiem czwartorzędów, więc prawdopodobnie robię tutaj coś głupiego. Oto trochę kodu: (Właściwie prawie cały kod z wyjątkiem metod, które ładują i rysują vbo (pochodzi z próbki OpenTK, która demonstruje vbo-s))

Ładuję sześcian do vbo i inicjuję kwaternion dla kamery

protected override void OnLoad(EventArgs e) {
    base.OnLoad(e);

    cameraPos = new Vector3(0, 0, 7);
    cameraRot = Quaternion.FromAxisAngle(new Vector3(0,0,-1), 0);

    GL.ClearColor(System.Drawing.Color.MidnightBlue);
    GL.Enable(EnableCap.DepthTest);

    vbo = LoadVBO(CubeVertices, CubeElements);
}

Ładuję tutaj rzut perspektywiczny. Jest to ładowane na początku i za każdym razem, gdy zmieniam rozmiar okna.

protected override void OnResize(EventArgs e) {
    base.OnResize(e);

    GL.Viewport(0, 0, Width, Height);

    float aspect_ratio = Width / (float)Height;

    Matrix4 perpective = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspect_ratio, 1, 64);
    GL.MatrixMode(MatrixMode.Projection);
    GL.LoadMatrix(ref perpective);
}

Tutaj otrzymuję ostatnią wartość obrotu i tworzę nową ćwiartkę, która reprezentuje tylko ostatni obrót i mnożę ją przez ćwiartkę kamery. Następnie przekształcam to w kąt osi, aby OpenGL mógł z niego korzystać. (Tak to zrozumiałem z kilku samouczków czwartorzędu online)

protected override void OnRenderFrame(FrameEventArgs e) {
    base.OnRenderFrame(e);

    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

    double speed = 1;
    double rx = 0, ry = 0;

    if (Keyboard[Key.A]) {
        ry = -speed * e.Time;
    }

    if (Keyboard[Key.D]) {
        ry = +speed * e.Time;
    }

    if (Keyboard[Key.W]) {
        rx = +speed * e.Time;
    }

    if (Keyboard[Key.S]) {
        rx = -speed * e.Time;
    }

    Quaternion tmpQuat = Quaternion.FromAxisAngle(new Vector3(0,1,0), (float)ry);
    cameraRot = tmpQuat * cameraRot;
    cameraRot.Normalize();

    GL.MatrixMode(MatrixMode.Modelview);
    GL.LoadIdentity();

    Vector3 axis;
    float angle;

    cameraRot.ToAxisAngle(out axis, out angle);
    //////////////////////////////////////////////////////////////////////
    // THIS IS WHAT I DID WRONG: I NEED TO CONVERT FROM RADIANS TO DEGREES
    //////////////////////////////////////////////////////////////////////
    //BEFORE
    //GL.Rotate(angle, axis);
    //AFTER
    GL.Rotate(angle * (float)180.0/(float)Math.PI, axis);
    GL.Translate(-cameraPos);

    Draw(vbo);
    SwapBuffers();
}

Oto 2 obrazy, aby lepiej wyjaśnić: Obracam chwilę i od tego:

to

wskakuje w to

wprowadź opis zdjęcia tutaj

Każda pomoc jest mile widziana.

Aktualizacja 1 : Dodaję do streamwriter, który zapisuje do pliku:

    sw.WriteLine("camerarot: X:{0} Y:{1} Z:{2} W:{3} L:{4}", cameraRot.X, cameraRot.Y, cameraRot.Z, cameraRot.W, cameraRot.Length);
    sw.WriteLine("ry: {0}", ry);

Dziennik jest dostępny tutaj: http://www.pasteall.org/26133/text . W linii 770 sześcian przeskakuje od prawej do lewej, gdy kamera zmienia znaki. Nie wiem czy to normalne.

Update2 Oto kompletny projekt.

gyozo kudor
źródło
2
Nie rozumiem problemu. Czy próbowałeś wydrukować czwartorzędowe, których używasz do renderowania?
Nicol Bolas,
1
Uzgodnione, debuguj wartości rx, ry i Quaternion.
spowolniony,
Dlaczego nie przesyłasz gdzieś całego swojego projektu do szybkiego udostępniania? Byłoby łatwiej.
bobobobo
OK, załaduję go, kiedy wrócę do domu.
gyozo kudor

Odpowiedzi:

9

Chociaż nie pokazałeś tutaj kodu niezbędnego do zweryfikowania mojego założenia, prawie mogę zagwarantować, że twoim problemem jest to, że ten wiersz:

cameraRot.ToAxisAngle(out axis, out angle);

zwraca wartość kąta wyrażoną w radianach , natomiast

GL.Rotate(angle, axis);

chce, aby kąt był podawany w stopniach .

Aby to naprawić, musisz przekonwertować wartość kąta podczas przekazywania jej do GL.Rotate (), w następujący sposób:

GL.Rotate(angle * 180.0/3.141593, axis);
Trevor Powell
źródło
Tak, to był problem. :) Wielkie dzięki. Wiedziałem, że Opengl oczekuje stopni, założyłem, że ToAxisAngle zwraca stopnie. Właśnie spojrzałem na kod opentk i to jest radian. BTW Na końcu podałem pełny kod źródłowy.
gyozo kudor
@NickCaplinger słusznie wskazuje, że w języku C # dostępna jest Math.Pistała, która powinna być używana zamiast dosłownej wartości 3,141593, której użyłem w tym ostatecznym obliczeniu.
Trevor Powell
1

Nie rób Może się wydawać, że mniej pracy byłoby mieć kamerę, którą można manipulować jak inne obiekty sceny, ale na dłuższą metę lepiej jest mieć kamerę, w której można określić pozycję, kierunek oczu i wektor w górę. Zwłaszcza, gdy zaczynasz programować modele ruchu, praca z czwartorzędami jest naprawdę trudna.

Wooyay
źródło
1
Zgoda. Nie jestem też fanem metody „jeśli trudno jest powiedzieć, że musi być lepiej” również w projektowaniu moich aplikacji =)
Patrick Hughes,
Quaternion jest trudny do wymówienia?
notlesh
1
Odrobina humoru sięga daleko = D Wnioskując, sugeruję, że pytanie pochodzi od kogoś, kto nie ma pojęcia DLACZEGO chce aparatu czwartorzędowego, tylko, że to fajne modne hasło i OMG Chcę go teraz tak źle! Z pewnością nie jest to temat do rozwiązania, gdy coś takiego jak poprawne obchodzenie się z radianami i parametrami stopni w zepsutym kodzie powyżej nie jest właściwie traktowane.
Patrick Hughes,
3
To pytanie tak naprawdę nie dotyczy kamer; chodzi o to, jak reprezentować rotacje w kodzie gry i jak przekonwertować te reprezentacje gry na formę, dzięki której OpenGL może z nich korzystać. To całkowicie uzasadnione pytanie i nie sądzę, by kpiny z braku doświadczenia domeny OP były w jakikolwiek sposób pomocne. To nie jest tak, że OP popełnił głupi błąd; Naleganie OpenGL na używanie stopni przy wyrażaniu kątów jest wręcz dziwaczne. Każdy popełnia ten błąd po raz pierwszy, używając OpenGL. Więc zejdźcie z wysokich koni, ludzie. Rany.
Trevor Powell,
Chciałem po prostu zrozumieć, jak działa kamera oparta na ćwiartce i stworzyłem ten projekt w dzień, w którym nie miałem nic innego do roboty. Nie mam z tym prawdziwych planów.
gyozo kudor
0

Nie wiem wiele o wewnętrznych elementach OpenTK. Z mojego doświadczenia nie widziałem biblioteki matematycznej z prawidłową implementacją quat, która zachowuje wymaganą precyzję (maszynę), ponieważ wszystkie one mają wpływ na błędy e ^ 2-e ^ 3. Efekt, który widzisz: precyzja (i wartość epsilon) wynosi około 10 ^ -5 i „skacze” w pobliżu zera. Tak sądzę :)

infra
źródło