Ortho и Persp меняют знак глубины Z? - PullRequest
0 голосов
/ 19 октября 2018
Координаты

NDC для OpenGL образуют куб, сторона которого -Z давит на экран, а его сторона +Z находится дальше всего.

Когда я использую ...

// ortho arguments are: left, right,  bottom, top,  near, far
pos = pos * glm::ortho<float>(-1, 1, -1, 1, -1, 1);

... отражается z компонент pos;-1 становится 1, 10 становится -10 и т. Д.

glm :: persp делает нечто подобное, и это странно?Если позиция имеет z, равную near, я ожидаю, что она будет лежать на плоскости, обращенной к плоскости куба NDC, но вместо этого ее знак будет произвольно отражен;он даже не приземляется на самую дальнюю сторону.

Почему это так?

1 Ответ

0 голосов
/ 19 октября 2018

Координаты NDC для OpenGL образуют куб, сторона которого -Z прижимается к экрану, а сторона + Z дальше всего.

Я посмотрел учебник Song Ho Ahns оПреобразования OpenGL, чтобы быть уверенным, что они не говорят глупостей.

Перспективная проекция

В перспективной проекции - трехмерная точка в усеченном видеусеченная пирамида (координаты глаза) отображается на куб (NDC);диапазон координат x от [l, r] до [-1, 1], координаты y от [b, t] до [-1, 1] и координаты z от [-n, -f]to [-1, 1].

Обратите внимание, что координаты глаза определены в правой системе координат, но NDC использует левую систему координат .Таким образом, камера в начале координат смотрит вдоль оси -Z в пространстве глаз, но она смотрит вдоль оси Z в NDC.

(Подчеркивание - мое.)

Для этого он привел следующую хорошую иллюстрацию:

Perspective Frustum and Normalized Device Coordinates (NDC)

Итак, я пришел к выводу, что

glm::ortho<float>(-1, 1, -1, 1, -1, 1);

не должен генерировать матрицу тождеств, а вместо этогогде ось z зеркально отражена, например, что-то вроде

|  1  0  0  0 |
|  0  1  0  0 |
|  0  0 -1  0 |
|  0  0  0  1 |

Поскольку у меня нет glm под рукой, я взял соответствующие строки кода из исходного кода на github ( glm ).Немного покопавшись в исходном коде, я наконец нашел реализацию glm::ortho() в orthoLH_ZO():

template<typename T>
GLM_FUNC_QUALIFIER mat<4, 4, T, defaultp> orthoLH_ZO(T left, T right, T bottom, T top, T zNear, T zFar)
{
    mat<4, 4, T, defaultp> Result(1);
    Result[0][0] = static_cast<T>(2) / (right - left);
    Result[1][1] = static_cast<T>(2) / (top - bottom);
    Result[2][2] = static_cast<T>(1) / (zFar - zNear);
    Result[3][0] = - (right + left) / (right - left);
    Result[3][1] = - (top + bottom) / (top - bottom);
    Result[3][2] = - zNear / (zFar - zNear);
    return Result;
}

Я немного преобразовал этот код, чтобы сделать следующий пример:

#include <iomanip>
#include <iostream>

struct Mat4x4 {
  double values[4][4];
  Mat4x4() { }
  Mat4x4(double val)
  {
    values[0][0] = val; values[0][1] = 0.0; values[0][2] = 0.0; values[0][3] = 0.0;
    values[1][0] = 0.0; values[1][1] = val; values[1][2] = 0.0; values[1][3] = 0.0;
    values[2][0] = 0.0; values[2][1] = 0.0; values[2][2] = val; values[2][3] = 0.0;
    values[3][0] = 0.0; values[3][1] = 0.0; values[3][2] = 0.0; values[3][3] = val;
  }
  double* operator[](unsigned i) { return values[i]; }
  const double* operator[](unsigned i) const { return values[i]; }
};

Mat4x4 ortho(
  double left, double right, double bottom, double top, double zNear, double zFar)
{
  Mat4x4 result(1.0);
  result[0][0] = 2.0 / (right - left);
  result[1][1] = 2.0 / (top - bottom);
  result[2][2] = - 1;
  result[3][0] = - (right + left) / (right - left);
  result[3][1] = - (top + bottom) / (top - bottom);
  return result;
}

std::ostream& operator<<(std::ostream &out, const Mat4x4 &mat)
{
  for (unsigned i = 0; i < 4; ++i) {
    for (unsigned j = 0; j < 4; ++j) {
      out << std::fixed << std::setprecision(3) << std::setw(8) << mat[i][j];
    }
    out << '\n';
  }
  return out;
}

int main()
{
  Mat4x4 matO = ortho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
  std::cout << matO;
  return 0;
}

Скомпилированный и запущенный, он обеспечивает следующий вывод:

   1.000   0.000   0.000   0.000
   0.000   1.000   0.000   0.000
   0.000   0.000  -1.000   0.000
  -0.000  -0.000   0.000   1.000

Live Demo на coliru

Ха!z масштабируется с -1, т. е. значения z отражаются в плоскости xy (как и ожидалось).

Следовательно, наблюдение OP полностью корректно и разумно:

... компонент zПозит отражается;-1 становится 1, 10 становится -10 и т. Д.


Самое сложное:

Почему это так?

Мое личное предположение: один из гуру SGI, который изобрел все эти вещи GL, сделал это в своей мудрости.

Еще одно предположение: в глазном пространстве ось x направлена ​​вправо, а ось y направлена ​​вверх.Переводя это в координаты экрана, ось y должна указывать вниз (так как пиксели обычно / технически адресуются, начиная с верхнего левого угла).Итак, это вводит другую зеркальную ось, которая изменяет управляемость системы координат (снова).

Это немного неудовлетворительно, и поэтому я погуглил и нашел это (дубликат?):

SO:Почему нормализованная система координат устройства левша?

...