Как реализовать камеру на основе кватерниона?

14

ОБНОВЛЕНИЕ Ошибка здесь была довольно простой. Я пропустил преобразование радиан в градусы. Нет необходимости читать все это, если у вас есть другие проблемы.

Я посмотрел несколько уроков по этому поводу, и когда мне показалось, что я понял, я попытался реализовать камеру на основе кватерниона. Проблема в том, что он не работает правильно, после вращения в течение ок. На 10 градусов он отскакивает назад до -10 градусов. Я понятия не имею, что не так. Я использую openTK, и у него уже есть класс кватернионов. Я новичок в opengl, я делаю это просто для удовольствия и не совсем понимаю кватернионы, так что, вероятно, я делаю что-то глупое здесь. Вот некоторый код: (На самом деле почти весь код, кроме методов, которые загружают и рисуют vbo (взят из примера OpenTK, который демонстрирует vbo-s))

Я загружаю куб в VBO и инициализирую кватернион для камеры

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

Я загружаю перспективную проекцию здесь. Это загружается в начале и каждый раз, когда я изменяю размер окна.

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

Здесь я получаю последнее значение поворота и создаю новый кватернион, который представляет только последнее вращение, и умножаю его на кватернион камеры. После этого я преобразую это в осевой угол, чтобы opengl мог использовать его. (Так я понял из нескольких онлайн-уроков по кватернионам)

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

Вот 2 изображения, чтобы объяснить лучше: я вращаюсь некоторое время и из этого:

это

это прыгает в это

введите описание изображения здесь

Любая помощь приветствуется.

Update1 : я добавляю их в потоковую запись, которая записывает в файл:

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

Журнал доступен здесь: http://www.pasteall.org/26133/text . В строке 770 куб прыгает справа налево, когда камера поворачивается. Y меняет знаки. Я не знаю, нормально ли это.

Update2 Вот полный проект.

гёдзо кудор
источник
2
Я не понимаю проблемы. Вы пытались распечатать кватернионы, которые вы используете для рендеринга?
Николь Болас
1
Согласитесь, отладьте ваши значения rx, ry и Quaternion.
замедленная
Почему бы вам не загрузить весь свой проект куда-нибудь на Rapidshare? Было бы проще.
Бобобобо
Хорошо, я загрузю его, когда вернусь домой.
gyozo kudor

Ответы:

9

Хотя вы не показали необходимый код для проверки моего предположения здесь, я почти гарантирую, что ваша проблема на самом деле в этой строке:

cameraRot.ToAxisAngle(out axis, out angle);

возвращает значение угла, выраженное в радианах , в то время как

GL.Rotate(angle, axis);

хочет, чтобы угол был представлен в градусах .

Чтобы исправить это, вам нужно преобразовать значение угла при передаче его в GL.Rotate (), например так:

GL.Rotate(angle * 180.0/3.141593, axis);
Тревор Пауэлл
источник
Да, это была проблема. :) Большое спасибо. Я знал, что opengl ожидает градусы, я предполагал, что ToAxisAngle возвращает градусы. Я только что посмотрел на код opentk, и это радианы. Кстати, я предоставил полный исходный код в конце.
gyozo kudor
@NickCaplinger правильно указывает на то, что в C # есть Math.Piдоступная константа, которую следует использовать вместо буквального значения 3.141593, которое я использовал в этом окончательном расчете.
Тревор Пауэлл
1

Не. Может показаться, что работать с камерой, которой можно манипулировать, как с другими объектами сцены, было бы меньше, но в долгосрочной перспективе лучше иметь камеру, в которой вы можете определить положение, направление глаза и вектор вверх. Особенно, когда вы начинаете программировать модели движения, работать с кватернионами - это действительно боль.

wooyay
источник
1
Согласовано. Я тоже не фанат метода разработки моих приложений «если трудно произнести, значит, лучше» =)
Патрик Хьюз
Кватернион сложно произнести?
Нотлеш
1
Немного юмора проходит долгий путь = D Под умозаключениями я подразумеваю, что вопрос исходит от того, кто понятия не имеет, ПОЧЕМУ он хочет камеру кватерниона, просто, что это аккуратное модное слово и OMG, я хочу, чтобы это так плохо сейчас! Это, конечно, не предмет, требующий решения, когда что-то вроде правильной обработки радианов и параметров степени в сломанном коде выше не рассматривается должным образом.
Патрик Хьюз
3
Этот вопрос не совсем о камерах; речь идет о том, как представлять повороты в игровом коде и как преобразовывать эти игровые представления в форму, чтобы их мог использовать OpenGL. Это совершенно законный вопрос, и я не думаю, что насмешка над отсутствием опыта работы с доменами полезна в любом случае. Это не так, как ОП сделал глупую ошибку; Стремление OpenGL использовать градусы при выражении углов совершенно странно. Каждый делает эту ошибку в первый раз, используя OpenGL. Так что отстаньте от своих высоких лошадей, люди. Geez.
Тревор Пауэлл
Я просто хотел понять, как работает камера на основе кватерниона, и я сделал этот проект в тот день, когда мне больше нечего было делать. У меня нет реальных планов с этим.
gyozo kudor
0

Не знаю много о внутренностях OpenTK. По своему опыту я не видел математической библиотеки с правильной реализацией quat, которая поддерживает необходимую точность (машина), потому что все они влияют на ошибки e ^ 2-e ^ 3. Таким образом, эффект, который вы видите: точность (и значение эпсилона) составляет около 10 ^ -5, и она «прыгает» около нуля. Это мое предположение :)

ниже
источник