现在的位置: 首页 > 综合 > 正文

Win32 OpenGL编程(9) 投影变换

2013年08月26日 ⁄ 综合 ⁄ 共 6573字 ⁄ 字号 评论关闭

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表示此矩形的前,后两面,超出此矩形范围的图形将会被裁掉。

OpenGL Programming Guide
》:

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);
Parameters

left, right

                        Specify the coordinates for the left and right vertical clipping planes.
bottom, top

                        Specify the coordinates for the bottom and top horizontal clipping planes.
nearVal, farVal

                        Specify 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;
//这里进行所有的绘图工作
void 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();
}

///////////////////////////////////////////////////////////

int Game_Main(void *parms = NULL, int num_parms = 0)
{
DWORD dwStartTime;

dwStartTime = GetTickCount();
// this is the main loop of the game, do all your processing
// here

// for now test if user is hitting ESC and send WM_CLOSE
if (KEYDOWN(VK_ESCAPE))
SendMessage(ghWnd,WM_CLOSE,0,0);

if (KEYDOWN(VK_UP))
{
gfNear -= 0.01;
}

if (KEYDOWN(VK_DOWN))
{
gfNear += 0.01;
}


SceneShow();


// 控制帧率
while (GetTickCount() - dwStartTime < TIME_IN_FRAME)
{
Sleep(1);
}


// return success or failure or your own return code here
return (1);

} // end Game_Main
以下就是当nearVal为0.8时的效果,也就是说,Z轴坐标大于0.8的,一律被截掉了,在工程制图中,我们老师常将,此时假设在此用一刀将物体切掉。
上面的源代码中还出现了一个新函数,glMatrixmode,用于指定以下的变换是针对哪个变换的,

glMatrixMode(GL_PROJECTION);

用于指定以下的变换是针对投影变换的,而用
glMatrixMode(GL_MODELVIEW);
方式调用时,(默认值)表示针对模型变换。
为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2008-10-28/glOrthoSample 目录,获取方式见文章最后关于获取博客完整源代码的说明。
 
image

 

透视投影

上述的正投影仅仅存在于想象之中,主要是工程制图(或者CAD)中能够正确反映物体的真实尺寸,现实中我们看东西,越远的越小,越近的越大,并且所有物体最终会消失在远方(地平线),OpenGL常用于VR(虚拟现实),这样程度的视觉仿真效果自然是有的,在OpenGL中,这样投影叫做透视投影。OpenGL提供了两个函数用于指定透视投影,两个函数仅仅是参数表示的方式不同,事实上效果是一样的。

OpenGL Programming Guide
》:

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);

Parameters

left, right

                        Specify the coordinates for the left and right vertical clipping planes.

bottom, top

                        Specify the coordinates for the bottom and top horizontal clipping planes.

nearVal, farVal

                        Specify 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);

Parameters

fovy

                        Specifies the field of view angle, in degrees, in the y direction.

aspect

                        Specifies 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).

zNear

                        Specifies the distance from the viewer to the near clipping plane

                        (always positive).

zFar

                        Specifies 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;

//这里进行所有的绘图工作
void 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();
}

int Game_Main(void *parms = NULL, int num_parms = 0)
{
DWORD dwStartTime;

dwStartTime = GetTickCount();
// this is the main loop of the game, do all your processing
// here

// for now test if user is hitting ESC and send WM_CLOSE
if (KEYDOWN(VK_ESCAPE))
SendMessage(ghWnd,WM_CLOSE,0,0);

if (KEYDOWN(VK_UP))
{
gfDis -= 0.01f;
}

if (KEYDOWN(VK_DOWN))
{
gfDis += 0.01f;
}

SceneShow();

// 控制帧率
while (GetTickCount() - dwStartTime < TIME_IN_FRAME)
{
Sleep(1);
}

// return success or failure or your own return code here
return (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);

三句确定的透视投影方式。

 image
 image
image

 

 

 

 

为节省篇幅仅贴出关键片段,完整源代码见我博客源代码的2008-10-28/glPerspective 目录,获取方式见文章最后关于获取博客完整源代码的说明。

 

小结:

相对来说,个人认为投影变换是属于OpenGL中重要但是确较难理解的一部分,动态变化上述函数的参数,准确理解上述函数中每个参数的意义是种不错的学习途径,其中Nate Robin的OpenGL教程有个很形象的教学程序,推荐给大家(实际上为《OpenGL Programming Guide
》中推荐的),http://www.xmission.com/~nate/tutors.html
中有下载的。对应投影变换的教程为projection一例。

 

 

参考资料

1. 《OpenGL Reference Manual
》,OpenGL参考手册

2. 《OpenGL
编程指南》(《OpenGL Programming Guide
》),Dave Shreiner,Mason Woo,Jackie Neider,Tom Davis
著,徐波译,机械工业出版社

3. 《Nehe OpenGL Tutorials》,Nehe著,在http://nehe.gamedev.net/
上可以找到教程及相关的代码下载,(有PDF版本教程下载)Nehe自己还做了一个面向对象的框架,作为演示程序来说,这样的框架非常合适。也有中文版
,各取所需吧。

4. 《OpenGL入门学习》 ,eastcowboy著,这是我在网上找到的一个比较好的教程,较为完善,而且非常通俗。这是第一篇的地址:http://bbs.pfan.cn/post-184355.html

本OpenGL系列其他文章

1.
Win32 OpenGL 编程(1)Win32下的OpenGL编程必须步骤

2. 《Win32 OpenGL编程(2) 寻找缺失的OpenGL函数

3. 《Win32 OpenGL编程(3) 基本图元(点,直线,多边形)的绘制

4. 《Win32 OpenGL编程(4) 2D图形基础(颜色及坐标体系进阶知识)

5. 《Win32 OpenGL编程(5)顶点数组详细介绍

6.《Win32 OpenGL编程(6) 踏入3D世界

7.《Win32 OpenGL编程(7) 3D视图变换——真3D的关键

8.《Win32 OpenGL编程(8) 3D模型变换及其组合应用

应用举例:《Win32 OpenGL编程系列 2D例子 -- 七巧板图形绘制

 

 

 

 

完整源代码获取说明

由于篇幅限制,本文一般仅贴出代码的主要关心的部分,代码带工程(或者makefile)完整版(如果有的话)都能用Mercurial在Google Code中下载。文章以博文发表的日期分目录存放,请直接使用Mercurial克隆下库:

https://blog-sample-code.jtianling.googlecode.com/hg/

Mercurial使用方法见《分布式的,新一代版本控制系统Mercurial的介绍及简要入门

要是仅仅想浏览全部代码也可以直接到google code上去看,在下面的地址:

http://code.google.com/p/jtianling/source/browse?repo=blog-sample-code

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie

抱歉!评论已关闭.