Я реализую перспективу с нуля для академического проекта. Я использую «Компьютерную графику: принципы и практики» Фоли, Ван Дама, Фейнера и Хьюза (второе издание на C).
Я только что последовал за книгой, реализовав все преобразования матриц, необходимые для перемещения, поворота, сдвига, масштабирования, проецирования, преобразования из перспективы в параллельные объемы канонического представления и для отсечения. Книга, очевидно, использует правую систему координат. Тем не менее, я закончил с появлением примитивов в левой системе координат, и я не могу объяснить, почему.
Вот матрицы, которые я использовал:
Translation:
1, 0, 0, dx
0, 1, 0, dy
0, 0, 1, dz
0, 0, 0, 1
Rotation (to align a coordinate system (rx, ry, rz) to XYZ):
rx1, rx2, rx3, 0
ry1, ry2, ry3, 0
rz1, rz2, rz3, 0
0 , 0 , 0 , 1
Scale:
sx, 0 , 0 , 0
0 , sy, 0 , 0
0 , 0 , sz, 0
0 , 0 , 0 , 1
Shear XY:
1, 0, shx, 0
0, 1, shy, 0
0, 0, 1 , 0
0, 0, 0 , 1
Projecting onto a plane at z = d, with PRP at origin, looking in the positive z direction:
1, 0, 0 , 0
0, 1, 0 , 0
0, 0, 1 , 0
0, 0, 1/d, 0
Затем, учитывая VRP, VPN, PRP, VUP, f и b (и направление проекции dop), уменьшите пространство до объема канонического просмотра для перспективы с использованием P:
rz = VPN / |VPN|
rx = (VUP x rz) / |VUP x rz|
ry = rz x rx
P = ScaleUniform(-1 / (vrp1Z + b)) *
Scale(-2 * vrp1Z / deltaU, -2 * vrp1Z / deltaV, 1) *
Shear(-dopX / dopZ, -dopY / dopZ) *
T(PRP) *
R(rx, ry, rz) *
T(-VRP)
Где vrp1 — ShearXY * T (-PRP) * (0, 0, 0, 1), deltaU и deltaV — ширина и высота окна просмотра. dop вычисляется как CW — PRP, где CW — центр окна просмотра.
Тогда Проекция (d) * P дает мне матрицу проекции.
Я спроецировал простые линии, представляющие единичные векторы на x, y и z, но полученное мною представление на экране было явно левой системой координат … Теперь мне нужно работать в правой системе координат, поэтому есть способ узнать, где я сделал не так?
Вот код, который я использовал:
Как вы можете видеть, компонент Z матрицы масштаба имеет противоположный знак, так как отсечение не работало должным образом, потому что что-то было правым, а что-то левым, но я не мог понять, что именно, поэтому я поменял знак масштаба, потому что это не было нужно в левой системе.
Vector rz = vpn.toUnitVector();
Vector rx = vup.cross(rz).toUnitVector();
Vector ry = rz.cross(rx).toUnitVector();
Vector cw = viewWindow.getCenter();
Vector dop = cw - prp;
Matrix t1 = Matrix::traslation(-vrp[X], -vrp[Y], -vrp[Z]);
Matrix r = Matrix::rotation(rx, ry, rz);
Matrix t2 = Matrix::traslation(-prp[X], -prp[Y], -prp[Z]);
Matrix partial = t2 * r * t1;
Matrix shear = Matrix::shearXY(-dop[X] / dop[Z], -dop[Y] / dop[Z]);
Matrix inverseShear = Matrix::shearXY(dop[X] / dop[Z], dop[Y] / dop[Z]);
Vector vrp1 = shear * t2 * Vector(0, 0, 0, 1);
Matrix scale = Matrix::scale(
2 * vrp1[Z] / ((viewWindow.xMax - viewWindow.xMin) * (vrp1[Z] + b)),
2 * vrp1[Z] / ((viewWindow.yMax - viewWindow.yMin) * (vrp1[Z] + b)),
1 / (vrp1[Z] + b)); // HERE <--- WAS NEGATIVE
Matrix inverseScale = Matrix::scale(
((viewWindow.xMax - viewWindow.xMin) * (vrp1[Z] + b)) / (2 * vrp1[Z]),
((viewWindow.yMax - viewWindow.yMin) * (vrp1[Z] + b)) / (2 * vrp1[Z]),
(vrp1[Z] + b));
float zMin = -(vrp1[Z] + f) / (vrp1[Z] + b);
Matrix parallel = Perspective::toParallelCvv(zMin);
Matrix inverseParallel = Perspective::inverseToParallelCvv(zMin);
Matrix perspective = Perspective::copAtOrigin(-vrp1[Z]);
projection = perspective * shear * partial;
canonicalView = parallel * scale * shear * partial;
canonicalToProjection = perspective * inverseScale * inverseParallel;
Задача ещё не решена.
Других решений пока нет …