Смена «3D-камеры» в направлении вверх - PullRequest
2 голосов
/ 04 февраля 2020

У меня возникли проблемы после урока по созданию "класса камеры" для трехмерного мира.

РЕДАКТИРОВАТЬ: Дополнительные ссылки на уроки

Текст урока: https://learnopengl.com/Getting-started/Camera

Код камеры: https://learnopengl.com/code_viewer_gh.php?code=src / 1.getting_started / 7.3.camera_mouse_zoom / camera_mouse_zoom. cpp

Полный пример кода: https://learnopengl.com/code_viewer_gh.php?code=src / 1.getting_started / 7.3.camera_mouse_zoom / camera_mouse_zoom. cpp

У меня был большой первоначальный успех, однако мне нужно изменить представление камеры о том, что «вверх» в мире, как мой активы уровня и 3d повернуты так, что -Z вверх вместо Y. Но когда я попытался изменить «World_Up» на xyz (0, 0, -1), я вижу очень странное дезориентирующее поведение, которое трудно описать, где перемещение мышь вверх и вниз не влияет на высоту тона и c.

Мне нужно определить, как сделать поведение моей камеры корректным при изменении «World_Up» - сейчас это работает только со стандартным xyz (0, 1, 0) ориентация.

   Y                    -Z                

   |                     |          
   |                     |          
   /-------- Z     To    /-------- Y
  /                     /             
 /                     /         

X                     X

Мой код sa mple ниже - мои извинения, что это на необычном языке, но это должно быть понятно. РЕДАКТИРОВАТЬ: ссылка на учебник (в C ++) выше имеет почти те же самые вычисления, так что если вы хотите проигнорировать мой пример и обсудить учебник, то все будет в порядке.

Суть расчетов в пределах Перемещение , Обновление и Просмотр

  ------------
  -- Camera --
  ------------

  CAMERA_ZOOM        : constant Real := 45.0;
  CAMERA_YAW         : constant Real := 0.0;---90.0;
  CAMERA_PITCH       : constant Real :=  0.0;
  CAMERA_SPEED       : constant Real :=  2.5;
  CAMERA_SENSITIVITY : constant Real :=  0.01;
  CAMERA_FOV : constant Real := 45.0;

  type Direction_Kind is (Forward_Direction, Backward_Direction, Left_Direction, Right_Direction);

  class type Camera_State is 
      function View return Matrix_4D;
      procedure Update;
      ...
      procedure Move (Dir : Direction_Kind; Start_Time : Time);
      procedure Look (X_Offset, Y_Offset : Int; Clamp : Bool := True);
  private
      Sensitivity  : Real      := CAMERA_SENSITIVITY;
      Zoom         : Real      := CAMERA_ZOOM;
      Move_Speed   : Real      := CAMERA_SPEED;
      Yaw          : Real      := CAMERA_YAW;
      Pitch        : Real      := CAMERA_PITCH;
      Right        : Vector_3D := ZERO_VECTOR_3D;
      Up           : Vector_3D := ZERO_VECTOR_3D;
      Position     : Vector_3D := ZERO_VECTOR_3D;
      Direction    : Vector_3D := ZERO_VECTOR_3D;

      -- !!! This works but (0.0, 0.0, -1.0) does not !!!
      World_Up     : Vector_3D := (0.0, 1.0, 0.0);
  end Camera;

  World_Camera : Camera_State;

  class body Camera_State is
    ...
    function View return Matrix_4D is
      (Look_At (Position, Position + Direction, Up));

    procedure Move (Dir : Direction_Kind; Start_Time : Time) is
      begin
        case Dir is
          when Backward_Direction => Position := Position + (Direction * Real_32 (Clock - Start_Time)  * Move_Speed);
          when Forward_Direction  => Position := Position - (Direction * Real_32 (Clock - Start_Time)  * Move_Speed);
          when Right_Direction    => Position := Position - (Normal (Cross (Direction, Up)) * Real_32 (Clock - Start_Time) * Move_Speed);
          when Left_Direction     => Position := Position + (Normal (Cross (Direction, Up)) * Real_32 (Clock - Start_Time) * Move_Speed);
        end case;
      end;

    procedure Look (X_Offset, Y_Offset : Int; Clamp : Bool := True) is
      begin
        Yaw   := Yaw   + Sensitivity * (Real_32 (X_Offset) * (100.0 / Real_32 (Window_Width.Get))) ;
        Pitch := Pitch - Sensitivity * (Real_32 (Y_Offset) * (100.0 / Real_32 (Window_Height.Get))) ;
        if Clamp then
          if Pitch > (PI / 2.0) - 0.01 then
            Pitch := (PI / 2.0) - 0.01;
          end if;
          if Pitch < -(PI / 2.0) + 0.01 then
            Pitch := -(PI / 2.0) + 0.01;
          end if;
        end if;
        Update;
      end;

    procedure Update is
      begin
        Line ("Yaw: "   & Yaw'Wide_Image);
        Line ("Pitch: " & Pitch'Wide_Image);
        Direction := Normal (Vector_3D'(X => Cos (Yaw) * Cos (Pitch),
                                        Y => Sin (Pitch),
                                        Z => Sin (Yaw) * Cos (Pitch)));
        Right := Cross (Direction, World_Up);
        Up    := Normal (Cross (Right, Direction));
      end;

  end Camera;

Мир с Y, восходящим:

enter image description here

1 Ответ

3 голосов
/ 04 февраля 2020

Фон

Определяя общий смысл up , вы вводите соглашение, которое позволяет вам определять рулон камеры дано только его вперед направление. Это соглашение работает до тех пор, пока вы не смотрите прямо вверх или прямо вниз. Нам, людям, нравится это, потому что наши глаза находятся рядом друг с другом, и нам приходится иметь дело со странными чувствами в нашей вестибулярной системе, когда наши головы наклонены в сторону. Держите оба уха ровно на одной высоте, и все хорошо. :)

Вот как это работает

Учебное пособие имеет следующее определение прямого вектора от рыскания и шага

if (pitch > 89.0f)
    pitch = 89.0f;
if (pitch < -89.0f)
    pitch = -89.0f;

glm::vec3 front;
front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
front.y = sin(glm::radians(pitch));
front.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
cameraFront = glm::normalize(front);

Этот вектор указывает где-то в плоскости xz, когда шаг равен нулю, а когда высота высока (до максимума 89 °), он приближается к вектору (0,1,0), а когда высота звука низкая (до мин. -89 °), он приближается к вектору (0, -1,0).

Расчет матрицы камеры выполняет традиционную просмотр операцию , объясненную здесь , которая начинается с этого cameraFront вектор является вперед вектором и пытается вычислить ортогональный правый вектор, беря перекрестное произведение cameraFront и вектора cameraUp. Ограничение диапазона высоты тона гарантирует, что front и cameraUp никогда не будут коллинеарными, и поэтому всегда можно определить, какой путь right относительно up, Единственный раз, когда вправо не имеет смысла, это если вы смотрите прямо вверх или вниз - в этих случаях любое направление может быть правильным (при повороте камеры вдоль ее главной оси), и это будет быть неоднозначным, пытаясь вывести вправо из вперед в одиночку ..

Вот что вы сделали неправильно

Теперь, если все, что вы сделали, это изменили cameraUp вектора на (0,0,1), без изменения способа определения высоты тона и рыскания, теперь вы вводите проблему, с которой вы можете легко получить вектор cameraFront, который является коллинеарным с (0,0,1) , Если шаг равен нулю, то рыскание = 90 ° дает выход cameraFront = (0,0,1), а рыскание = -90 ° дает выход cameraFront = (0,0, -1). В обоих этих случаях будет получена единичная матрица (без решения) для матрицы камеры, поскольку перекрестное произведение с cameraFront и cameraUp равно нулю, то есть cameraRight равно нулю, и все это разваливается.

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

Вот как вы можете это исправить

Так что если вы хотите изменить cameraUp на (0,0,1), то имеет смысл, что вам, вероятно, следует переделать определение шаг и рыскание также. Вы бы сохранили предел, что шаг колеблется от -89 до +89, но сделайте так, чтобы pitch = 0 давало векторы в плоскости xy вместо плоскости xz. По сути, выберите новый вектор для yaw = 0, pitch = 0 и убедитесь, что это вектор в плоскости xy. (Если я могу смело сделать предположение: + x, вероятно, все еще естественно вправо . Так что yaw = 0, вероятно, указывает вниз по оси y? Просто мое собственное предположение.)

Но это код что я вычеркнул (который вычисляет передний вектор из рыскания и высоты тона), который нужно переизобразить, чтобы хорошо играть с вашим определением up , равным (0,0,1).

На В конце дня, просто перемаркировка осей - это все, что вам нужно. Вот один из способов сделать это:

front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch));
front.y = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
front.z = sin(glm::radians(pitch));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...