В opengl, почему мы должны делать gluPerspective перед gluLookAt?

Так под GL_PROJECTION я сделал

    glu.gluPerspective(90,aspect,1,10);
    glu.gluLookAt(0,0,3,0,0,0,0,1,0);

это работает нормально, но когда я меняю порядок, на экране не появляется никаких объектов, я поворачиваю камеру и ничего не получается.

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

2 ответа

Решение

Чтобы увидеть объект на экране, вам нужно, чтобы он попадал в объем канонического представления, который для OpenGL составляет [−1, 1] во всех трех измерениях. Чтобы преобразовать объект, вы примерно

P' = проекция × вид × модель × P

где P' является конечной точкой, которая должна находиться в объеме канонического представления, а P является начальной точкой в ​​пространстве модели. P преобразуется матрицей модели с последующим просмотром, а затем проекцией.

Порядок, которому я следовал, основан на векторном столбце, где каждое последующее преобразование умножается на пре / слева. Другой способ прочитать ту же формулу - это прочитать ее слева направо, где вместо преобразования точки система координат преобразуется и интерпретируется P в преобразованной системе пространственно представляет P' в оригинальной системе. Это просто еще один способ увидеть это, результат одинаков для обоих; как численно, так и пространственно.

почему мы должны делать gluPerspective перед gluLookAt?

Более старый конвейер с фиксированными функциями OpenGL умножает пост / справа и, следовательно, порядок должен быть обратным, чтобы получить тот же эффект. Поэтому, когда нам нужен LookAt сначала, а затем Perspective, мы делаем обратное, чтобы получить ожидаемый результат.

Предоставление двух в правильном порядке приводит к

P' = Вид × Проекция × Модель × P

так как матричное умножение антикоммутативно, вы не получите право P' который попадает в объем канонического просмотра и, следовательно, черный экран.

См. Главу 3 "Красная книга" в разделе " Команды преобразования общего назначения", в котором объясняется порядок следования OpenGL. Выдержка:

Примечание. Все умножение матриц с помощью OpenGL происходит следующим образом. Предположим, что текущая матрица равна C, а матрица, указанная с помощью glMultMatrix*() или любой из команд преобразования, равна M. После умножения конечная матрица всегда CM. Поскольку матричное умножение обычно не коммутативно, порядок имеет значение.


Я хочу знать, почему первый случай работает, а второй нет.

Чтобы узнать, что на самом деле происходит с матрицей, сформированной в неправильном порядке, давайте проведем небольшую тренировку в 2D. Допустим, область канонического обзора равна [−100, 100] как в X, так и в Y; все, что находится вне этого, вырезано. Начало этого мнимого квадратного экрана находится в центре, X идет направо, Y идет вверх. Когда преобразование не применяется, вызывая DrawImage рисует изображение в начале координат. У вас есть изображение 1 × 1; его матрица модели масштабируется на 200 так что это становится 200 × 200 образ; тот, который заполняет весь экран. Поскольку источник находится в центре экрана, чтобы нарисовать изображение таким образом, чтобы оно заполняло экран, нам нужна матрица вида, которая переводит (перемещает) изображение на (-100, -100). Формулировка этого

P' = Вид × Модель = Перевести −100, −100 × Шкала 200, 200

[ 200,  0,  −100 ]
[  0,  200, −100 ]
[  0,   0,   1   ]

Тем не менее, результат

Модель × Вид = S 200, 200 × T −100, −100

[ 200,  0,  −20000 ]
[  0,  200, −20000 ]
[  0,   0,    1    ]

Умножение предыдущей матрицы на точки (0, 0) и (1, 1) приведет к (-100, -100) и (100, 100), как и ожидалось. Углы изображения будут выровнены по углам экрана. Однако умножение последней матрицы на них приведет к (-20000, -20000) и (-19800, -19800); хорошо вне видимой области. Это связано с тем, что геометрически последняя матрица сначала переводит, а затем масштабирует, а не масштабирует, а затем переводит. Переведенная шкала приводит к полностью отключенной точке.

В

glu.gluPerspective(90,aspect,1,10);
glu.gluLookAt(0,0,3,0,0,0,0,1,0);

В этом случае первые координаты модели / мира (в R^3) преобразуются в координаты вида (также R^3). Затем проекция отображает координаты вида в перспективное пространство (P^4), которое затем уменьшается путем деления перспективы на координаты NDC. Это в целом, как это должно работать.

Теперь взгляните на:

glu.gluLookAt(0,0,3,0,0,0,0,1,0);
glu.gluPerspective(90,aspect,1,10);

Здесь мировые координаты проецируются непосредственно в проективное пространство (P^4). Поскольку матрица lookAt является отображением из R^3 -> R^3, и мы уже находимся в P^4, это не сработает. Даже если бы можно было повернуть P^4, параметры gluLookAt должны были бы быть адаптированы, чтобы соответствовать диапазонам проективного пространства.

Примечание. В общем случае никогда не следует добавлять gluLookAt в стек GL_PROJECTION. Поскольку он описывает матрицу представления, он лучше подходит для стека GL_MODELVIEW. Для справки посмотрите здесь.

Другие вопросы по тегам