在opengl中有一个原点为(0,0,0)的世界坐标系。
嗯,技术上没有。
让我困惑的是 glTranslate、glRotate 等所有转换都做了什么?它们是在世界坐标中移动对象,还是移动相机?
两者都没有。 OpenGL 不知道对象,OpenGL 不知道相机,OpenGL 不知道世界。 OpenGL 关心的只是图元、点、线或三角形、每个顶点的属性、标准化设备坐标 (NDC) 和 NDC 映射到的视口。
当您告诉 OpenGL 绘制图元时,每个顶点都会根据其属性进行处理。位置是属性之一,通常是在局部“对象”坐标系中具有 1 到 4 个标量元素的向量。手头的任务是以某种方式将局部顶点位置属性转换为视口上的位置。在现代 OpenGL 中,这发生在一个小程序中,在 GPU 上运行,称为 顶点着色器。顶点着色器可以以任意方式处理位置。但通常的方法是应用一些非奇异的线性变换。
这样的变换可以用同质变换矩阵来表示。对于 3 维向量,4 个元素的向量中的齐次表示,其中第 4 个元素为 1。
在计算机图形学中,三重转换管道已成为一种标准的做事方式。首先将对象局部坐标转换为相对于虚拟“眼睛”的坐标,从而进入眼睛空间。在 OpenGL 中,这种转换曾经被称为 modelview 转换。对于眼睛空间中的顶点位置,可以进行多种计算,例如照明可以以广义方式表示,因此这些计算发生在眼睛空间中。接下来,眼睛空间坐标被转换为所谓的剪辑空间。这种变换将眼睛空间中的一些体积映射到具有特定边界的特定体积,几何图形被裁剪到该特定体积。由于这种转换有效地应用了投影,因此在 OpenGL 中这曾经被称为 projection 转换。
在剪辑空间之后,位置被它们的同质分量“归一化”,产生归一化的设备坐标,然后简单地映射到视口。
总结一下:
一个顶点位置由局部变换到裁剪空间
vpos_eye = MV · vpos_local
eyespace_calculations(vpos_eye);
vpos_clip = P · vpos_eye
·: inner product column on row vector
然后到达NDC
vpos_ndc = vpos_clip / vpos_clip.w
最后到视口(NDC 坐标在 [-1, 1] 范围内)
vpos_viewport = (vpos_ndc + (1,1,1,1)) * (viewport.width, viewport.height) / 2 + (viewport.x, viewport.y)
*: vector component wise multiplication
OpenGL 函数 glRotate、glTranslate、glScale、glMatrixMode 仅操作变换矩阵。 OpenGL曾经有四个变换矩阵:
可以使用 glMatrixMode 设置矩阵操作函数作用于哪一个。每个矩阵操作函数通过乘以它们在选择矩阵顶部描述的变换矩阵来组成一个新矩阵,从而替换它。 glLoadIdentity 函数将当前矩阵替换为恒等式,glLoadMatrix 将其替换为用户定义的矩阵,glMultMatrix 在其上乘以用户定义的矩阵。
那么模型视图矩阵如何模拟对象放置和相机。好吧,正如你已经说过的
如您所知,相同的运动可以通过移动物体或相机来实现。
您无法真正区分它们。通常的方法是将对象局部到眼睛的转换分为两个步骤:
- 对象到世界 - OpenGL 称之为“模型转换”
- 世界观 - OpenGL 将此称为“视图变换”
它们一起形成模型视图,在 modelview 矩阵描述的固定函数 OpenGL 中。现在因为转换的顺序是
- 本地到世界,M模型矩阵
vpos_world = M · vpos_local
- 世界观,V查看矩阵
vpos_eye = V · vpos_world
我们可以替换为
vpos_eye = V · ( M · vpos_local ) = V · M · vpos_local
将V · M替换为ModelView矩阵=: MV
vpos_eye = MV · vpos_local
因此,您可以看到复合矩阵 M 的 V 和 M 仅取决于您乘以模型视图矩阵的操作顺序,以及您决定在哪一步“从这里将其称为模型变换开”。
即紧接着
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
视图已定义。但在某些时候,您将开始应用模型转换,之后的一切都是模型。
请注意,在现代 OpenGL 中,所有矩阵操作函数都已被删除。 OpenGL 的矩阵堆栈从来都不是完整的功能,也没有真正的应用程序真正使用过它。大多数程序只是glLoadMatrix-ed 自己计算的矩阵,并没有打扰 OpenGL 内置的矩阵操作例程。
自从引入着色器后,整个 OpenGL 矩阵堆栈就变得难以使用,说得好听点。
结论:如果您打算以现代方式使用 OpenGL,请不要打扰内置函数。但请记住我写的内容,因为您的着色器所做的将与 OpenGL 的固定函数管道所做的非常相似。