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:
pula para isso
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.