我正在为一个学术项目从头开始实施透视图。我正在使用 Foley、van Dam、Feiner 和 Hughes 撰写的“计算机图形学:原则和实践”(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 是观察窗口的中心。
然后 Projection(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;