前言:图形学第五弹,移动相机!这一章难度比较大,为了静下心来看这个文章,我把大神的文章整个翻译了一遍。
3D 空间的基本变化
在欧几里得坐标系中,坐标可以被「原点」和「基坐标」表示。 在坐标系
意味着什么呢?意味着,向量 OP 可以按以下的方法表示
现在有另一个坐标系了 我们如何转换坐标呢?新的坐标系可以由旧坐标系表示
一图解决所有问题
所以我们重新表示一下 OP
用之前的公式代替
同样给我们了新的公式去转换坐标
让我们创建我们自己的 gluLookAt
我们的玩具渲染器只能让相机在 Z 轴画图像,移动相机没有问题,只需要移动整个场景,让我们的相机不移动
让我们以这种方式解决问题:我们想要用位于e(眼睛)的相机绘制一个场景,相机应该指向点c(中心),使得给定的向量u(向上)是在最终渲染中是垂直的
画个图表示一下
这个图表示,我们可以在新的坐标系中画图, 但是我们给的模型是坐标系
。没有问题!我们所需要的就是去计算坐标的转换,这里是 C++ 代码计算必要的 4×4 模型场景矩阵
void lookat(Vec3f eye, Vec3f center, Vec3f up) {
Vec3f z = (eye-center).normalize();
Vec3f x = cross(up,z).normalize();
Vec3f y = cross(z,x).normalize();
Matrix Minv = Matrix::identity();
Matrix Tr = Matrix::identity();
for (int i=0; i<3; i++) {
Minv[0][i] = x[i];
Minv[1][i] = y[i];
Minv[2][i] = z[i];
Tr[i][3] = -center[i];
}
ModelView = Minv*Tr;
}
注意向量 给出了
(不要忘记去正则化它, 这在后面会有用)我们如何计算
? 简单通过
和
叉乘得到,然后我们计算
使它与计算的
,
正交。(让我提醒你一下,我们问题的设定
和
不一定是正交)。最后一步是将原点转换为中心
,我们的转换矩阵就绪。现在只需在模型框架中获得坐标
的任意点,将其乘以矩阵 ModelView,我们就可以获得相机框架中的坐标!顺便说一下,ModelView这个名字来自 OpenGL 术语。
Viewport
如果你从一开始就按照这个课程,你应该记得像这样的奇怪的代码:
screen_coords[j] = Vec2i((v.x+1.)*width/2., (v.y+1.)*height/2.);
这是什么意思?这意味着我有一个点 Vec2f v,[-1,1] * [ - 1,1] 它属于我想在图像(宽度,高度)尺寸的图像中绘制它。值(v.x 1)在 0 和 2 之间变化,(v.x 1)/ 2在0和1之间变化,并且(v.x 1)* width / 2扫描所有图像。
但是现在我们正在摆脱这些丑陋的构造,我想重写矩阵形式的所有计算。让我们考虑以下 C++ 代码:
Matrix viewport(int x, int y, int w, int h) {
Matrix m = Matrix::identity(4);
m[0][3] = x+w/2.f;
m[1][3] = y+h/2.f;
m[2][3] = depth/2.f;
m[0][0] = w/2.f;
m[1][1] = h/2.f;
m[2][2] = depth/2.f;
return m;
}
这个 code 创建了这样一个矩阵
这意味着原来 [-1,1] * [ - 1,1] * [ - 1,1] 被映射到屏幕立方体 [x,xw] * [y,yh] * [0,d ]。这是立方体,而不是矩形。这是因为 z-buffer 的深度计算。这里 d 是 z 缓冲区的分辨率。我喜欢它等于255,因为简单的倾斜 z-buffer 的黑白图像用于调试。 在 OpenGL 术语中,该矩阵称为 ViewPort 矩阵。
Chain of coordinate transformations 坐标一系列的变换
所以,让我们总结一下。我们的模型是在他们自己的坐标系中创建的,它们要被插入以世界坐标表示的场景中。使用矩阵模型进行从一个坐标系到另一个坐标系的转换。然后我们想要在相机框架(眼睛坐标系中)表示它们,转换这个过程称为 View。得到新的坐标以后,使用透视投影。矩阵转换到场景中被叫做 Clip Coordinate。最后,我们画出这个场景,这个矩阵变化的过程 Clip Coordinate 到 Screen Coordinates 被叫做 ViewPort
再一次如果我们从 .obj 文件读取一个点 v,然后画它在屏幕上。经历了以下步骤
Viewport * Projection * View * Model * v
Transformation of normal vectors 法向量的转换
这里有一个广泛知道的事实
- 如果我们有一个平面和它的法向量,这个平面经过一系列仿射变换,那么法向量同样也会经过相同的仿射变换。等价于原始映射矩阵的逆矩阵的转换。
什么什么什么?!我认识了很多了解这个事实的程序员,但对他们来说仍然是一个黑魔法。事实上,它并不复杂。拿一支铅笔绘制一个 2D 三角形 和一个法向量 n,n 等于
。然后让我们将所有 y 坐标拉伸 2 倍,保持 x 坐标不变。因此,我们的三角形变为
。如果我们以相同的方式变换向量 n,它变为
并且它不再与三角形的变换边正交。
因此,为了消除所有的黑魔法迷雾,我们需要理解一件简单的事情:我们不需要简单地变换法向量(因为它们可能变得不正常),我们需要计算(新)法向量到变换后的模型。
回到 3D,我们有一个向量。我们知道穿过原点并且 n为法线的平面具有方程
。让我们以矩阵形式编写它(在齐次坐标中执行):
回想一下 -是一个向量,所以我们在嵌入 4D 时用0增加它,而(x,y,z)用1增加,因为它是一个点。
让我们在其间插入一个单位矩阵
可以写成
左括号告诉我们通过应用仿射映射的逆转置矩阵,可以从旧法线计算变换对象的法线。