Преобразование мира вершинных шейдеров, почему мы используем 4-мерные векторы? - PullRequest
3 голосов
/ 22 октября 2009

С этого сайта: http://www.toymaker.info/Games/html/vertex_shaders.html

У нас есть следующий фрагмент кода:

// transformations provided by the app, constant Uniform data
float4x4 matWorldViewProj: WORLDVIEWPROJECTION;

// the format of our vertex data
struct VS_OUTPUT
{
  float4 Pos  : POSITION;
};

// Simple Vertex Shader - carry out transformation
VS_OUTPUT VS(float4 Pos  : POSITION)
{
  VS_OUTPUT Out = (VS_OUTPUT)0;
  Out.Pos = mul(Pos,matWorldViewProj);
  return Out;
}

Мой вопрос: почему struct VS_OUTPUT имеет 4-мерный вектор в качестве своей позиции? Разве положение не просто x, y и z?

Ответы [ 3 ]

9 голосов
/ 22 октября 2009

Потому что вам нужна координата w для расчета перспективы. После вывода из вершинного шейдера DirectX выполняет деление перспективы путем деления на w.

По существу, если в качестве выходной позиции вершины у вас есть 32768, -32768, 32768, 65536, то после деления w вы получите 0,5, -0,5, 0,5, 1. В этот момент w можно отбросить, поскольку он больше не нужен. Затем эта информация передается через матрицу видового экрана, которая преобразует ее в пригодные для использования 2D-координаты.

Редактировать: если вы посмотрите, как выполняется умножение матриц с использованием проекционной матрицы, вы увидите, как значения размещаются в правильных местах.

Взятие матрицы проекции, указанной в D3DXMatrixPerspectiveLH

2*zn/w  0       0              0
0       2*zn/h  0              0
0       0       zf/(zf-zn)     1
0       0       zn*zf/(zn-zf)  0

И, применяя его к случайным x, y, z, 1 (примечание для позиции вершины w всегда будет 1), для входного значения вершины вы получите следующее

x' = ((2*zn/w) * x) + (0 * y) + (0 * z) + (0 * w)
y' = (0 * x) + ((2*zn/h) * y) + (0 * z) + (0 * w)
z' = (0 * x) + (0 * y) + ((zf/(zf-zn)) * z) + ((zn*zf/(zn-zf)) * w)
w' = (0 * x) + (0 * y) + (1 * z) + (0 * w)

Мгновенно вы можете видеть, что w и z различны. Координата w теперь содержит только координату z, переданную в матрицу проекции. z содержит нечто гораздо более сложное.

Итак ... предположим, что у нас есть входная позиция (2, 1, 5, 1), zn (Z-Near) равен 1, а zf (Z-Far равен 10) и aw (ширина) равен 1 и ах (высота) 1.

Проходя через эти значения, мы получаем

x' = (((2 * 1)/1) * 2
y' = (((2 * 1)/1) * 1
z' = ((10/(10-1)  * 5 + ((10 * 1/(1-10)) * 1)
w' = 5

расширяя, что мы тогда получим

x' = 4
y' = 2
z' = 4.4
w' = 5

Затем мы выполняем окончательное разделение перспективы и получаем

x'' = 0.8
y'' = 0.4
z'' = 0.88
w'' = 1

И теперь у нас есть окончательная координата. Предполагается, что x и y находятся в диапазоне от -1 до 1, а z - от 0 до 1. Как вы можете видеть, вершина отображается на экране.

В качестве странного бонуса вы можете видеть, что если | x '| или | у '| или | z '| больше чем | w '| или z 'меньше 0, что вершина находится вне экрана. Эта информация используется для отсечения треугольника на экране.

В любом случае, я думаю, что это довольно исчерпывающий ответ: D

Edit2: будьте осторожны, я использую основные матрицы ROW. Основные матрицы столбцов транспонированы.

4 голосов
/ 22 октября 2009

Вращение задается 3-х мерной матрицей, а перевод - вектором. Вы можете выполнить оба преобразования в одной операции, объединив их в одну матрицу 4 x 3:

rx1 rx2 rx3 tx1
ry1 ry2 ry3 ty1
rz1 rz2 rz3 tz1

Однако, поскольку это не квадрат, существуют различные операции, которые не могут быть выполнены (инверсия для одной). Добавляя дополнительную строку (которая ничего не делает):

0   0   0   1

все эти операции становятся возможными (если не простыми).

Как Гоз объясняет в свой ответ , делая "1" не тождественным значением, матрица становится перспективным преобразованием.

1 голос
/ 22 октября 2009

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

Можно было бы построить матрицу, которая бы напрямую отображала ваши мировые пространственные координаты в такой куб, но постепенное движение от дальней плоскости к ближней будет линейным. То есть движение на одну ногу (к зрителю) на расстоянии одной мили от зрителя приведет к тому же увеличению размера, что и движение на одну ногу, когда на расстоянии нескольких футов от камеры.

Однако, если у нас есть другая координата в нашем векторе (w), мы можем разделить вектор по компонентам на w, и наши примитивы не будут демонстрировать вышеуказанное поведение, но мы все равно можем заставить их оказаться внутри -блок куб выше.

Более подробные объяснения см. http://www.opengl.org/resources/faq/technical/depthbuffer.htm#0060 и http://en.wikipedia.org/wiki/Transformation_matrix#Perspective_projection.

Простым ответом было бы сказать, что если вы не сообщите конвейеру, что это значит, то вы не предоставите ему достаточно информации о вашей проекции. Это можно проверить напрямую, не понимая, что с ним делает конвейер ...

Как вы, наверное, знаете, матрицу 4х4 можно разбить на части в зависимости от того, что делает каждая часть. Матрица 3x3 в верхнем левом углу изменяется при выполнении операций поворота или масштабирования. Четвертый столбец изменяется при выполнении перевода. Если вы когда-нибудь осмотрите перспективную матрицу, она изменит нижний ряд матрицы. Если вы посмотрите, как выполняется умножение матрицы на вектор, то увидите, что нижний ряд матрицы влияет ТОЛЬКО на результирующую w-компоненту вектора. Поэтому, если вы не сообщите конвейеру о w, он не будет иметь всей вашей информации.

...