Войти
ПодсказкиСтатьи

Летающая камера

Автор:

Самый понятный и простой способ сделать летающую камеру.

Что для этого надо:

- умножение вектора на матрицу

+ Показать

- инвертирование матрицы
+ Показать

- создание кватерниона из углов Эйлера
+ Показать

- создание матрицы из кватерниона
+ Показать

- перевод градусов в радианы
+ Показать

Обозначения:
f32 - float

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

if (isRMBHold)
{
    camera->rotate(mouseDelta, deltaTime);
}

mouseDelta - значение, на сколько пикселей переместился курсор  за прошедший кадр. Для этого надо запомнить координаты курсора с прошлого кадра.

+ Показать

Дальше реализация самого вращения.

void Camera::rotate(const v2f& mouseDelta, f32 dt)
{
  f32 speed = 4.4f;
  Mat4 RX;
  Mat4 RY;
  bool update = false;
  if (mouseDelta.x != 0.f)
  {
    update = true;
    RY.setRotation(Quat(v4f(0.f, math::degToRad(-mouseDelta.x) * dt * speed, 0.f, 0.f)));
  }
  if (mouseDelta.y != 0.f)
  {
    update = true;
    RX.setRotation(Quat(v4f(math::degToRad(-mouseDelta.y) * dt * speed, 0.f, 0.f, 0.f)));
  }

  if (update)
    m_camera->m_rotationMatrix = RX * m_camera->m_rotationMatrix * RY;
}
Для того чтобы поворачивать камеру, нужно создать максимум 2 матрицы.
Первая матрица вращает всё по оси ИКС (водим мышкой взад вперёд) - RX
Вторая матрица вращает всё по оси ИГРЕК(водим мышкой влево вправо) - RY
RX матрица должна зависить от RY - от текущего направления взгляда, для того чтобы клевать носом в любую сторону
Дальше важен порядок умножения матриц. Я не знаю как это всё работает, но знаю что должно быть так, и подбирал значения пока не заработает
RX * m_camera->m_rotationMatrix * RY

Теперь обновление viewMatrix

void camera_onUpdate(yyCamera* camera)
{
  math::makePerspectiveRHMatrix(
    camera->m_projectionMatrix,
    camera->m_fov,
    camera->m_aspect,
    camera->m_near,
    camera->m_far);

  camera->m_viewMatrix = camera->m_rotationMatrix * camera->m_objectBase.m_globalMatrix;
}
viewMatrix это и есть rotationMatrix, только нужно добавить информацию о позиции. В моём случае m_globalMatrix хранит WORLD матрицу с учётом матриц всех предков(с предками не тестировал поэтому не уверен в работоспособности). Сейчас это обычная матрица со значением позиции.

Умножение m_rotationMatrix на позицию (которая лежит в m_globalMatrix) необходимо, так как перемещая камеру, мы перемещаем её строго по осям (например, летая вперёд или назад, изменяем координату Z, не зависимо от ориентации камеры).

Теперь полёт

if (isKeyHold(yyKey::K_W))
  сamera->moveForward(deltaTime);

if (isKeyHold(yyKey::K_S))
  сamera->moveBackward(deltaTime);

if (isKeyHold(yyKey::K_A))
  сamera->moveLeft(deltaTime);

if (isKeyHold(yyKey::K_D))
  сamera->moveRight(deltaTime);

if (isKeyHold(yyKey::K_E))
  сamera->moveUp(deltaTime);

if (isKeyHold(yyKey::K_Q))
  сamera->moveDown(deltaTime);

Реализация

void Camera::moveLeft(f32 dt)
{
  _moveCamera(v4f(-m_moveSpeed * dt, 0.f, 0.f, 1.f));
}
void Camera::moveRight(f32 dt)
{
  _moveCamera(v4f(m_moveSpeed * dt, 0.f, 0.f, 1.f));
}
void Camera::moveUp(f32 dt)
{
  _moveCamera(v4f(0.f, m_moveSpeed * dt, 0.f, 1.f));
}
void Camera::moveDown(f32 dt)
{
  _moveCamera(v4f(0.f, -m_moveSpeed * dt, 0.f, 1.f));
}
void Camera::moveBackward(f32 dt)
{
  _moveCamera(v4f(0.f, 0.f, m_moveSpeed * dt, 1.f));
}
void Camera::moveForward(f32 dt)
{
  _moveCamera(v4f(0.f, 0.f, -m_moveSpeed * dt, 1.f));
}
Как видно, перемещаясь влево или вправо, меняем значение только по X. Вверх вниз - только по Y. Вперёд назад - только по Z.

void Camera::_moveCamera(v4f& vel)
{
  auto RotInv = m_rotationMatrix;
  RotInv.invert();
  vel = math::mul(vel, RotInv);
  m_objectBase.m_localPosition += vel;
}

И так. Как происходит перемещение?

Вычисляем направление (direction) (методы moveForward moveBackward ит.д.).
Крутим это направление (умножение на инвертированную матрицу).
Прибавляем к текущей позиции получившееся значение (повёрнутое направление).

Я назвал переменную vel от слова velocity так как это похоже на скорость чем на направление.

m_localPosition это и есть позиция, которая обычно суётся в world матрицу
Можно и не сувать

void camera_onUpdate(yyCamera* camera)
{
  math::makePerspectiveRHMatrix(
    camera->m_projectionMatrix,
    camera->m_fov,
    camera->m_aspect,
    camera->m_near,
    camera->m_far);

  auto V = math::mul(-camera->m_objectBase.m_localPosition, camera->m_rotationMatrix);
  camera->m_viewMatrix = camera->m_rotationMatrix;
  camera->m_viewMatrix[3] = V;
  camera->m_viewMatrix[3].w = 1.f;
}

Установка курсора в центр экрана не очень простая вещь.
Просто оставлю код как мне это удалось сделать

 
if(удержание правой кнопки мыши)
{
// тут вращение и перемещение камеры

// в конце ставлю курсор в центр окна и изменяю m_cursorCoordsOld
auto cursorX = std::floor((f32)window.m_data->m_clientSize.x / 2.f);
auto cursorY = std::floor((f32)window.m_data->m_clientSize.y / 2.f);
g_inputContex->m_cursorCoordsOld.set(cursorX, cursorY);

yySetCursorPosition(cursorX, cursorY, window.m_data);

}
...

void yySetCursorPosition(f32 x, f32 y, yyWindow* window)
{
#ifdef YY_PLATFORM_WINDOWS
  RECT clientRect;
  GetClientRect(window->m_hWnd, &clientRect);
  ClientToScreen(window->m_hWnd, (LPPOINT)&clientRect.left);
  ClientToScreen(window->m_hWnd, (LPPOINT)&clientRect.right);

  POINT point;
  point.x = (int)x + clientRect.left;
  point.y = (int)y + clientRect.top;

  ScreenToClient(window->m_hWnd, &point);

  SetCursorPos(clientRect.left + point.x, clientRect.top + point.y);
#else
#error Need implementation
#endif
}

Запустить видео по клику - Как делать игрыЗапустить видео по клику - Как делать игры

#камера

12 декабря 2020