应用LED光源的照明系统的模拟显示程序设计_第1页
应用LED光源的照明系统的模拟显示程序设计_第2页
应用LED光源的照明系统的模拟显示程序设计_第3页
应用LED光源的照明系统的模拟显示程序设计_第4页
应用LED光源的照明系统的模拟显示程序设计_第5页
已阅读5页,还剩20页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、应用LED光源的照明系统的模拟显示程序设计     第一章 绪  论本章的主要目的是介绍OpenGL的一些基本知识,包括OpenGL的功能和特性,以便于进一步的讲述。 1.1 毕业设计概述随着时代的发展,人们对于图像处理的要求日益增高。2D平面的图像处理已经不能满足人们视觉的需要,于是出现了大量的3D处理引擎与接口。其中,OpenGL(即开放性图形库 Open Graphics Library)是近几年发展起来的一个性能卓越的三维图形标准,它源于SGI公司为其图形工作站开发的IRIS GL,在跨平台移植过程中发展成

2、为OpenGL。OpenGL作为一个性能优越的图形应用程序设计界面(API),具有广泛的可移植性。OpenGL适用于广泛的计算机环境,从个人计算机到工作站和超级计算机,用户都可以利用OpenGL创建漂亮的三维图形。由于许多在计算机界具有领导地位的计算机公司纷纷采用OpenGL作为三维图形应用程序设计界面,因此,OpenGL是从事三维图形开发工作的技术人员所必须掌握的开发工具。利用OpenGL,可以绘制有各种不同效果的交互式三维动画场景,在建筑学,医学,CAD,以及广告业娱乐业中,已经开发出了很多实用的OpenGL程序。OpenGL属于较为底层的语言,利用OpenGL绘制三维场景可能需要大量的程

3、序代码,但是其灵活度却比较强,由于OpenGL给予的接口比较基础,要实现较为复杂的三维场景,需要将很多基础的函数进行复杂的组合。所以直接用OpenGL算法实现复杂三维场景的绘制已经不能很好的满足现在的需要。现在最常用的方法是,利用三维建模软件,如3DMAX,MAYA,建筑模型,然后在OpenGL中导入模型数据,再利用OpenGL的处理方法实现互动效果。在这次的毕业设计中,由于所需要实现的模型不是非常复杂,所以我采用的是直接计算的方法,利用数学函数计算整个模型的数据,具体方法在下面会有详细地介绍。当然制作三维模型不单要计算模型的数据,还要涉及到多个方面,比如模型的法向量,光照效果,环境光设置,人

4、机交互等等,这些功能的实现,不仅要对OpenGL有必要的了解,而且涉及到一些数学方法和基本的物理知识;而对于人机交互的实现,由于OpenGL提供的函数库里面有一些基本的人机交互函数,但是由于OpenGL的跨平台性限制了它功能的强大,所以我在这里主要是利用到了.NET平台提供方法,在MFC中调用OpenGL函数实现图形绘制,而利用MFC编程实现对窗口的交互方法。这里需要提出的是,OpenGL的现实模式与.NET的现实模式不同,再MFC调用OpenGL的时候需要设置窗口像素属性,这些在下面也会有详细地介绍。在整个学习和制作过程中,由于是初涉类似OpenGL的实现形式,而且对C+语言也只停留在理论阶

5、段,所以遇到了很多不可预计的困难,但是在老师和朋友的帮助下,还是在预定的时间里完成了设计,同时也对这方面的知识产生了浓厚的兴趣,希望在以后的生活工作中可以有更深一步的学习。1.2 OpenGL主要特性OpenGL有很多优良的特性,这些特性包括:l 工业标准  OpenGL的规范由独立的国际协会OpenGL体系结构审查委员会进行指导,因此,OpenGL是唯一开放的、中立于制造商的、能够运行在多个平台上的图形标准。l 稳定性  OpenGL在各种平台上的实现时间已经超过了十年,除了OpenGL规范得到良好的控制外,建议的更新也及时的通知给开发商,而且Open

6、GL向后兼容,确保已有的应用程序在版本更新后不过时。l 可靠性 不管在什么样的操作系统或窗口系统上运行,任何OpenGL的应用程序在任何兼容OpenGL API的硬件上都会显示出一致的视觉效果。l 可扩展性 OpenGL的设计考虑得非常周到,兼顾到了以后的升级。应用开发者和硬件厂商能够利用OpenGL API中的扩展机制支持硬件的各种新特征。l 可扩充性 基于OpenGL API的程序能够运行在从消费电器到PC机,工作站,甚至超级计算机上,因此,OpenGL程序可以扩充到开发者选择的任何机器类型上运行。l 易用性 OpenGL命令函数具有很强的逻辑性,因

7、此开发者可以凭直觉开发三维场景,并用OpenGL编制图形软件与使用其他图形库相比,代码良要小得多。另外,OpenGL驱动程序封装了底层硬件信息,从而使应用程序开发人员无须考虑指定硬件的特点。目前,OpenGL支持的平台包括Windows系列,OS/2,Unix以及Solaris等操作系统。1.3 OpenGL主要功能OpenGL能够声称逼真的三维图像,从绘制简单的2D物体到复杂的3D场景,OpenGL都能够高效地完成绘制。l 创建二维和三维物体 任何复杂的形体都可以通过点线面组合形成,OpenGL中提供了点线面的定义方式,并且提供了对物体着色的函数,通过它们可以绘制各种形体。l

8、60;布置场景并以适当的角度,适当的方式观看场景 OpenGL中可以通过平移,旋转,缩放函数对视角进行选择。l 在场景中引入光线是绘制的图形有真实感和立体感 OpenGL中可以设置光照,通过面和点的法向量自动计算追踪光线,形成真实的光照感。l 通过纹理映射使物体更加真实 OpenGL可以通过纹理函数对物体表面进行纹理映射,使物体看起来具有表面纹理,更加真实。l 实现特殊效果,如半透明,烟雾等l 绘制位图和图像l 制作动画,提供交互技术 OpenGL中提供了双缓存来实现动画桢的平滑过渡,OpenGL库中也提供了简单的人机交互函数来实现用户的鼠标键

9、盘操作。1.4 设计任务说明这次的设计任务是使用OpenGL制作一个车头灯的外型显示。总体外型为一个旋转抛物面(圆形),然后对此抛物面进行切割,两个底面圆形,两个侧面的抛物形,最后加入LED光源。然后给这个模型添加人机互动,按住左键拖拉对模型进行旋转;按住右键拖拉对模型进行平移;键盘上下键对模型在屏幕纵深方向前移后移;键盘左右键控制模型的形成步骤,循环。1.5 设计任务分析首先,我选用的是现在流行的.NET平台作为程序的基础,因为.NET平台可以使整个的程序设计变得简单很多。其次,在选用制作模型的方法的时候,有两种作为备选方案。其一是使用SOLIDWORKS做二次开发,其二是使用OpenGL直

10、接制作模型。实际上,二次开发的方法比较简单,尤其是对于制作模型这一过程,但是它对于人机交互的支持不是很好;使用OpenGL制作模型有两种方法,直接制作或者是通过导入数据来制作模型,对于比较复杂、不规则的模型,如果使用直接制作的方法比较复杂或者是根本不可能完成,所以只能通过外部程序,如3DMax等软件制作出整个模型,然后在OpenGL中导入模型数据,直接使用这些数据进行建模。但是对于这次的毕业设计,模型的形状比较规则,所以我采用的是直接建模的方法,这里面的关键技术就是如何建立数学表达,并通过一定的算法表现出来。最后,要对模型插入人机交互的方法,此方法的实现是基于.NET平台的,只需要改变一定的参

11、数,然后在OpenGL函数中调用此参数即可。此过程比较简单,后面的章节有较为详细的说明。 第二章 OpenGL的实现方法2.1 OpenGL的图形操作OpenGL对图形进行操作并最终将图形绘制到屏幕的过程如下:1. 利用几何原图(点、线和多边形)构造物体模型,然后建立物体的数学描述。2. 在三维空间中选择合适的位置放置物体,并确定观看场景的最佳视点。3. 计算场景中所有物体的颜色。物体的颜色可能来自于程序中的设定,如果在场景中使用了光照和纹理映射,那么所观察到的场景中物体的最终效果还取决于光照条件和纹理映射方式。4. 把场景中物体模型的数学描述及颜色信息

12、转换到计算机屏幕的像素点上,这个过程也就是光栅化。在这一系列的步骤中,OpenGL可能还要进行其他的操作,比如隐藏面的摘除。此外,在场景被光栅化后,将场景绘制到屏幕上之前,还可以根据需要对像素进行操作。2.2 OpenGL的函数库OpenGL的核心函数均以gl开头,这些函数是OpenGL中最基本的命令函数,利用这些函数可以执行顶点操作,定义光照及颜色,生成纹理坐标,执行矩阵转换及像素操作等等。此外,系统还提供了与OpenGL相关的库函数,这些库函数为:1. OpenGL实用库(GLU)。GLU库包含了一些实现核心OpenGL接口的命令函数,它们是比OpenGL核心函数更高一层的库函数。GLU库

13、中的函数均以glu作为前缀2. OpenGL辅助库(GLAUX)。辅助库中提供了一些基本的窗口管理和简单形体的绘制函数,是为初学者提供的简化程序设计任务所设计的。GLAUX库中的函数均以glaux作为前缀3. OpenGL GLUT库。GLUT库是与窗口系统独立的工具包,它已经被移植到各种各样的OpenGL实现之上,GLUT库对于OpenGL程序设计人员来说已经成为一个普及的库,由于OpenGL没有专门的窗口管理函数和输入事件管理函数,而GLUT库则提供了管理事件的命令函数。GLUT库中的函数均以glut作为前缀。4. OpenGL对windows窗口系统的扩展(WGL)。这里包括WGL函数和

14、WIN32函数,WGL函数可以让程序员创建和选择OpenGL绘制描述表,交换前后缓存等等,WIN32库函数用来初始化像素格式,控制绘制,同时支持访问OpenGL扩展所必需的设备。WGL函数均以wgl作为前缀。5. OpenGL对X窗口系统的扩展(GLX)。这个库函数提供创建OpenGL绘制描述表并将其与X窗口系统连接起来的函数,它的库函数均以glx开头。 第三章 M FC调用OpenGL函数的方法这一章中,我将着重介绍如何创建一个显示OpenGL模型的MFC单文本结构。3.1 OpenGL的像素格式每一个WINDOWS程序都必须处理设备描述表,在创建设备描述表之前必须设置设备的像素格

15、式,该设置包括了绘制所需要的各种属性,如绘制时像素数据是使用RGBA颜色值还是使用颜色索引值,所存储的颜色值的位数,像素缓存是使用单缓存还是双缓存,在深度缓存中所使用的数据位数以及其他的OpenGL图形信息。进行OpenGL绘制的窗口必须设置像素格式,这是因为缺省的像素格式是为WINDOWS的GDI设置的。对一个窗口只能设置一次像素格式,一般来说,像素格式的设置过程放在OnCreate函数中实现。设置结构如下:typedef struct tagPIXELFORMATDESCRIPTOR WORD nSize; WORD nVersion; DWORD dwFla

16、gs; BYTE iPixelType; BYTE cColorBits; BYTE cRedBits; BYTE cRedShift; BYTE cGreenBits; BYTE cGreenShift; BYTE cBlueBits; BYTE cBlueShift; BYTE cAlphaBits; BYTE cAlphaShift; BYTE cAccumBits; BYTE cAccumRedBits; BYTE cAccumGreenBits; 

17、;BYTE cAccumBlueBits; BYTE cAccumAlphaBits; BYTE cDepthBits; BYTE cStencilBits; BYTE cAuxBuffers; BYTE iLayerType; BYTE bReserved; DWORD dwLayerMask; DWORD dwVisibleMask; DWORD dwDamageMask; PIXELFORMATDESCRIPTOR;下面对结构体成员的含义作简要地说明。nSize:说明该数据结构的长度。nVersio

18、n:说明该数据结构的版本,此值应设置为1。dwFlags:制定像素缓存的属性,该属性可以用下面这些常量的逻辑或来表示,其中PFD_SUPPORT_GDI与PFD_DOUBLEBUFFER互斥。PFD_DRAW_TO_WINDOWS把绘制结果输出到窗口;PFD_DRAW_TO_BITMAP把绘制结果输出到位图;PFD_SUPPORT_GDI支持GDI绘制;PFD_SUPPORT_OPENGL支持OpenGL绘制;PFD_GENERIC_ACCELERATED当有硬件加速驱动程序支持时,设置该项;PFD_GENERIC_FORMAT普通格式;PFD_DOUBLEBUFFER像素缓存为双缓存结构;P

19、FD_STEREO像素缓存为立体结构;PFD_SWAP_LAYER_BUFFERS在双缓存的多层面像素格式中,指示设备是否能一次只交换一个层面,否则,所有的层面都作为一组一起交换。iPixelType:说明像素数据的类型,包括PFD_TYPE_RGBA和PFD_TYPE_COLORINDEX,前者指定RGBA模式,后者制定颜色索引模式,两种类型只能选择其一。cColorBits:说明颜色缓存中颜色位平面的个数。cRedBits、cRedShift、cGreenBits、cGreenShift、cBlueBits、cBlueShift:不使用,置为0。cAlphaBits:说明颜色缓存中Alph

20、a位平面的个数。cAlphaShift:不使用,设置为0。cAccumBits:说明累计缓存位平面的个数。cDepthBits:说明深度缓存的位数。cStencilBits:说明模板缓存的位数。cAuxBuffers:说明辅助缓存的个数,一般设置为0。其余各属性设置为0。3.2 绘制描述表windows应用程序与窗口有关的操作都需要处理设备描述表。设备描述表示一个结构,它定义了一组图形对象、和该对象相关的属性以及影响输出的图形模式,所有这些定义都影响下一步调用该设备描述表对窗口的操作,包括画笔的颜色,填充模式,调色板设定,映射模式以及其他图形信息。同样,调用OpenGL函数的每个线程都要有一个

21、当前的绘制描述表,该描述表示传递所有OpenGL命令的端口,它把OpenGL与windows 窗口系统连接起来。绘制描述表与设备描述表具有相同的像素格式,但绘制描述表包含有OpenGL信息。绘制描述表必须在设备描述表之上创建。在获得一个设备描述表之后,首先设置它的像素格式,然后在其上创建一个绘制描述表,该过程由wglCreateContext函数创建。然后调用wglMakeCurrent函数使得该绘制描述表成为当前,在使用OpenGL函数绘制场景。当绘制结束后,把绘制描述表从线程上脱离,并调用wglDeleteContext删除它。线程在调用任何OpenGL函数之前,都必须设置一个当前绘制描述

22、表,否则,所有的OpenGL调用都被忽略。3.3 模拟车灯模型的MFC初始化这次设计我选择的是.NET平台下的C+ MFC编程,初始化的实现过程如下: 1选择“文件”下的“新建项目”,如下图:              图3. 3. 1在随后出现的窗口“MFC应用程序向导”中,将“高级功能”中的ActiveX控件勾掉,如下图           &

23、#160;    图3. 3. 22重载PreCreateWindow函数:打开OpenGLView.cpp文件,在属性窗口中选择“重写”选项卡,编写代码重载PreCreateWindow函数,如下图:              图3. 3. 3代码如下:BOOL COpenGLView:PreCreateWindow(CREATESTRUCT& cs) / TODO: 在此处通过修改 CREATE

24、STRUCT cs 来修改窗口类或 / 样式 cs.style=cs.style|WS_CLIPSIBLINGS|WS_CLIPCHILDREN;    return CView:PreCreateWindow(cs);3添加响应WM_CREATE的消息处理函数,并加入设置像素格式和创建绘制描述表的代码。打开OpenGLView.cpp,在属性窗口选择“消息”选项卡,编写响应WM_CREATE的函数OnCreate(),如下图:          

25、;图 3. 3. 4编写代码如下:int COpenGLView:OnCreate(LPCREATESTRUCT lpCreateStruct)    if (CView:OnCreate(lpCreateStruct) = -1)        return -1;    / TODO:  在此添加您专用的创建代码    m_pDC=new CClientDC(this);    ASSERT(

26、m_pDC!=NULL);    if(!bSetupPixelFormat()        return -1;        m_hRC=wglCreateContext(m_pDC->GetSafeHdc();    wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC);    return 0;下面我解释一下这段代码:首先创建

27、一个设备描述表m_pDC=new CClientDC(this),然后调用设置像素格式的函数bSetupPixelFormat(),如果调用失败则返回,如果设置像素格式成功的话,即在设备描述表之上创建一个绘制描述表m_hRC=wglCreateContext(m_pDC->GetSafeHdc(),然后使用wglMakeCurrent将绘制描述表m_hRC设置为当前.4. 在OpenGLView.h类定义中添加public属性声明,如下public:    CClientDC *m_pDC;    HGLRC m_hR

28、C;    CRect m_rect;double m_left,m_right,m_bottom,m_top,m_near,m_far;其中,最后一行是用来定义视景体属性的,这部分在后面会有介绍然后在OpenGLView.h中声明public方法BOOL bSetupPixelFormat(void),用来进行像素格式的设置,并在OpenGLView.cpp中定义这个函数,如下:BOOL COpenGLView:bSetupPixelFormat()    static PIXELFORMATDESCRIPTOR pfd= 

29、;           sizeof(PIXELFORMATDESCRIPTOR),  /size of this pfd            1,  /viersion number            PFD_DRAW_TO_WINDOW| /support w

30、indow            PFD_SUPPORT_OPENGL| /support OpenGL            PFD_DOUBLEBUFFER,   /double buffered            PFD_TYPE_RGBA

31、,      /RGBA type            24,            0,0,0,0,0,0,            0,      

32、0;     0,            0,            0,0,0,0,            32,         

33、60;  0,            0,            PFD_MAIN_PLANE,            0,            0

34、,0,0                ;        int pixelformat;        if (pixelformat=ChoosePixelFormat(m_pDC->GetSafeHdc(),&pfd)=0)         

35、;   MessageBox("ChoosePixelFormat failed!");        return FALSE;            if(SetPixelFormat(m_pDC->GetSafeHdc(),pixelformat,&pfd)=FALSE)          

36、;  MessageBox("SetPixelFormat failed!");        return FALSE;        return TRUE;这段程序的开头是来设置PIXELFORMATDESCRIPTOR结构体的,后面程序比较简单,这里就不解释了。然后在OpenGLView.cpp文件开始的部分,加入下面代码:#include<glgl.h>#include<glglu.h>#include

37、<glglaux.h>#include<glglut.h>5添加响应WM_SIZE的消息处理函数,并加入相应的代码。同样的,打开OpenGLView.cpp文件,选择属性窗口的“消息”选项卡,编写OnSize()函数,如下图:          图 3. 3. 5代码如下:void COpenGLView:OnSize(UINT nType, int cx, int cy)    CView:OnSize(nType, cx, cy);

38、60;   / TODO: 在此处添加消息处理程序代码    wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC);    m_left=-cx/2.0;    m_right=cx/2.0;    m_bottom=-cy/2.0;    m_top=cy/2.0;    m_near=-1000;    m_far=1000;&

39、#160;   glMatrixMode(GL_PROJECTION);    glLoadIdentity();    gluPerspective(60.0,m_rect.right/m_rect.bottom,50,1000.0);    glMatrixMode(GL_MODELVIEW);    glLoadIdentity();    glViewport(0,0,cx,cy);   

40、 wglMakeCurrent(NULL,NULL);下面我来解释一下此段代码,首先,我上文提到过,线程在调用任何OpenGL函数之前,必须设置一个当前绘制描述表,既是代码开头的wglMakeCurrent()函数。这段程序完成的任务是,在改变窗口大小的时候,也相应的改变视景体的尺寸,使得物体不会变形,视景体的定义在以后的章节中会有相应的介绍。6用类似的方法添加响应WM_ERASEBKGND的消息处理函数,由于OpenGL用自己的glClear函数进行相应的功能,所以要注释掉原来的代码,仅返回true值,修改后的代码如下:BOOL COpenGLView:OnEraseBkgnd(CDC* p

41、DC)    / TODO: 在此添加消息处理程序代码和/或调用默认值   / return CView:OnEraseBkgnd(pDC);    return TRUE;注意将第二行注释掉了。7用类似的方法添加响应WM_DESTROY的消息处理函数,代码如下:void COpenGLView:OnDestroy()    CView:OnDestroy();    / TODO: 在此处添加消息处理程序代码    wglM

42、akeCurrent(NULL,NULL);    wglDeleteContext(m_hRC);    delete m_pDC;此段代码的意义也比较明显,这里就不进行解释了。至此为止,对于MFC调用OpenGL的初始化已经基本完成。 第四章 空间环境的设置OpenGL空间环境的设置包括有:视景体的定义,光源的定义,还有一些其他方面的声明,比如打开深度检测和法向量等,这些设置都可以在重载函数OnInitialUpdata中设置,在这一章里我会以这次的毕业设计为例作一些说明。4.1 两种投影方式绘制三维物体后需要在平面显示

43、器上显示出来,因此需要通过投影的方式降低物体的维数为二维。OpenGL中的投影方式有两种正视投影和透视投影,它们之间的区别是比较明显的。所谓的透视投影就是我们平时看物体时的效果,离我们远的物体看起来小,离我们近的物体看起来大,投影原理如下图:              图4. 1. 1一个三维物体经过透视投影显示在屏幕上,经由我们的眼睛看到就如同在真实世界里面的效果是一样的,一般来说,透视投影用在虚拟现实技术中来模拟真实的视觉效果。所谓的正视投影就是不考虑物体的远近

44、,在屏幕上把物体的真实尺寸表现在屏幕上,其投影原理我们很容易想象。一般来说,正视投影是用在工程方面,不考虑远近的问题,直接显示物体的真实尺寸。在这次的毕业设计中,我使用的是透视投影,来更好的模拟车灯在空间中的运动效果。OpenGL用定义不同的视景体来实现两种不同的投影方式。4.2 定义视景体在定义视景体之前,有必要了解一下OpenGL中视景体的含义。我们现实生活中肉眼看到的物体实际上是被限制在一个范围的,也就是说,在离开这个范围之外的物体是不能被视觉感知的,OpenGL中把这个范围成为视景体,定义视景体的过程实际上也就是定义一个范围,在这个范围之外的物体是看不到的。一般来说,OpenGL定义视

45、景体有三个函数,glFrustum()和gluPerspective()对应建立透视投影中的视景体,glOrtho()对应建立平行的视景体,也就是正视投影中的视景体。这次的毕业设计中,我使用的是gluPerspective()函数,下面我对这个函数做一些说明:gluPerspective()函数的命令原型为:void gluPerspective(double fovy, double aspect, double znear, double zfar);函数说明:该函数构造了一个对称的视景体,变量fovy制定了x-z平面上的视线角范围,取值范围为0180;aspect为视景体的宽度和高度的纵

46、横比;znear和zfar变量定义了视点到近切割面和远切割面的距离,它们应该为正值。视景体的定义放在重载函数OnInitialUpdata()中,添加代码如下:    glMatrixMode(GL_PROJECTION);    glLoadIdentity();    gluPerspective(60.0,m_rect.right/m_rect.bottom,50,1000.0);说明:glMatrixMode()用来指明操作的是哪个矩阵,这里的常量GL_PROJECTION表明下面的两条语句是对投

47、影矩阵进行操作;glLoadIdentity()是将矩阵的内容装载为单位矩阵,由于OpenGL对图形的操作实际上都是对矩阵进行运算,然后将结果存储在相应的矩阵中,所以,如果不清空为单位矩阵的话,那么后面的矩阵运算都是对原来已经运算后得出的结果矩阵进行操作,这样得不到我们需要的效果。4.3 定义光源OpenGL中对于光源的定义会显得比较重要一些,不仅仅是因为光源的定义会影响物体的颜色,而且如果缺乏有效光源照射的三维物体实际上投影到屏幕上以后是缺乏三维效果的,这一点很容易理解。OpenGL允许系统定义最多8个光源,每个光源有很多属性需要设置。OpenGL把光照近似的分为红、绿、蓝。共有四种光照的类

48、型,环境光,漫射光,镜面光和散射光。在实际操作中,一旦设置了环境光,那么不论这种光照是否确定位置,是否限制光源的照射方向与照射角度,那么整个空间中都将充满了这种环境光;慢射光,镜面光比较容易理解;设置散射光可以模拟发光物体,好像这个物体自身发光一样,一般可以用来实现对于灯泡之类物体的制作。下面我介绍这次毕业设计中创建光源使用的函数,以及步骤和方法:1 创建光源 glLightif(GLenum light, GLenum pname, TYPE param) 这次毕业设计中,我创建了两个光源,一个是用来模拟自然光的光源,一个是用来模拟车灯出射光的方向光源,代码如下:

49、0; glLightfv(GL_LIGHT0,GL_POSITION,Light_position);     glLightfv(GL_LIGHT1,GL_DIFFUSE,lampdiffuse);    glLightfv(GL_LIGHT1,GL_SPECULAR,lampspecular);    glLightfv(GL_LIGHT1,GL_POSITION,lampposition);    glLightf(GL_LIGHT1,GL_SPOT_CUTO

50、FF,90);    glLightfv(GL_LIGHT1,GL_SPOT_DIRECTION,lampdirection);说明,此函数的第一个参数指定了创建的是几号光源,第二个参数指定了要设置的光源属性:GL_POSITION是光源的位置,GL_DIFFUSE是光源中漫射光分量,GL_SPECULAR是光源中镜面光的分量,GL_SPOT_CUTOFF是聚光源的张角,GL_SPOT_DIRECTION是聚光源的方向。由此可见,我将GL_LIGHT1设置为一个聚光源。第三个参数是此光源属性的值。2 使能光源 glEnable(GLenum light)

51、 glEnable()函数在OpenGL中使用广泛,很多功能在使用前都必须使用此函数使能。代码如下: glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_LIGHT1);说明,第一句是用来使能光照的,后面两句分别使能不同的光源。 3 指定光照模型 glLightModelifGLenum pname, TYPE param代码如下:    glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER,local_view)

52、;glLightModelfv(GL_LIGHT_MODEL_AMBIENT, model_ambient);glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);说明,第一句定义了局部视点的位置,第二局定义了光照模型中的全局环境光的颜色,第三局打开了双面光照的功能。.4.4 其他必要的环境设置1 开启深度检测    glDepthFunc(GL_LESS);   glEnable(GL_DEPTH_TEST);   glClearDepth(1.0f);说明:深度缓存在Op

53、enGL里面比较重要,没有深度检测也将使物体失去立体感。顾名思义,深度也就是物体里视点在纵深方向的距离,所以,没有纵深感,那么物体也就只是一个平面而已了。深度检测的另一个重要作用就是进行隐藏面摘除,对于场景中的不可见物体,可以使用OpenGL中的隐藏面摘除技术对其不进行绘制,从而提高程序的绘制效率。最简单的实现方法就是使用深度缓存,程序会自动监测新像素点的深度至于像苏中存储的深度值,如果新像素点更接近于视点,则新像素点将会替代原像素点,这样就完成了隐藏面摘除。2 使能物体法向量,代码如下    glEnable(GL_AUTO_NORMAL); 

54、;  glEnable(GL_NORMALIZE);定义法向量也很重要,由于在引入光照后,物体必须正确的对光线进行反射,而计算反射的时候需要知道反射面的法向量,这两句使能了面的法向量,对于法向量的计算在后面将会作介绍。3 清空颜色缓存   glClearColor(0.0,0.0,0.0,1.0)颜色缓存是存储屏幕上的图像的内存区域,OpenGL将当前所设置的清除颜色作为状态变量,并一直进行跟踪。这样就不必在每次清除缓存后重新制定颜色值。这样,对于环境的设置也基本完成,接下去就是要进行物体的绘制了。 第五章 创建一个车灯模型在这一章中,我将会介

55、绍这次毕业设计的核心部分,如何创建一个车灯模型,首先设定物体的表面材质,然后制作抛物面,对抛物面进行切割,对切割处进行修补,加入灯泡,形成一个车灯模型。当然,在介绍这些之前,还需要知道一些其他的预备知识。5.1 预备知识1模型转换与视点转换: glTranslatefd(TYPE x, TYPE y, TYPE z )、      glRotatefd( TYPE angle, TYPE x, TYPE y, TYPE z )、     glScalefd(TYPE x, T

56、YPE y, TYPE z )这三个函数分别对模型进行平移,旋转,缩放。我们可以想象一下,视点转换和模型转换可以使用相同的函数进行,比如,在用相机拍摄物体时,我们可以保持物体的位置不动,而将相机移离物体,这就相当于视点转换;也可以保持相机的位置不动,将物体移离相机,这就相当于模型转换。模型转换和视点转换共同构成模型视景矩阵。2矩阵堆栈操作:glPushMatrix(),glPopMatrix()说明,OpenGL中有两个最基本的矩阵,模型视景矩阵和投影矩阵,它们都有相应的矩阵堆栈,这些矩阵的当前值就是在矩阵堆栈中的最顶层元素。对于转换操作,发出转换命令后生成的新的当前矩阵就存储在矩阵堆栈中,因

57、此,我们可以利用矩阵堆栈存储当前值,并在需要的时候将当前值弹出堆栈。举例来说,我们现在需要绘制一个自行车,设置自行车的两个轮子相对于坐标原点是对称的。我们可以这样绘制:将坐标系原点移动到前车轮中心,然后进行绘制,完成后,计算前车轮中心到后车轮中心的距离,并将坐标系原点移动到后车轮中心,再绘制后车轮。显然,这种绘制顺序是不科学的,如果需要绘制更多的对称物体,那么这种计算量会非常大,而且容易产生错误。这种问题,我们使用矩阵堆栈可以很容易得到解决。在绘制前车轮之前,将当前矩阵保存在矩阵堆栈中,然后将坐标原点移动到前车轮中心,绘制前车轮,绘制结束后,将保存的当前矩阵弹出矩阵堆栈,这时堆栈顶部的矩阵仍为

58、原坐标系,下一步只需相对于原坐标系移动相应距离到后车轮进行绘制,这样,所有物体的位置都是相对于原坐标系进行设置,程序计算量得到缩减,而且不容易出错误。3视景体的切割:glClipPlane()我们在前面介绍的视景体定义中,视景体是由六个切割面组成的六面体,除此之外,若要进一步限制视景体内所显示的场景中的物体,可以定义附加的切割面(最多可以定义六个),定义的函数原型为:glClipPlane(GLenum plane, const double *equation)函数说明:plane变量为GL_CLIP_PLANEi,i是05之间的整数,表示定义的是第i个切割面。equation变量定义了平面

59、方程Ax+By+Cz+D=0的系数,有数祖表示。在定义了切割平面后,必须用glEnable(GL_CLIP_PLANEi)使能切割操作。也可以用glDisable()关闭切割操作。4 显示列表使用显示列表,我们可以把需要反复绘制的OpenGL命令存储在显示列表中,然后在需要的时候调用显示列表,这样不仅可以大大提高程序的执行效率,而且还可以简化程序的设计流程。创建显示列表:glNewList(int list, GLenum mode) 、glEndList()显示列表的内容必须在这两个函数之间添加,list参数用来制定该显示列表的正整数(此显示列表的唯一标识),mode参数的可选值为

60、GL_COMPILE, GL_COMPILE_AND_EXECUTE, 如果不希望立即执行显示列表中的命令,使用GL_COMPILE常数,如果希望指定的命令立即执行一次,同时也备以后执行,则使用GL_COMPILE_AND_EXECUTE常数。执行显示列表:glCallList(int list)在执行显示列表时,显示列表中的内容是不能更改的,也就是说,如果在显示列表中有函数表达式,那么即使在显示列表之后改变了表达式的内容,显示列表中表达式的内容也不会受到影响。另外,显示列表是可以嵌套的。5 定义法向量:glNormal()此语句后面所有的点都被定义相同的法向量,此语句还可以定义面法

61、向量,后面会有实例。5.2 物体的材质属性5.3 定义计算法向量的函数计算方法比较简单,使用的是向量运算,代码如下:void CallNormal(float *p1,float *p2,float *p3,float *n)    float a3,b3;    int i;    /形成向量    for (i=0;i<3;i+)            ai=p2i-

62、p1i;        for (i=0;i<3;i+)            bi=p3i-p1i;        /计算法向量    n0=a1*b2-a2*b1;    n1=a2*b0-a0*b2;    n2=a0*b1-a1*b0;    f

63、loat temp1=n0*n0;    float temp2=n1*n1;    float temp3=n2*n2;    float length=sqrt(temp1+temp2+temp3);    if(length!=0)            for (i=0;i<3;i+)      

64、0;             ni=ni/length;            说明:传入参数为三点坐标值,*n为储存法向量的数组指针。此函数计算的方法比较简单,使用的是向量差积的方法。下面,我们就可以开始绘制车灯模型了。5.4 绘制车灯模型的关键技术对于绘制一个二次曲面有很多方法,比如在OpenGL中有直接的二次曲面方法,贝塞尔求值器和NURBS曲面求值器,它们的好处在于可以用较少的点

65、来控制一个曲面的形式,而且储存量也大为减少。这两种方法更适合来创建不规则物体,也就是没有精确的数学描述的曲面。毕业设计中的车灯实际上是一个规则物体,对它进行分解的话,是一个经过切割的旋转抛物面(圆形旋转),两个平面抛物面,一个圆形的底面。所以,我们可以通过直接计算的方法,用一些小的三角形进行拟合。为什么采用三角形而不是四边形、五边形呢,理由很简单,三角形的三个顶点可以确定在一个平面内,OpenGL只能处理这种多边形。在整个过程中,遇到了以下几个问题:1如何选择三角形?想要把整个旋转抛物面绘制出来,必须用n个小三角形进行贴图,三角形如何选择呢?下面我给出我的方法:对于X-Y平面上的任意一点P(x

66、,y),我们可以通过向四个方向扩展形成如右图的图形(两个对角三角形),可以想象一下,扩展出来的四个点P1(x,y+step),P2(x+step,y),P3(x-step,y),P4(x,y-step)再通过上面的方法继续扩展,就可以用这些不重复三角形占据整个X-Y。在此基础上,计算出每个相应X-Y平面上的点对应在旋转抛物面Z轴上的坐标值,就可以形成旋转抛物面上的不重复的,全部的三角形。这样,就可以完成旋转抛物面的形成,如下图:        图5. 4. 22如何对抛物面进行切割?由于形成抛物面的是一些小三角形,

67、所以边缘会有一定程度的锯齿状,当然,提高分辨率(减小step的值)可以将锯齿现象减少到最小,但是无法从根本上解决问题。下面给出我的方法:其实,我们可以用简单的视景体切割面来进行切割操作,问题在于,定义切割面以后,如果旋转这个物体的时候,切割面不会移动,所以切割的位置会改变,这里我使用了glPushMatrix()与glPopMatrix()对整个切割过程进行“隔离”,这样,切割面相当于附着在物体上面,随物体一起移动和转动,这样就可以变相的对物体进行切割。进行四次切割(两个圆形底面,两个抛物形侧面)后的效果如下图:     图5. 4. 33如何

68、填补圆底以及两个抛物面?切割以后必须对需要填补的地方进行填补,如小的圆底和两个抛物形侧面都需要用相应形状的平面补好。这里的难度一个在与计算这些面的数学形式以及位置,一个在于如何绘制这些面。计算方面只需要对整个抛物面的形成以及切割过程比较了解,经过反复核对,就可以计算出添加面的精确数学描述。而对于绘制这些面则需要使用一些方法。形成圆面时我使用的是旋转的方法,简单的说是将一个长矩形(长等于圆形的半径)进行旋转360度,重复绘制这个矩形就可以形成一个圆面。形成平面抛物面时我使用的是梯形填补的方法,简单的说就是根据抛物面的参数,不停的形成梯形,将这些梯形按照纵向排列,就可以形成一个抛物面,具体代码见附

69、录。填补后的效果如下图      图5. 4. 44双面光照的问题。其实,生成的所有的平面,都只能定义一个法向量,也就是说,另外一面对于光照的反射是没有法向量可以参照进行计算的。我的解决方法是对每一个平面都进行两次绘制,换句话讲,每一个看上去的“平面”都是有两个间隔很小的平面组合而成的,而这两个平面的法向量是相反的,所以可以形成双面光照反射的效果,如图:     图5. 4. 5然后在车灯外壳里面加入车灯,至此,绘制车灯模型的工作基本完成。最后,在车灯四周添加墙壁实现对车灯光照的效果,绘制程序到此为止。下面,我们需要为模型实现人机交互。 第六章 人机交互的实现实际上在这次的毕业设计中,实现人机交互主要是通过编写c+程序来实

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论