¿Cómo implemento una cámara basada en quaternion?

14

ACTUALIZACIÓN El error aquí fue bastante simple. He perdido una conversión de radianes a grados. No es necesario leer todo si tienes algún otro problema.

Miré varios tutoriales sobre esto y cuando pensé que entendía, intenté implementar una cámara basada en el cuaternión. El problema es que no funciona correctamente, después de girar durante aprox. 10 grados salta de nuevo a -10 grados. No tengo idea de lo que está mal. Estoy usando openTK y ya tiene una clase quaternion. Soy un novato en opengl, estoy haciendo esto solo por diversión, y realmente no entiendo cuaterniones, así que probablemente estoy haciendo algo estúpido aquí. Aquí hay algo de código: (En realidad, casi todo el código, excepto los métodos que cargan y dibujan un vbo (se toma de una muestra de OpenTK que demuestra vbo-s))

Cargo un cubo en un vbo e inicializo el cuaternión para la cámara

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);
}

Cargo una proyección en perspectiva aquí. Esto se carga al principio y cada vez que cambio el tamaño de la ventana.

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);
}

Aquí obtengo el último valor de rotación y creo un nuevo cuaternión que representa solo la última rotación y lo multiplico con el cuaternión de la cámara. Después de esto, lo transformo en ángulo de eje para que opengl pueda usarlo. (Así es como lo entendí de varios tutoriales en línea de quaternion)

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();
}

Aquí hay 2 imágenes para explicar mejor: giro un rato y de esto:

esta

salta a esto

ingrese la descripción de la imagen aquí

Cualquier ayuda es apreciada.

Actualización 1 : los agrego a una máquina de escribir que escribe en un archivo:

    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);

El registro está disponible aquí: http://www.pasteall.org/26133/text . En la línea 770, el cubo salta de derecha a izquierda, cuando camerarot.Y cambia de signo. No sé si esto es normal.

Update2 Aquí está el proyecto completo.

gyozo kudor
fuente
2
No entiendo el problema. ¿Has intentado imprimir los cuaterniones que estás usando para renderizar?
Nicol Bolas
1
De acuerdo, depure sus valores de rx, ry y Quaternion.
deceleratedcaviar
¿Por qué no subes todo tu proyecto a Rapidshare? Sería más fácil
bobobobo
OK, lo subiré cuando llegue a casa.
gyozo kudor

Respuestas:

9

Si bien no ha mostrado el código necesario para verificar mi suposición aquí, casi puedo garantizarle que su problema es en realidad esta línea:

cameraRot.ToAxisAngle(out axis, out angle);

está devolviendo un valor de ángulo expresado en radianes , mientras

GL.Rotate(angle, axis);

quiere que el ángulo se proporcione en grados .

Para solucionarlo, debe convertir el valor del ángulo al pasarlo a GL.Rotate (), así:

GL.Rotate(angle * 180.0/3.141593, axis);
Trevor Powell
fuente
Sí, ese era el problema. :) Muchas gracias. Sabía que opengl espera grados, supuse que ToAxisAngle devuelve grados. Acabo de mirar el código opentk y es radianes. Por cierto, he proporcionado el código fuente completo al final.
gyozo kudor 01 de
@NickCaplinger señala correctamente que en C #, hay una Math.Piconstante disponible, que debe usarse con preferencia al valor literal 3.141593 que utilicé en ese cálculo final.
Trevor Powell
1

No lo hagas Puede parecer que sería menos trabajo tener una cámara que se pueda manipular como otros objetos de escena, pero a la larga es mejor tener una cámara donde se pueda definir una posición, la dirección del ojo y el vector hacia arriba. Especialmente cuando comienzas a programar modelos de movimiento, es realmente un trabajo trabajar con cuaterniones.

wooyay
fuente
1
Convenido. No soy fanático del método "si es difícil de pronunciar, debe ser mejor" para diseñar mis aplicaciones, también =)
Patrick Hughes
Quaternion es difícil de pronunciar?
notlesh
1
Un poco de humor es muy útil = D Por inferencia, quiero decir que la pregunta proviene de alguien que no tiene idea de POR QUÉ quiere una cámara de cuaternión, solo que es una palabra de moda ordenada y ¡Dios mío, la quiero muchísimo ahora! Ciertamente no es un tema a abordar cuando algo como manejar radianes y parámetros de grado correctamente en el código roto anterior no se trata adecuadamente.
Patrick Hughes
3
Esta pregunta no es realmente sobre cámaras; se trata de cómo representar rotaciones en el código del juego y cómo convertir esas representaciones del juego en una forma tal que OpenGL pueda usarlas. Es una pregunta perfectamente legítima, y ​​no creo que burlarse de la falta de experiencia de dominio del OP sea útil de ninguna manera. No es que el OP haya cometido un error estúpido; La insistencia de OpenGL en usar grados al expresar ángulos es francamente extraña. Todos cometen este error la primera vez que usan OpenGL. Así que baje de sus caballos, gente. Geez
Trevor Powell
Solo quería entender cómo funciona una cámara basada en un cuaternión e hice este proyecto en un día en que no tenía nada más que hacer. No tengo planes reales con esto.
gyozo kudor
0

No sé mucho sobre los componentes internos de OpenTK. En mi experiencia, no vi la biblioteca matemática con la implementación adecuada de quat que mantiene la precisión (máquina) necesaria, porque todos afectan los errores e ^ 2-e ^ 3. Entonces, el efecto que ves: la precisión (y el valor de épsilon) es de aproximadamente 10 ^ -5, y 'salta' cerca de cero. Esta es mi suposición :)

infra
fuente