一般而言,OpenGL目标场景产生的变换过程分为5步:
① 将未安装镜头的相机固定在指定位置上,并调整好镜头的朝向(视图变换)
② 对场景进行安排,将各个物体摆放在指定位置(模型变换)
③ 选择照相机镜头并安装,同时整放大倍数,使得3D场景可以在照相机的CCD上形成一个清晰的平面图像(投影变换)
④ 确定最终照片的大小。(视口变换)
⑤ 进行拍照。(绘制场景)
如图。首先明确OpenGL中存在一个世界坐标系。该坐标系始终不变。OpenGL中一切的物体,都存在于世界坐标系下。
确定照相机位置,然后以该位置(ViewPoint)为原点,建立一个照相机坐标系,或者叫观察者坐标系。而由于成像是在照相机上,所以想要成像,就需要将所有的物体都从世界坐标系映射到观察者坐标系。该映射工作是针对照相机而言的,所有物体的坐标依然是世界坐标,只是照相机需要将它们的世界坐标映射到自身的观察者坐标。gluLookAt就确定了这样一组映射关系。这也就是所谓的视图变换。
接着将物体摆放在世界坐标系下。也就是所谓的模型变换。
由于照相机所看到的范围理论上来说是无限大的(无限远),因此需要定义一个取景区域(一个3D的立方体空间),该区域内的模型会被显示,该区域外的模型不会被显示。该区域内的景物会被从世界坐标系投影到观察者坐标系的平面上,从而由3D变为2D,也就是投影变换。
现在,照相机可以成像了。模型通过投影变换,在观察者坐标系下映射为2D图像,这就是需要显示的图像。但虽然照相机已经拍摄到了图像,依然需要冲洗成照片才能看到,或者说才能显示在屏幕显示区域上。这就需要将所拍摄的图像,映射到照片或者屏幕显示区域上。这就是视口变换。
但由于照相机拍摄到的图像有一个宽高比r,照片及屏幕上显示区域也有一个宽高比R,所以若r≠R,会导致冲洗或显示出来的图像变形。注意拍摄的图像都是正常的,仅仅是因为显示区域的宽高比不对,所以导致了显示出的图像变形,这类似使用Bitblt将一张正常图像拉伸显示。为了让冲洗出的照片或者显示出的图像与拍摄的比例相符,就需要让照片及显示区域的宽高比与拍摄的图像宽高比一致。而拍摄图像的宽高比并不取决于相机,而是取决于投影变换中选取的立方体区域。立方体区域x,y的范围决定了拍摄照片的宽高比。从而立方体区域的宽高比必须与照片宽高比一致,才可以让冲洗出的照片上的图像比例正常。
感觉上,需要让照片宽高比来适应立方体区域宽高比。但是在实际应用中,照片,也就是屏幕显示区域是确定的,不可更改,所以只能让立方体区域宽高比来适应照片宽高比。这就是为什么glOrtho等投影变换函数要根据屏幕显示区域宽高比来进行设置。
有些文章使用了类似如下的图片:
其中屏幕显示区域放在了照相机的视锥中。这仅仅是为了比例容易表示。实际情况应该是相机从取景区域取景后,再将照相机中成的像映射到屏幕显示区域中。
另外,视图转换虽然理论上存在一个观察者坐标系,但实际上视图转换输入的依然是世界坐标系。模型转换也是使用世界坐标系。比如说,初始化状态下照相机与模型都位于(0,0,0)点。要把相机放置到世界坐标系的(1,0,0)点处,这个点的坐标是个世界坐标系点,是视图转换时所使用的。再比如说,把模型从世界坐标系(0,0,0)移动到(-1,0,0)点处,此时依然是世界坐标系点,是模型转换所使用的。
在上面这个例子中,移动照相机,照相机的世界坐标改变,最终照相机与模型之间在x轴上距离为1;移动模型,模型的世界坐标改变,最终模型与照相机之间在x轴上距离为1。也就是说,无论是移动照相机还是移动模型,造成的最终结果是相同的。所以,视图转换与模型转换,本质上相同。所以OpenGL在代码中将这两种转换视为一种,称之为模型视图转换(modelview)。
当需要切换到视图转换或者模型转换时,调用glMatrixMode(GL_MODELVIEW)。