在opengl中有一个原点(0,0,0)的世界坐标系。
让我困惑的是所有的转换,如 glTranslate、glRotate 等是做什么的?他们是在世界坐标中移动物体,还是移动相机?如您所知,相同的运动可以通过移动物体或相机来实现。
我猜glTranslate、glRotate、更改对象和gluLookAt 会更改相机?
在opengl中有一个原点(0,0,0)的世界坐标系。
让我困惑的是所有的转换,如 glTranslate、glRotate 等是做什么的?他们是在世界坐标中移动物体,还是移动相机?如您所知,相同的运动可以通过移动物体或相机来实现。
我猜glTranslate、glRotate、更改对象和gluLookAt 会更改相机?
在opengl中有一个原点(0,0,0)的世界坐标系。
嗯,技术上没有。
让我困惑的是所有的转换,如 glTranslate、glRotate 等是做什么的?他们是在世界坐标中移动物体,还是移动相机?
两者都不。OpenGL 不知道对象,OpenGL 不知道相机,OpenGL 不知道世界。OpenGL 关心的只是图元、点、线或三角形、每个顶点属性、标准化设备坐标 (NDC) 和 NDC 映射到的视口。
当您告诉 OpenGL 绘制图元时,每个顶点都会根据其属性进行处理。位置是属性之一,通常是在局部“对象”坐标系中具有 1 到 4 个标量元素的向量。手头的任务是以某种方式将局部顶点位置属性转换为视口上的位置。在现代 OpenGL 中,这发生在一个小程序中,在 GPU 上运行,称为顶点着色器。顶点着色器可以以任意方式处理位置。但通常的方法是应用一些非奇异的线性变换。
这种变换可以用同质变换矩阵表示。对于 3 维向量,具有 4 个元素的向量中的同质表示,其中第 4 个元素为 1。
在计算机图形学中,三重转换管道已成为一种标准的做事方式。首先将对象局部坐标转换为相对于虚拟“眼睛”的坐标,从而转换为眼睛空间。在 OpenGL 中,这种转换曾经被称为模型视图转换。对于眼睛空间中的顶点位置,可以进行多种计算,例如照明可以以广义方式表示,因此这些计算发生在眼睛空间中。接下来,眼睛空间坐标被转换为所谓的剪辑空间。这种转换映射了一些眼睛空间中的体积到具有特定边界的特定体积,几何图形被剪裁到该体积。由于这种变换有效地应用了投影,因此在 OpenGL 中这曾经被称为投影变换。
在剪辑空间之后,位置被它们的同质分量“归一化”,产生归一化的设备坐标,然后简单地映射到视口。
回顾一下:
顶点位置从局部变换到裁剪空间
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中。现在因为转换的顺序是
vpos_world = M · vpos_local
vpos_eye = V · vpos_world
我们可以用
vpos_eye = V · ( M · vpos_local ) = V · M · vpos_local
替换V · M
为模型视图矩阵_=: MV
vpos_eye = MV · vpos_local
因此,您可以看到复合矩阵 M 的 V 和 M 仅取决于您乘以模型视图矩阵的操作顺序,以及您决定“从这里开始将其称为模型变换”的步骤。
即在一个之后
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
视图已定义。但在某些时候,您将开始应用模型转换,之后的一切都是模型。
请注意,在现代 OpenGL 中,所有矩阵操作函数都已被删除。OpenGL 的矩阵堆栈从来都不是完整的功能,也没有真正的应用程序真正使用过它。大多数程序只是glLoadMatrix
编辑了他们自己计算的矩阵,并没有打扰 OpenGL 内置的矩阵操作例程。
自从引入了着色器之后,整个 OpenGL 矩阵堆栈就很难用了,说得好听点。
结论:如果您打算以现代方式使用 OpenGL,请不要打扰内置函数。但请记住我写的内容,因为您的着色器所做的将与 OpenGL 的固定函数管道所做的非常相似。
OpenGL是一个低级API,在“场景”中没有“物体”和“相机”之类的高级概念,所以只有两种矩阵模式:MODELVIEW(“相机”矩阵乘以“对象”变换)和投影(从世界空间到后透视空间的投影变换)。
“模型”和“视图”(对象和相机)矩阵之间的区别取决于您。glRotate/glTranslate 函数只是将当前选择的矩阵乘以给定的矩阵(甚至不区分 ModelView 和 Projection)。
这些函数乘以(转换)当前矩阵集,glMatrixMode()
因此它取决于您正在处理的矩阵。OpenGL有4种不同类型的矩阵;GL_MODELVIEW、GL_PROJECTION、GL_TEXTURE 和 GL_COLOR,这些函数中的任何一个都可以更改这些矩阵中的任何一个。因此,基本上,您不会转换对象,而只是操纵不同的矩阵来“伪造”该效果。
请注意,这glulookat()
只是一个方便的功能,相当于平移后跟一些旋转,没有什么特别之处。
所有的变换都是对对象的变换。甚至 gluLookAt 也只是一个转换对象的转换,就好像相机在你告诉它的位置一样。从技术上讲,它们是顶点上的转换,但这只是语义。
没错,glTranslate、glRotate 在渲染之前改变了对象坐标,而 gluLookAt 改变了相机坐标。