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

StackOverflow https://stackoverflow.com/questions/1605703

  •  05-07-2019
  •  | 
  •  

Вопрос

С этого сайта: 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?

Это было полезно?

Решение

Потому что вам нужна координата 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 х 3:

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

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

0   0   0   1

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

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

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

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

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

Дополнительные пояснения см. на http://www.opengl.org /resources/faq/technical/depthbuffer.htm#0060 и http: // en .wikipedia.org / вики / Transformation_matrix # Perspective_projection .

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top