Математика матрицы перспективы

Я хочу создать свою собственную матрицу mvp в качестве упражнения.

auto lookAt(Vec)(const Vec eye, const Vec center, const Vec up)
if(isVector!Vec && Vec.dimension is 3){
    auto z = (eye - center).unit;
    auto x = cross(up.unit, z);
    auto y = cross(z, x);
    alias Mat = Matrix!(Vec.Type, 4, 4);
    alias Vec4 = Vector!(Vec.Type, 4);
    return Mat(Vec4(x, 0),
               Vec4(y, 0),
               Vec4(z, 0),
               Vec4(0, 0, 0, 1)) * translate(-eye);
}

Преобразование вида было довольно простым, я настроил его так, что теперь смотрю по отрицательной оси Z.

Но у меня есть несколько проблем с матрицей перспективы.

y' = 1 / (tan(fov/2) * z
x' = 1 / (ar * tan(fov/2) * z

Где x' и y' — новые искаженные значения. Так я придумал эту матрицу

Mat4f projection(float fov, float ar, float near, float far){
  import std.math: tan;
  auto tanHalfAngle = tan(fov/2.0f);
  return Mat4f(
      Vec4f(1.0f / (ar * tanHalfAngle) , 0, 0, 0),
      Vec4f(0, 1.0f / tanHalfAngle, 0, 0),
      Vec4f(0, 0, 1, 0),
      Vec4f(0, 0, -1, 0));
}

В настоящее время я просто настроил его так, чтобы значение Z всегда было -1. Но я сделал небольшую ошибку, потому что теперь я делю на -Z.

Дело в том, что Матрица выше работает. В настоящее время я визуализирую вращающийся куб, и, кроме значений глубины, все выглядит правильно.

Но я очень хотел эту матрицу

Mat4f projection(float fov, float ar, float near, float far){
  import std.math: tan;
  auto tanHalfAngle = tan(fov/2.0f);
  return Mat4f(
      Vec4f(1.0f / (ar * tanHalfAngle) , 0, 0, 0),
      Vec4f(0, 1.0f / tanHalfAngle, 0, 0),
      Vec4f(0, 0, -1, 0),
      Vec4f(0, 0, 1, 0));
}

Но куб больше не появляется на экране, и я не понимаю, почему.

auto view = lookAt(Vec3f(0, 0, -5), Vec3f(0, 0, 0), Vec3f(0, 1, 0));
auto proj = projection(PI/2, 4.0f/3.0f, 0.1f, 100);

Я устанавливаю тестовые значения

auto v = proj * view * Vec4f(0.5,0.5,0.5,1);

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

Vec(-0.375, 0.5, -5.5, 5.5)
Vec(-0.0681818, 0.0909091, -1) // divided by w

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

Vec(-0.375, 0.5, 5.5, -5.5)
Vec(0.0681818, -0.0909091, -1) //divided by w

Единственное, что изменилось, это знак.

Но почему я вижу куб с первой матрицей, а со второй ничего не вижу?


person Maik Klein    schedule 09.04.2016    source источник


Ответы (1)


Правило отсечения по умолчанию в OpenGL формулируется в пространстве отсечения, поскольку все точки, удовлетворяющие следующему неравенству, лежат внутри усеченной пирамиды видимости:

-w <= x, y, z <= w

Обратите внимание, что это тривиально исключает любые точки с w ‹ 0, поскольку тогда -w будет > w, поэтому это не может быть выполнено.

Преобразование вида было довольно простым, я настроил его так, что теперь смотрю по отрицательной оси Z.

Это не совсем так. Матрица вида не определяет только направление взгляда, на него также влияет матрица проекции. При использовании стандартной перспективной проекции последнюю строку проекционной матрицы можно просто интерпретировать как вектор направления главной оси проекции, поэтому он определяет вектор направления просмотра в пространстве глаза. Классическая проекция GL использует (0 0 -1 0), поэтому -z будет направлением просмотра.

Задача матрицы вида теперь состоит в размещении камеры и вращении сцены таким образом, чтобы намеченное направление просмотра в мировом пространстве отображалось на направление обзора в пространстве глаз, используемое проекционной матрицей.

Когда вы переворачиваете матрицу проекции, чтобы разделить ее на z вместо -z, это, конечно, означает, что она будет смотреть в этом направлении. Поскольку вы не настроили свою матрицу обзора, чтобы следовать этому соглашению, ваша камера теперь смотрит точно в противоположном направлении, в котором настроена ваша матрица обзора. Все точки, лежащие «перед» камерой в точке z_eye < 0, будут иметь отрицательное значение w_clip и будут обрезаны ближней плоскостью.

person derhass    schedule 09.04.2016