Win32 OpenGL编程(9) 投影变换
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
提要
在前文(系列文章(7),以下简称XO7,系列其他文章类似)中的照相机比喻中提到了4种3D变换,如下:
1.确定照相机的位置的过程对应于“视图变换”(Viewing Transformations)
2.确定物体位置的过程对应于“模型变换”(Modeling Transformations)
3.确定照相机放大倍数的过程对应于“投影变换”(Projection Transformations)
4.确定照片大小的过程对应于“视口变换”(Viewport Transformations)
XO7中我们讲的是第一种变换视图变换,即改变观察者本身的位置,视角等的变换效果,XO8中讲的是第二种变换模型变换,本文开始继续按顺序讲解下一个3D变换过程,投影变换。
正投影
投影变换的过程就像是照相机选镜头的过程,目的是确定视野,其实说起来投影一词,我在学习工程制图的时候就接触过了,不知道大家是否学过这门课程,工程制图就是一种将三维空间的事物设想投影在二维空间中,然后画下来,工程中应用非常广泛,那时候学的那些剖面图什么的,也是累死我了-_-!OpenGL就可以模拟这样的过程,并且名字和工程制图中的名字是一样的,叫正投影。
OpenGL以glOrtho来指定一个正交平行的矩形,屏幕上显示的就是此物体在此矩形的正面的投影。在XO2中用过的gluOrtho2D实际上是此函数的一个去掉Z轴坐标的简化版,而glOrtho包括的参数nearVal,farVal表示此矩形的前,后两面,超出此矩形范围的图形将会被裁掉。
glOrtho — multiply the current matrix with an orthographic matrix
C Specification
void glOrtho( GLdouble left,
GLdouble right,
GLdouble bottom,
GLdouble top,
GLdouble nearVal,
GLdouble farVal);
Parametersleft, right
Specify the coordinates for the left and right vertical clipping planes.
bottom, topSpecify the coordinates for the bottom and top horizontal clipping planes.
nearVal, farValSpecify the distances to the nearer and farther depth clipping planes.
These values are negative if the plane is to be behind the viewer.
用此函数,通过调整nearVal与farVal我们可以实现类似工程制图中的剖面的效果,见下例:
GLfloat gfNear = 1.0; SceneShow(GLvoid) { glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0, 0.0, 0.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1.0, 1.0, -1.0, 1.0, gfNear, -1.0); glPushMatrix(); DrawSmoothColorPyramid(0.5); glPopMatrix(); glFlush(); } Game_Main( *parms = NULL, num_parms = 0) { DWORD dwStartTime; dwStartTime = GetTickCount(); (KEYDOWN(VK_ESCAPE)) SendMessage(ghWnd,WM_CLOSE,0,0); (KEYDOWN(VK_UP)) { gfNear -= 0.01; } (KEYDOWN(VK_DOWN)) { gfNear += 0.01; } SceneShow(); (GetTickCount() - dwStartTime < TIME_IN_FRAME) { Sleep(1); } (1); }
以下就是当nearVal为0.8时的效果,也就是说,Z轴坐标大于0.8的,一律被截掉了,在工程制图中,我们老师常将,此时假设在此用一刀将物体切掉。
上面的源代码中还出现了一个新函数,glMatrixmode,用于指定以下的变换是针对哪个变换的,
glMatrixMode(GL_PROJECTION);
用于指定以下的变换是针对投影变换的,而用
glMatrixMode(GL_MODELVIEW);
方式调用时,(默认值)表示 针对模型变换。
为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2008-10-28/glOrthoSample 目录,获取方式见文章最后关于获取博客完整源代码的说明。
透视投影
上述的正投影仅仅存在于想象之中,主要是工程制图(或者CAD)中能够正确反映物体的真实尺寸,现实中我们看东西,越远的越小,越近的越大,并且所有物体最终会消失在远方(地平线),OpenGL常用于VR(虚拟现实),这样程度的视觉仿真效果自然是有的,在OpenGL中,这样投影叫做透视投影。OpenGL提供了两个函数用于指定透视投影,两个函数仅仅是参数表示的方式不同,事实上效果是一样的。
glFrustum — multiply the current matrix by a perspective matrix
C Specification
void glFrustum( GLdouble left,
GLdouble right,
GLdouble bottom,
GLdouble top,
GLdouble nearVal,
GLdouble farVal);
Parametersleft, right
Specify the coordinates for the left and right vertical clipping planes.
bottom, topSpecify the coordinates for the bottom and top horizontal clipping planes.
nearVal, farValSpecify the distances to the near and far depth clipping planes.
Both distances must be positive.gluPerspective — set up a perspective projection matrix
C Specification
void gluPerspective( GLdouble fovy,
GLdouble aspect,
GLdouble zNear,
GLdouble zFar);
Parametersfovy
Specifies the field of view angle, in degrees, in the y direction.
aspectSpecifies the aspect ratio that determines
the field of view in the x direction.
The aspect ratio is the ratio of x (width) to y (height).
zNearSpecifies the distance from the viewer to the near clipping plane
(always positive).
zFarSpecifies the distance from the viewer to the far clipping plane
(always positive).
事实上,glFrustum指定的方式与glOrtho很像,参数意义也一致,仅仅是使用后OpenGL使用的投影方式不同,gluPerspective函数指定的方式是模拟观察者(照相机)的视角,用fovy指定观察者的上下视角(及在y-z平面上观察的角度),aspect指定观察宽度与高度的比,然后用zNear指定离观察者较近的截面,用zFar指定离观察者较远的截面(都指离观察者的距离),下例展示了原来那个三角锥从远方的一个小点然后到放大到原来大小的过程:
GLfloat gfDis = 5.0; SceneShow(GLvoid) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 5.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0.0, 0.0, gfDis, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0, 0.0, 0.0); glPushMatrix(); DrawSmoothColorPyramid(0.5); glPopMatrix(); glFlush(); } Game_Main( *parms = NULL, num_parms = 0) { DWORD dwStartTime; dwStartTime = GetTickCount(); (KEYDOWN(VK_ESCAPE)) SendMessage(ghWnd,WM_CLOSE,0,0); (KEYDOWN(VK_UP)) { gfDis -= 0.01f; } (KEYDOWN(VK_DOWN)) { gfDis += 0.01f; } SceneShow(); (GetTickCount() - dwStartTime < TIME_IN_FRAME) { Sleep(1); } (1); } // end Game_Main
上面例子中靠
gluLookAt(0.0, 0.0, gfDis, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0);
来调整观察者的位置,通过上下键调节,会模拟出一种慢慢走近三角锥的过程,三角锥也是随着走近由小慢慢变大。
实际上最重要的还是
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 5.0);
三句确定的透视投影方式。
为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2008-10-28/glPerspective 目录,获取方式见文章最后关于获取博客完整源代码的说明。
小结:
相对来说,个人认为投影变换是属于OpenGL中重要但是确较难理解的一部分,动态变化上述函数的参数,准确理解上述函数中每个参数的意义是种不错的学习途径,其中Nate Robin的OpenGL教程有个很形象的教学程序。对应投影变换的教程为projection一例