Arhn - архитектура программирования

Невозможно ограничить угол наклона камеры между [-90º, 90º] в OpenGL с помощью векторов!

У меня возникла большая проблема с ограничением угла наклона камеры (от -90º до 90º) с помощью приведенного ниже кода. Это своего рода продолжение этот вопрос.

Проблема, похоже, в том, что камера поворачивается более чем на -90º или более чем на +90º, и когда это произойдет, я буду смотреть вниз (или вверх), но вид просто повернется на 180º вокруг оси Y.

Пример: я смотрю на север, глядя на горизонт, и начинаю смотреть вниз до тех пор, пока не могу больше опускаться (ограничено приведенным ниже кодом). Затем я начинаю смотреть вверх и смотрю на юг.

void Camera::Rotate(Vector3D angle) {
    angle = angle * CAMERA_ROTATION_SPEED;

    accumPitchAngle += angle.x;

    if(accumPitchAngle > 90.0f) {
        angle.x = 90.0f - (accumPitchAngle - angle.x);
        accumPitchAngle = 90.0f;
    }

    if(accumPitchAngle < -90.0f) {
        angle.x = -90.0f - (accumPitchAngle - angle.x);
        accumPitchAngle = -90.0f;
    }

    // Rotate along the WORLD_SKY_VECTOR axis (yaw/heading rotation)
    // WORLD_SKY_VECTOR = (0.0f, 1.0f, 0.0f)
    if(angle.y != 0.0f) {
        Reference = RotateArbitraryAxis(Reference, WORLD_SKY_VECTOR, angle.y);
        RightVector = Vector3D::CrossProduct(Reference, WORLD_SKY_VECTOR);
        UpVector = Vector3D::CrossProduct(RightVector, Reference);
    }

    // Rotate along the x axis (pitch rotation)
    if(angle.x != 0.0f) {
        Reference = RotateArbitraryAxis(Reference, RightVector, angle.x);
        UpVector = Vector3D::CrossProduct(RightVector, Reference);
    }

    // Makes sure all vectors are perpendicular all the time
    Reference.Normalize();
    RightVector = Vector3D::CrossProduct(Reference, UpVector);
    RightVector.Normalize();
    UpVector = Vector3D::CrossProduct(RightVector, Reference);
    UpVector.Normalize();
}

Vector3D Camera::RotateArbitraryAxis(const Vector3D v, Vector3D u, double angle) {
    Vector3D result;

    u.Normalize();

    double scalar = Vector3D::DotProduct(v, u);
    double c = cos(Math::DegreesToRadians(angle));
    double s = sin(Math::DegreesToRadians(angle));
    double a = 1.0f - c;

    result.x = u.x * scalar * a + (v.x * c) + ((-u.z * v.y) + (u.y * v.z)) * s;
    result.y = u.y * scalar * a + (v.y * c) + (( u.z * v.x) - (u.x * v.z)) * s;
    result.z = u.z * scalar * a + (v.z * c) + ((-u.y * v.x) + (u.x * v.y)) * s;

    return result;
}

Вероятно, проблема в операторе if(angle.y != 0.0f), если я прокомментирую этот блок кода, проблема вообще не существует. Это как-то связано с WORLD_SKY_VECTOR, но этот код таков, что позволяет мне поверните заголовок и держите камеру горизонтально. Если бы я вместо этого использовал UpVector, проблема была бы решена. Но это годится только для авиасимулятора, мне нужно выровнять горизонт, вот причина WORLD_SKY_VECTOR. Но кажется, что это причина «переключения в сторону», когда я направляю камеру прямо вниз.

Согласно запросу в комментарии ниже... Это для камеры от первого лица (и третьего лица, но я еще не начал реализовывать эту часть), и когда я смотрю прямо вниз, -90º (или прямо вверх, +90º) и когда угол изменяется от -89º до -91º (или от +89º до +91º), я хочу, чтобы камера предотвращала это и не выходила за пределы -90º, +90º. Когда он достигает этого предела, мне нужно, чтобы камера могла вернуться (либо вверх, если я на -90º, либо вниз, если я на +90º). Прямо сейчас это работает только иногда, в других случаях я буду смотреть в другую сторону, а не так, как я изначально смотрел.


Ответы:


1

Проблема 1: код ниже «//Убедитесь, что все векторы перпендикулярны все время», гарантирует, что вектор вашей камеры UP действительно вверх (относительно WORLD_SKY_VECTOR). Так :

  • Когда вы смотрите полностью вниз, «вверх» мало что значит. В вашем случае ваш camera_up должен быть направлен на север, а не на небо.
  • Когда вы продолжаете поворачиваться, после этой точки камера поворачивается, чтобы держать ее вверх к небу.

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

Проблема 2: То же, что и выше. CrossProduct(Reference, WORLD_SKY_VECTOR) дает крест(вверх,вниз), который ничего не значит, просто попробуйте на бумаге.

Если вы хотите иметь возможность смотреть «вверх ногами»:

  • Вычислите вектор направления взгляда. Вы надежно знаете это, потому что у вас есть ориентация и позиция.
  • Вычислите правильный вектор. Это то же самое, но с ориентацией на 90° больше и шагом 0 (камера всегда горизонтальна, т.е. вы не наклоняете голову)
  • Вычислите свой восходящий вектор как крест (справа, спереди).

So :

yaw += whatever;
pitch += whatever;
FrontVector = SphericalToCartesian(yaw, pitch);
RightVector = SphericalToCartesian(yaw + PI/2.0f, 0.0);
UpVector = cross(RightVector, FrontVector);

с SphericalToCartesian что-то вроде if (pitch=0,yaw=0) означает смотреть на юг:

x = cos(pitch) * sin(yaw)
y = sin(pitch)
z = cos(pitch) * cos(yaw)
22.03.2011
  • Вы правы в перпендикулярном коде, я должен использовать UpVector, а не WORLD_SKY_VECTOR, я изменил это для другой проблемы, которая у меня была, и, пока я ее исправил, я облажался с этой. Однако это все еще не решает проблему. Я обновил вопрос, почему мой полный код для функции Rotate(), пожалуйста, взгляните. Также удалил мой второй вопрос, который, возможно, больше не актуален, я опубликую еще один вопрос, если потребуется, отредактируйте свой пост, чтобы удалить ответ на второй вопрос, если хотите. 22.03.2011
  • Примечание. Я сделал то, что вы предложили, заменив WORLD_SKY_VECTOR на UpVector в коде под // Makes sure all vectors are perpendicular all the time. Но я все еще использую WORLD_SKY_VECTOR в вращении рыскания/направления, чтобы держать камеру всегда перпендикулярной земле/небу, иначе это будет похоже на симулятор полета, я не хочу этого. Я получил это из другого вопроса, который я связал в верхней части этого поста. 22.03.2011
  • Нет, проблема не решена. Проблема все еще существует. Я только что обновил вопрос, чтобы содержать полный обновленный код (ранее я удалил несколько строк, думал, что они не важны, но они могут быть). 23.03.2011
  • Я думаю, что понимаю, почему проблема, вероятно, связана с WORLD_SKY_VECTOR, поскольку у меня будет вектор Reference, указывающий прямо вниз, а вектор неба, указывающий прямо вверх, и это проблематично. Я понятия не имею, как решить эту проблему, кроме как с помощью решения aib (то есть с ограничением -89,9 и +89,9). 23.03.2011
  • Реальный вопрос: что вы хотите, чтобы произошло, когда accumPitchAngle изменится с 89 ° до 91 °? Как вы хотите, чтобы ваша камера вела себя? (я не говорю о коде). Это FPS-камера? Камера свободного полета? Опишите его как можно подробнее (в вашем вопросе, если возможно), код зависит от него. 23.03.2011
  • Я только что придумал простое и легкое решение, опубликованное в качестве ответа, пожалуйста, посмотрите и дайте мне знать, что вы думаете. 23.03.2011
  • Это более запутанно и содержит больше кода, чем предлагаемое мной решение. В чем проблема с моим решением? 23.03.2011

  • 2

    Вы не можете взять векторное произведение двух параллельных векторов. Я думаю, что это не так, то есть, когда accumPitchAngle составляет ± 90 °.

    Возможно, вы захотите ограничить его значением от -89,999° до +89,999°.

    Редактировать: Чтобы начать с самого начала, вы хотите преобразовать шаг и рыскание в вектор вперед и вектор вверх для gluLookAt(), верно? Тогда я предлагаю:

    1) Используйте yaw (и только yaw), чтобы создать вектор right, параллельный земле.
    2) Скрестите right и WORLD_SKY_VECTOR, чтобы получить вектор forward, правильный по рысканью, но не по тангажу.
    3) Поверните forward от right до pitch градусов, чтобы получить правильный вектор движения вперед и по тангажу, и по рысканию. (Думаю, вы зашли так далеко.)
    4) Скрестите forward и right, чтобы получить вектор камеры up, который будет работать во всех случаях.

    22.03.2011
  • Я думал об этом, но мне не очень нравится эта идея, она кажется хакерской, а не реальным решением. Мне приходится иметь дело с углами в других ситуациях, и это может вызвать другие проблемы. Пока я пытаюсь этого избежать, если я не найду лучшего решения, мне придется его рассмотреть. 22.03.2011
  • @Nazgulled: Вместо того, чтобы вычислять right как произведение forward и world_up, разве вы не можете просто рассчитать его по углу рыскания? Если нет, я предлагаю вам сохранить оба forward и right (или forward и personal_up), потому что одного вектора недостаточно. 22.03.2011
  • В редактировании вы предлагаете мне отказаться от моего текущего решения, другими словами, отказаться от решения (в основном избавиться от RotateArbitraryAxis()). Мне дали другой вопрос, который отлично работает, помимо этой небольшой проблемы. Должен быть другой способ решить эту проблему, кроме как переписать все заново. 23.03.2011
  • Нет, вам понадобится эта функция, чтобы вращать forward вокруг right. Обратите внимание, что человек, который дал вам эту функцию, предложил вам использовать кватернионы, чтобы обойти эту проблему в первую очередь. Во-вторых, это не решение, если оно не работает, и что заставляет вас думать, что проблема в любом случае невелика? В-четвертых, вы никогда ничего не добьетесь, если не будете отказываться от кода, особенно если он не работает. (Пример: вы не осознали, как мало вам на самом деле нужно изменить.) 23.03.2011
  • Но это работает. Это функция вращения вокруг произвольной оси, и она отлично работает, если я использую вектор вверх вместо вектора неба. Проблема в векторе, а не в методе. Это небольшая проблема, потому что я могу просто сделать то, что вы изначально предложили; проблема будет решена и никто не заметит разницы. Вы правы, я не понял, что мне нужно изменить, потому что, честно говоря, ваш ответ сбил меня с толку. Другими словами, мне трудно связать имена ваших векторов с моими. Я также думаю, что вы предлагаете еще один вектор, чем у меня есть, что еще больше меня сбивает с толку. 23.03.2011
  • Насчет использования кватернионов и отказа от кода — это неправда. Я здесь не для того, чтобы создать идеальную систему камер, используя для этого лучший и наиболее правильный код. Я здесь не для того, чтобы разрабатывать самую классную игру. Я здесь не для того, чтобы разработать что-то хорошее и продать это. Я должен все проанализировать и выбрать то, что лучше, учитывая мои приоритеты. У меня нет ни времени все выбрасывать, ни времени выучить кватернионы и все переписать. Готов ли я учиться и делать что-то лучше? Конечно, вот где веселье. У меня есть время? Нет. Перейти на 89,9 легко и эффективно. 23.03.2011
  • Вы уже знаете лучше, чем кто-либо другой, что без диаграмм это сложно объяснить :). Я попробую Blender на работе сегодня, если у меня будет немного времени. Тем не менее, я бы хотел, чтобы вы сказали, что нашли мой ответ запутанным. 23.03.2011
  • В любом случае, проблема в том, что вы пытаетесь создать вектор right из вектора forward (эталонного) и вектора WORLD_SKY_VECTOR, что терпит неудачу, когда они параллельны. Вы могли бы более просто создать его как функцию рыскания: right = (cos(yaw), 0.0, sin(yaw)); Затем вы могли бы создать вектор forward_horizontal, вращая right вокруг WORLD_SKY_VECTOR, используя функцию или просто (cos(yaw-90), 0.0, sin(yaw-90)). Затем вы можете повернуть его вокруг right, чтобы придать ему шаг. Затем вы можете скрестить его с right, чтобы получить личный вектор up и использовать его с gluLookAt(). 23.03.2011
  • Да, я так понял, что это проблема. Я пытался реализовать ваше предложение, но я все еще в замешательстве. Я не знаю точно, что вы подразумеваете под yaw (я пробовал разные переменные угла), и я понятия не имею, куда поместить весь этот код и какой собственный код удалить. Достаточно сказать, что все, что я пробовал, вообще не сработало. В любом случае, я бы предпочел использовать свой RotateArbitraryAxis вместо всех этих cos/sin, конечно, везде, где это возможно. 23.03.2011
  • Я только что придумал простое и легкое решение, опубликованное в качестве ответа, пожалуйста, посмотрите и дайте мне знать, что вы думаете. 23.03.2011

  • 3

    Через некоторое время я придумал решение, которое довольно простое и понятное. Как было сказано много раз в ответах и ​​комментариях, проблема заключается в том, что прямой вектор (мой Reference) смотрит прямо вверх или вниз, что означает, что этот вектор параллелен WORLD_SKY_VECTOR, и поэтому он становится все беспорядочным.

    Моя мысль, стоящая за этим решением, заключается в том, что когда я смотрю прямо вниз (или вверх) и хочу повернуться влево или вправо, это на самом деле вращение вокруг вектора движения вперед. Почему бы вместо этого не выполнить движение по крену, когда угол тангажа составляет -90º или 90º?

    Собрав это воедино, я решил проблему, просто заменив поворот рыскания/курса следующим кодом:

    if(angle.y != 0.0f) {
        if(abs(accumPitchAngle) == 90.0f) {
            RightVector = RotateArbitraryAxis(RightVector, Reference, -angle.y);
            UpVector = Vector3D::CrossProduct(RightVector, Reference);
        } else {
            Reference = RotateArbitraryAxis(Reference, WORLD_SKY_VECTOR, angle.y);
            RightVector = Vector3D::CrossProduct(Reference, WORLD_SKY_VECTOR);
            UpVector = Vector3D::CrossProduct(RightVector, Reference);
        }
    }
    

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

    23.03.2011
  • угол == 90.0f вряд ли возможен. Я отредактировал свой ответ с фактическим кодом, не могли бы вы попробовать? 23.03.2011
  • Что ты имеешь в виду? Это происходит из-за зажима, который происходит раньше, если, например, угол равен 88,7, а мышь перемещается на 3,2, 88.7 + 3.2 = 91.9, и поэтому значение будет зафиксировано на 90, а поворота шага будет достаточно только для достижения 90 (90 - (91.9 - 3.2) = 1.3). Вот в чем проблема. Если только я не понимаю, что вы говорите. 23.03.2011
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..