openGL入门教程第五课--视图

我们生活在一个三维的世界——如果要观察一个物体,我们可以:

1、从不同的位置去观察它。(视图变换) 使用gluLookAt函数指定视图变换,这个函数的参数表示照相机(或眼睛)的位置、瞄向、以及那个方向是向上的。如果并没有调用gluLookAt,照相机位于原点,指向z轴的负方向,朝上的向量为(0,1,0)。

2、移动、旋转或缩放。(模型变换)

3、为了在显示器上显示场景中的三维物体,需要把三维物体投影到二维观察平面上。这种将三维物体变为二维图形的变换称为投影变换。(投影变换)

4、把整个看到的图形画下来,但它只占据纸张的一部分,而不是全部。(视口变换)

OpenGL变换实际上是通过矩阵乘法来实现。无论是移动、旋转还是缩放大小,都是通过在当前矩阵的基础上乘以一个新的矩阵来达到目的。

1、 模型变换和视图变换

从“相对移动”的观点来看,改变观察点的位置与方向和改变物体本身的位置与方向具有等效性。在OpenGL中,实现这两种功能甚至使用的是同样的函数。

由于模型和视图的变换都通过矩阵运算来实现,在进行变换前,应先设置当前操作的矩阵为“模型视图矩阵”。设置的方法是以GL_MODELVIEW为参数调用glMatrixMode函数,像这样: glMatrixMode(GL_MODELVIEW); 通常,我们需要在进行变换前把当前矩阵设置为单位矩阵。这也只需要一行代码: glLoadIdentity()。

然后,就可以进行模型变换和视图变换了。进行模型和视图变换,主要涉及到三个函数:

 glTranslate*,把当前矩阵和一个表示移动物体的矩阵相乘。三个参数分别表示了在三个坐标上的位移值。

 glRotate*,把当前矩阵和一个表示旋转物体的矩阵相乘。物体将绕着(0,0,0)到(x,y,z)的直线以逆时针旋转,参数angle表示旋转的角度。

 glScale*,把当前矩阵和一个表示缩放物体的矩阵相乘。x,y,z分别表示在该方向上的缩放比例。

假设当前矩阵为单位矩阵,然后先乘以一个表示旋转的矩阵R,再乘以一个表示移动的矩阵T,最后得到的矩阵再乘上每一个顶点的坐标矩阵v。所以,经过变换得到的顶点坐标就是((RT)v)。由于矩阵乘法的结合率,((RT)v) = (R(Tv)),换句话说,实际上是先进行移动,然后进行旋转。即:实际变换的顺序与代码中写的顺序是相反的。由于“先移动后旋转”和“先旋转后移动”得到的结果很可能不同,初学的时候需要特别注意这一点。

以上都是针对改变物体的位置和方向来介绍的。如果要改变观察点的位置,除了配合使用glRotate*和glTranslate*函数以外,还可以使用这个函数:gluLookAt(GLdoble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble centerz,GLdouble upx,GLdouble upy,GLdouble upz);。它的参数比较多,前三个参数表示了观察点的位置,中间三个参数表示了观察目标的位置,最后三个参数代表从(0,0,0)到(x,y,z)的直线,它表示了观察者认为的“上”方向。up坐标表示观察者本身的方向,如果将观察点比喻成我们的眼睛,那么这个up则表示我们是正立还是倒立异或某一个角度在看,所看的影像不相同。

2、投影变换

投影变换就是定义一个可视空间,可视空间以外的物体不会被绘制到屏幕上。(注意,从现在起,坐标可以不再是-1.0到1.0了!

OpenGL支持两种类型的投影变换,即透视投影和正投影。

如果需要操作投影矩阵,需要以GL_PROJECTION为参数调用glMatrixMode函数。 glMatrixMode(GL_PROJECTION)。

 通常,我们需要在进行变换前把当前矩阵设置为单位矩阵。 glLoadIdentity()。

透视投影则由于和人的视觉系统相似,多用于在二维平面中对三维世界的呈现。

透视投影的原理

基本的透视投影模型由视点E和视平面P两部分构成(要求E不在平面P上)。视点可以认为是观察者的位置,也是观察三维世界的角度。视平面就是渲染三维对象透视图的二维平面。如图1所示。对于世界中的任一点X,构造一条起点为E并经过X点的射线R,R与平面P的交点Xp即是X点的透视投影结果。三维世界的物体可以看作是由点集合 { Xi} 构成的,这样依次构造起点为E,并经过点Xi的射线Ri,这些射线与视平面P的交点集合便是三维世界在当前视点的透视图,

使用glFrustum函数可以将当前的可视空间设置为透视投影空间。

它创建一个透视视景体,其操作是创建一个透视投影矩阵,并且用这个矩阵乘以当前矩阵。这个函数的参数只定义近裁剪平面的左下角点和右上角点的三维空间坐标,即(left,bottom,-near)和(right,top,-near);最后一个参数far是远裁剪平面的Z负值,其左下角点和右上角点空间坐标由函数根据透视投影原理自动生成。near和far表示离视点的远近,它们总为正值。

也可以使用更常用的gluPerspective函数。

void gluPerspective(
  GLdouble fovy,
  GLdouble aspect,
  GLdouble zNear,
  GLdouble zFar
);
fovy是一个角度,此角度以z轴为对称轴(被z轴平分吧);

aspect指的是视锥平面(垂直z轴)的宽高比;

近平面垂直z轴,near是近平面到坐标系中心的距离;

远平面垂直z轴,far是远平面到坐标系中心的距离;

在默认时,以上两个函数的视点都在原点,视线沿Z轴指向负方向。

正射投影,又叫平行投影。这种投影的视景体是一个矩形的平行管道,也就是一个长方体,如图3-5所示。正射投影的最大一个特点是无论物体距离相机多远,投影后的物体大小尺寸不变。

使用glOrtho函数可以将当前的可视空间设置为正投影空间。

3、视口变换

当一切工作已经就绪,只需要把像素绘制到屏幕上了。这时候还剩最后一个问题:应该把像素绘制到窗口的哪个区域呢?通常情况下,默认是完整的填充整个窗口,但我们完全可以只填充一半。

视口变换对应的就是在底片上选择多大的一个图像。你是想要一个钱包大小的还是一个海报大小的照片。窗口系统,不是OpenGL,负责在屏幕上开窗口。当然,默认情况下视口被设置为打开的窗口的整个像素矩形。你使用glViewport()函数选择一个更小的绘制区域;例如,你可以将窗口分割开来,在一个窗口中实现多个视图。

视口变换就是将视景体内投影的物体显示在二维的视口平面上。在计算机图形学中,它的定义是将经过几何变换、投影变换和裁剪变换后的物体显示于屏幕窗口内指定的区域内,这个区域通常为矩形,称为视口。OpenGL中相关函数是:

glViewport ( GLintx, GLinty,GLsizeiwidth,GLsizeiheight );

这个函数定义一个视口。函数参数(x,y)是视口在屏幕窗口坐标系中的左下角点坐标,参数width和height分别是视口的宽度和高度。默认时,参数值即(0,0,winWidth,winHeight),指的是屏幕窗口的实际尺寸大小。所有这些值都是以像素为单位,全为整型数。

4、操作矩阵堆栈

我们在进行矩阵操作时,有可能需要先保存某个矩阵,过一段时间再恢复它。当我们需要保存时,调用glPushMatrix函数,它相当于把矩阵(相当于盘子)放到堆栈上。当需要恢复最近一次的保存时,调用glPopMatrix函数,它相当于把矩阵从堆栈上取下。

 

#include<GL/glut.h> // 太阳、地球和月亮 // 假设每个月都是30天 // 一年12个月,共是360天  static int day = 200; // day的变化:从0到359  void myDisplay(void) {   glEnable(GL_DEPTH_TEST);   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   glMatrixMode(GL_PROJECTION);   glLoadIdentity();   gluPerspective(75, 1, 1, 400000000);   glMatrixMode(GL_MODELVIEW);   glLoadIdentity();   gluLookAt(0, -200000000, 200000000, 0, 0, 0, 0, 0, 1); // 绘制红色的“太阳”   glColor3f(1.0f, 0.0f, 0.0f);   glutSolidSphere(69600000, 20, 20); // 绘制蓝色的“地球”   glColor3f(0.0f, 0.0f, 1.0f);   glRotatef(day/360.0*360.0, 0.0f, 0.0f, -1.0f);   glTranslatef(150000000, 0.0f, 0.0f);   glutSolidSphere(15945000, 20, 20); // 绘制黄色的“月亮”   glColor3f(1.0f, 1.0f, 0.0f);   glRotatef(day/30.0*360.0 - day/360.0*360.0, 0.0f, 0.0f, -1.0f);   glTranslatef(38000000, 0.0f, 0.0f);   glutSolidSphere(4345000, 20, 20);   glFlush();  }

 

  int main(int argc, char *argv[])  {   glutInit(&argc, argv);  glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);  glutInitWindowPosition(100, 100);  glutInitWindowSize(400, 400);   glutCreateWindow("“太阳,地球和月亮系统程序");   glutDisplayFunc(&myDisplay);   glutMainLoop();  return 0;  }

 

OpenGL把三维坐标中的物体绘制到二维屏幕,绘制的顺序是按照代码的顺序来进行的。因此后绘制的物体会遮住先绘制的物体,即使后绘制的物体在先绘制的物体的“后面”也是如此。使用深度测试可以解决这一问题。使用的方法是:1、以GL_DEPTH_TEST为参数调用glEnable函数,启动深度测试。2、在必要时(通常是每次绘制画面开始时),清空深度缓冲,即:glClear(GL_DEPTH_BUFFER_BIT);其中,glClear(GL_COLOR_BUFFER_BIT)与glClear(GL_DEPTH_BUFFER_BIT)可以合并写为: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 且后者的运行速度可能比前者快。
为了让地球和月亮在离我们很近时,我们仍然不需要变换观察点和观察方向就可以观察它们,我们把观察点放在这个位置:(0, -200000000, 0)——因为地球轨道半径为150000000,咱们就凑个整,取-200000000就可以了。观察目标设置为原点(即太阳中心),选择Z轴正方向作为“上”方。当然我们还可以把观察点往“上”方移动一些,得到(0, -200000000, 200000000),这样可以得到45度角的俯视效果。gluLookAt(0, -200000000, 200000000, 0, 0, 0, 0, 0, 1)。
为了得到透视效果,我们使用gluPerspective来设置可视空间。假定可视角为60度(如果调试时发现该角度不合适,可修改之。我在最后选择的数值是75。),高宽比为1.0。最近可视距离为1.0,最远可视距离为200000000*2=400000000。即:gluPerspective(60, 1, 1, 400000000);
画出来的结果如下:
openGL入门教程第五课 - River - bowen_tong的博客
 学的真吃力啊。但是好消息是教程里说下面很长一段内容都是轻松愉快的了,期待。openGL入门教程第五课 - River - bowen_tong的博客
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值