Como implemento uma câmera baseada em quaternion?

14

ATUALIZAÇÃO O erro aqui foi bastante simples. Perdi uma conversão de radiano para graus. Não é necessário ler a coisa toda se você tiver algum outro problema.

Eu olhei para vários tutoriais sobre isso e, quando pensei ter entendido, tentei implementar uma câmera baseada em quaternion. O problema é que não funciona corretamente, depois de girar por aprox. 10 graus, ele volta para -10 graus. Eu não tenho ideia do que está errado. Estou usando o openTK e ele já tem uma classe de quaternion. Eu sou um noob no opengl, estou fazendo isso apenas por diversão, e realmente não entendo quaternions, então provavelmente estou fazendo algo estúpido aqui. Aqui está um código: (Na verdade, quase todo o código, exceto os métodos que carregam e desenham um vbo (é retirado de uma amostra do OpenTK que demonstra o vbo-s))

Carrego um cubo em um vbo e inicializo o quaternion para a câmera

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

Eu carrego uma projeção em perspectiva aqui. Isso é carregado no início e toda vez que redimensiono a janela.

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

Aqui, obtenho o valor da última rotação e crio um novo quaternion que representa apenas a última rotação e multiplica-o pelo quaternion da câmera. Depois disso, eu o transformo em ângulo de eixo para que o opengl possa usá-lo. (Foi assim que eu entendi em vários tutoriais on-line 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();
}

Aqui estão duas imagens para explicar melhor: eu giro um pouco e a partir disso:

esta

pula para isso

insira a descrição da imagem aqui

Qualquer ajuda é apreciada.

Update1 : eu os adiciono a um streamwriter que grava em um arquivo:

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

O log está disponível aqui: http://www.pasteall.org/26133/text . Na linha 770, o cubo salta da direita para a esquerda quando camerarot.Y muda de sinal. Não sei se isso é normal.

Atualização2 Aqui está o projeto completo.

gyozo kudor
fonte
2
Eu não entendo o problema. Você já tentou imprimir os quaternions que você está usando para renderizar?
Nicol Bolas
1
Concordado, depure seus valores rx, ry e Quaternion.
Deceleratedcaviar
Por que você não carrega todo o seu projeto em algum lugar para o rapidshare? Seria mais fácil.
bobobobo
OK, enviarei quando chegar em casa.
Gyozo kudor

Respostas:

9

Embora você não tenha mostrado o código necessário para verificar minha suposição aqui, quase posso garantir que seu problema é realmente esta linha:

cameraRot.ToAxisAngle(out axis, out angle);

está retornando um valor de ângulo expresso em radianos , enquanto

GL.Rotate(angle, axis);

quer que o ângulo seja fornecido em graus .

Para corrigi-lo, você precisa converter o valor do ângulo ao passá-lo para GL.Rotate (), assim:

GL.Rotate(angle * 180.0/3.141593, axis);
Trevor Powell
fonte
Sim, esse era o problema. :) Muito obrigado. Eu sabia que o opengl espera graus, presumi que o ToAxisAngle retorna graus. Eu apenas olhei para o código opentk e é radianos. BTW eu forneci o código fonte completo no final.
Gyozo kudor
O @NickCaplinger indica corretamente que, em C #, existe uma Math.Piconstante disponível, que deve ser usada preferencialmente ao valor literal de 3.141593 que eu usei no cálculo final.
Trevor Powell
1

Não. Pode parecer menos trabalhoso ter uma câmera que possa ser manipulada como outros objetos de cena, mas, a longo prazo, é melhor ter uma câmera em que você possa definir uma posição, direção dos olhos e vetor ascendente. Especialmente quando você começa a programar modelos de movimento, é realmente difícil trabalhar com quaterniões.

wooyay
fonte
1
Acordado. Eu não sou um fã do "se é difícil de pronunciar, deve ser melhor" método de projetar minhas aplicações, também =)
Patrick Hughes
Quaternion é difícil de pronunciar?
Novelas
1
Um pouco de humor percorre um longo caminho = D Por inferência, sugiro que a pergunta venha de alguém que não tem idéia POR QUE ele quer uma câmera de quaternário, apenas que é um chavão e OMG eu quero tanto agora! Certamente não é um assunto a ser resolvido quando algo como manipular radianos e parâmetros de graus corretamente no código quebrado acima não é tratado adequadamente.
Patrick Hughes
3
Esta questão não é realmente sobre câmeras; trata-se de como representar rotações no código do jogo e como convertê-las em um formato para que o OpenGL possa usá-las. É uma pergunta perfeitamente legítima, e não acho que zombar da falta de experiência no domínio do OP seja útil. Não é como se o OP tivesse cometido um erro estúpido; A insistência do OpenGL em usar graus ao expressar ângulos é absolutamente bizarra. Todo mundo comete esse erro pela primeira vez usando o OpenGL. Então saia do alto, pessoal. Nossa.
Trevor Powell
Eu só queria entender como funciona uma câmera baseada em quaternion e fiz esse projeto em um dia em que não tinha mais nada para fazer. Não tenho planos reais com isso.
Gyozo kudor
0

Não sabe muito sobre os componentes internos do OpenTK. Na minha experiência, não vi uma biblioteca matemática com a implementação adequada de quat que mantém a precisão necessária (máquina), porque todos eles afetam os erros e ^ 2-e ^ 3. Portanto, o efeito que você vê: a precisão (e o valor épsilon) é de cerca de 10 ^ -5 e 'salta' perto de zero. Este é o meu palpite :)

infra
fonte