В 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. Для справки посмотрите здесь.