Войти
ПрограммированиеФорумГрафика

Проекционная и видовая матрицы

#0
20:09, 6 дек. 2015

Неожиданно для себя обнаружил, что совершенно не понимаю как работают проекционная и видовая матрицы.

Как бы высокоуровнево пишут, что проекционная задает способ проецирования (ортография, перспектива) и пространство отсечения (куб или усеченная пирамида).
Видовая матрица отвечает за положение объекта перед камерой.

У меня есть вот такой простенький вертексный шейдер (видовая матрица пока что просто единичная)

attribute vec4 Position;
uniform mat4 Projection;
uniform mat4 ModelView;
void main()
{
  gl_Position = Projection * ModelView * Position;
}

так вот, иду я значит в документацию, например сюда https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd373965(v=vs.85).aspx и смотрю как стряпается ортографическая матрица, переношу ее, закидываю в нее параметры ширины и высоты экрана. На экране при этом рисуется совершенно абсурдная бурда.

  float projection[] =
  {
    2.0f / (right - left),        0.0f,            0.0f,          -(right + left) / (right - left),
    0.0f,              2.0f / (top - bottom),      0.0f,          -(top + bottom) / (top - bottom),
    0.0f,              0.0f,            -2.0f / (far - near),    -(far + near) / (far - near),
    0.0f,              0.0f,            0.0f,          1.0f
  };

  GLint projectionUniform = glGetUniformLocation(shader.program, "Projection");
  glUniformMatrix4fv(projectionUniform, 1, GL_FALSE, projection);

видовая, опять-таки повторюсь, просто единичная

  float modelView[] =
  {
    1.0f, 0.0f, 0.0f, 0.0f,
    0.0f, 1.0f,  0.0f, 0.0f,
    0.0f, 0.0f,  1.0f, 0.0f,
    0.0f, 0.0f,  0.0f, 1.0f
  };

  GLint modelviewUniform = glGetUniformLocation (shader.program, "ModelView");
  glUniformMatrix4fv(modelviewUniform, 1, GL_FALSE, modelView);


Треугольник точно правильный, состоит из следующих вертексов

GLfloat v[] = 
  {
    -0.5, -0.5,
    0.0, 0.5,
    0.5, -0.5
  };

Как бы, превые два диагональных значения проекционной матрицы я могу объяснить - это мы "сжимаем" наш ортонормированный базис и сжать его надо в половину экранных размеров, потому что нуль в середине, а единицы по краям (не знаю как объяснить понятнее). по этому для того, чтобы получить положение вертекса на экране надо домножить его на 1/size/2 или преобразуя дробь - на 2/size.

Что за near и far? как их вообще можно обозначить? в особенности при ортографичесокой проекции? Этож по хорошему можно смотреть очень далеко и все равно все видеть. И почему по оси Z изменен знак?

И если я все правильно понимаю, то тут выходит, что мы умножаем матрицу на вектор (а не наоборот), а такое преобразование портит вектору значение параметра w делая его равным tx*x + ty*y + tz*z + w. Я так понимаю, что это все и портит. Зачем это нужно?


#1
20:48, 6 дек. 2015
  float projection[] =
  {
    2.0f / (right - left),        0.0f,            0.0f,          -(right + left) / (right - left),
    0.0f,              2.0f / (top - bottom),      0.0f,          -(top + bottom) / (top - bottom),
    0.0f,              0.0f,            -2.0f / (far - near),    -(far + near) / (far - near),
    0.0f,              0.0f,            0.0f,          1.0f
  };
В OpenGL матрицы column-major.
Правка:

Хранятся column-major, в смысле.

>Что за near и far?
Передняя и задняя плоскости отсечения. Этап отсечения всё-равно в конвеере присутствует. Для перспективной - это помогает не делить на 0. Для ортогональной - конкретно это менее критично. Но диапазон и точность буфера глубины всё-равно от них зависят.

>И почему по оси Z изменен знак?
В системе координат по умолчанию OpenGL "вперёд" - это -z. Но near и far принимаются с положительным знаком (для удобства?). Соответственно в матрице он меняется.

>а такое преобразование портит вектору значение параметра w делая его равным tx*x + ty*y + tz*z + w
Вот такого быть не должно, если не ошибаюсь.

> https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd373965(v=vs.85).aspx
https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd373965… vs.85%29.aspx
Скобочки можно эскейпать (%28, %29). Мелочь, а приятно.
#2
21:24, 6 дек. 2015

FordPerfect
Ну, давайте вместе посмотрим, и если что вы меня поправите. Смотрите, у меня есть радиус-вектор

x, y, z, w

и проекционная матрица

nx, 0,  0,  tx
0,  ny, 0,  ty
0,  0,  nz, tz
0,  0,  0,  1

умножаем матрицу на вектор,  столбцы на строки

{nx * x + 0 * y + 0 * z + 0 * w}, 
{0 * x + ny * y + 0 * z + 0 * w},
{0 * x + 0 * y + nz * z + 0 * w},
{tx * x + ty * y + tz * z + 1 * w}

или в сокращенной записи

{nx * x}
{ny * y}
{nz * z}
{tx * x + ty * y + tz * z + 1 * w}

Такая вот ерунда выходит. И ведь просто ладно бы если я где-то при этом накосячил, но ведь при отрисовке вообще какая-то ересь получается.

#3
21:32, 6 дек. 2015


там есть красивая картинка где-то посередине

http://www.scratchapixel.com/lessons/3d-basic-rendering/perspecti… line-clipping

#4
21:50, 6 дек. 2015

Fennec
Математически спецификация OpenGL сформулирована в терминах вектор-столбцов:
[cht]
A=
\begin{bmatrix}
n_x & 0 & 0 & t_x\\
0 & n_y & 0 & t_y\\
0 & 0 & n_z & t_z\\
0 & 0 & 0 & 1
\end{bmatrix}
[/cht], [cht]
\mathbf{b}=
\begin{bmatrix}
x\\
y\\
z\\
w
\end{bmatrix}
[/cht]
[cht]
A \cdot \mathbf{b} =
\begin{bmatrix}
n_x x + t_x w\\
n_y y + t_y w\\
n_z z + t_z w\\
w
\end{bmatrix}
[/cht]

#5
22:12, 6 дек. 2015

FordPerfect
Окей, тогда у меня такой вопрос:

Операция умножения двух матриц выполнима только в том случае, если число столбцов в первом сомножителе равно числу строк во втором

получается, что при таком представлении мы не можем умножить вектор на матрицу?

codingmonkey
минуту, сейчас гляну статью

#6
22:29, 6 дек. 2015

Fennec
Ну да, вектор-столбец 4x1 на матрицу 4x4 множится только в одном порядке: матрица*вектор. Обратно - не определено.

#7
22:36, 6 дек. 2015

Другое дело, что GLSL это позволяет, превращая вектор в строку (и обратно):
https://en.wikibooks.org/wiki/GLSL_Programming/Vector_and_Matrix_… ons#Operators

If a vector is multiplied to a matrix from the left, the result corresponds to multiplying a row vector from the left to the matrix. This corresponds to multiplying a column vector to the transposed matrix from the right:

Изображение

#8
23:00, 6 дек. 2015

FordPerfect
то есть выходит GLSL делает эти операции коммутативными?

Тогда выходит, что ортографическая матрица - это просто матрица масштабирования и перемещения?

и все же, в моем случае получается такая история: например возьмем разрешение экрана 800х600, а начало координат хочу чтобы было в левом верхнем углу, тогда параметры должны быть

float left = 0;
float right = width;
float bottom = -height;
float top = 0;
float far = -1;
float near = 1;
    1 / 400,       0,           0,    -1,
    0,             1 / -300,    0,    -1,
    0,             0,           1      0,
    0,             0,           0,     1

сжимаем ось х в 400 раз
сжимаем ось у в 300 раз
ось z не трогаем

но по 2 осям отступаем на 1 назад (кстати внезапно стало понятно, зачем такие значения для far и near). Вопрос: для чего? В смысле, я же хотел их подвинуть в совсем другое место, что я делаю не так?

#9
23:10, 6 дек. 2015

Fennec
>но по всем трем осям отступаем на 1 назад. Вопрос: для чего?
В OpenGL отсечение происходит по [cht] [-1; \, +1] \times [-1; \, +1] \times [-1; \, +1] [/cht]. Соответственно к этим координатам и приводится.

Строго говоря, отсечение происхожит в однородных координатах (clip space):
-w < x < +w
-w < y < +w
-w < z < +w

https://www.opengl.org/wiki/Vertex_Post-Processing

Правка: обобщённых → однородных.
#10
23:20, 6 дек. 2015

Fennec
>то есть выходит GLSL делает эти операции коммутативными?
Нет.
Справа:
[cht]A \cdot b[/cht]
Слева:
[cht](b^T \cdot A)^T=A^T \cdot b[/cht]

А вообще, это ж не секретные вещи и они есть в OpenGL Programming Guide: The Official Guide to Learning OpenGL (в народе "Red Book").

#11
0:35, 7 дек. 2015

FordPerfect
О, книжка.

Окей, спасибо большое за все объяснения и ссылки!

#12
6:28, 7 дек. 2015

На самом деле нет разницы какой математикой определять видимые вершины (которые попадают в область камеры), но системе нужно как то определить запускать ли фрагментный шейдер или переходить к следующего примитиву в вершинном, и было принято решение ввести стандарт: программист любым способом должен привести координаты вершины в локальные координаты камеры, и если после этого у всех трех координат полигона хотя бы одна компонента x,y,z не входят в диапазон от -w до w, тогда фрагментный шейдер вообще не запускается, поскольку все три вершины полигона лежат вне области камеры, а значит примитив не виден. Это касается и оргогональной и перспективной проекции. Вот русское подробное описание общепринятой математики такого преобразования координат, где объясняется что происходит с "w" компонентой вектора и почему параметры в матрице именно такие - http://gamesmaker.ru/programming/directx/virtualnaya-kamera-persp… ya-proekciya/ (на сайте описывается решение в DirectX которое по сути ничем не отличается от OpenGL).

#13
11:59, 7 дек. 2015

Phisix
И вам большое спасибо за информацию.

Похоже я разобрался.

ПрограммированиеФорумГрафика

Тема в архиве.