基于opengl的屏幕对象拾取_本科毕业论文(编辑修改稿)内容摘要:

MFC 的基础上进一步实现了皮肤、渐变风格、多顶层窗口程序、属性列表等较受欢迎的功能;同时,在 C++在线社区中,很大一部分开放的源代码也是基于 MFC 的。 VC++产品 版本 与 MFC 版本 如下: 表格 1 VC++产品版本与 MFC 版本 MFC 特点 封装 构成 MFC 框架的是 MFC 类库。 MFC 类库是 C++类库。 这些类或者封装了 Win32 应用程序编程接口,或者封装了应用程序的概念,或者封装了 OLE 特 性,或者封装了 ODBC和 DAO 数据访问的功能,等等, 8 分述如下 : ( 1)对 Win32 应用程序编程接口的封装 ; ( 2)对应用程序概念的封装 ; ( 3)对 COM/OLE 特性的封装 ; ( 4)对 ODBC 功能的封装。 继承 首先, MFC 抽象出 众多类的共同特性,设计出一些基类作为实现其他类的基础。 这些类中 , 最重要的类是 CObject 和 CCmdTarget。 CObject 是 MFC 的根类,绝大多数 MFC 类是其派生的,包括CCmdTarget。 CObject 实现了一些重要的特性,包括动态类信息、动态创建、对象序列化、 对程序调试的支持,等等。 所有从 CObject 派生的类都将具备或者可以具备 CObject 所拥有的特性。 CCmdTarget 通过封装一些属性和方法,提供了消息处理的架构。 MFC 中,任何可以处理消息的类都从 CCmdTarget 派生。 针对每种不同的对象, MFC 都设计了一组类对这些对象进行封装,每一组类都有一个基类,从基类派生出众多更具体的类。 这些对象包括以下种类:窗口对象,基类是 CWnd;应用程序对象,基类是 CwinThread;文档对象,基类是 Cdocument,等等。 虚拟函数和动态约束 MFC 以“ C++”为基础,自然支持虚拟函数和动态约束。 但是作为一个编程框架,有一个问题必须解决:如果仅仅通过虚拟函数来支持动态约束,必然导致虚拟函数表过于臃肿,消耗内存,效率低下。 例如, CWnd 封装 Windows 窗口对象时,每一条 Windows 消息对应一个成员函数,这些成员函数为派生类所继承。 如果这些函数都设计成虚拟函数,由于数量太多,实现起来不现实。 于是, MFC建立了消息映射机制,以一种富有效率、便于使用的手段解决消息处理函数的动态约束问题。 应用程序的构成 图 2 解释了该应用程序的结构 与对象 ,箭头 表示信息流向。 图 2 应用程序的结构 从 CWinApp、 CDocument、 CView、 CMDIFrameWnd、 CMDIChildWnd 类对应地派生出 CTApp、 CTDoc、CTView、 CMainFrame、 CChildFrame 五个类,这五个类的实例分别是应用程序对象、文档对象、视对象、主框架窗口对象和文档边框窗口对象。 主框架窗口包含了视窗口、工具条和状态栏。 对这些 9 类或者对象解释如下 : ( 1)应用程序 ( CWinApp) 应用程序类派生于 CWinApp。 基于框架的应用程序必须有且只有一个应 用程序对象,它负责应用程序的初始化、运行和结束。 ( 2)边框窗口 (CMDIFrameWnd) 如果是 SDI 应用程序,从 CFrameWnd 类派生边框窗口类,边框窗口的客户子窗口 (MDIClient)直接包含视窗口;如果是 MDI 应用程序,从 CMDIFrameWnd 类派生边框窗口类,边框窗口的客户子窗口 (MDIClient)直接包含文档边框窗口。 如果要支持工具条、状态栏,则派生的边框窗口类还要添加CToolBar 和 CStatusBar 类型的成员变量,以及在一个 OnCreate 消息处理函数中初始化这两个控制窗口。 边框窗口用来管理文档边框窗口、视窗口、工具条、菜单、加速键等,协调半模式状态(如上下文的帮助 (SHIFT+F1 模式 )和打印预览)。 ( 3)文档边框窗口 (CMDIChildWnd) 文档边框窗口类从 CMDIChildWnd 类派生, MDI 应用程序使用文档边框窗口来包含视窗口。 ( 4)文档 (CDocument) 文档类从 CDocument 类派生,用来管理数据,数据的变化、存取都是通过文档实现的。 视窗口通过文档对象来访问和更新数据。 ( 5)视 (CView) 视类从 CView 或 它的派生类派生。 视和文档联系在一起,在文档和用户之间起中介作 用,即视在屏幕上显示文档的内容,并把用户输入转换成对文档的操作。 用图的形式可直观地表示所涉及的 MFC 类的继承或者派生关系 ,如下: 图 3 MFC 的层次 基于 OpenGL+MFC 的三维模拟的编程环境配置 用 MFC 调用 OpenGL 函数来进行三维模拟的编程环境配置: (一)创建 MFC 项目 (1) 创建项目文件:选择 File/New 菜单选项,建立一个名为 MyTest 的单文档 (SDI) 应用程序; (二)配置 OpenGL 开发环境 ( 1) 将 91. h, glu. h, glaux. h 和 glut. h 拷贝到 ⋯⋯⋯(即盘符 +路径 )\ Microsoft Visual Studio\ VC98\ Include\ GL 目录中; ( 2) 将 opengl32. 1ib, glu32. 1ib, glaux. 1ib 和 glut32. 1ib 拷贝到 ⋯⋯⋯(即盘符 +路径 )\ Microsoft 10 Visual Studio\ Vc98\ Lib 目录中; ( 3) 将 , , , , 文件拷贝到操作系统安装目录 C:\ WINDOWS\ system32 目录下 ; ( 4)选择 Project/Setting 菜单选项。 在 Link 栏的 Lib 输入域中添加 、 ,若需使用 OpenGL 的辅助库函数,则还需添加。 到此,基于 OpenGL+ MFC 的开发环境就建立好。 (三)初始化 OpenGL 具体编程步骤 : 第一步:修改窗口风格设置。 需要在函数 CGLView:: PreCreateWindow 中增加对 Windows 窗口风格的设置,以防止在窗口重叠时把图形绘制到子窗口和兄弟窗口。 实现代码如下 : cs. style l=WS_CLIPCHILDREN WS CLIPSIBLINGS; 第二步:设置像素格式。 首先需要在视图类 CGLView 中添加一个成员函数,函数原型如下: BOOL CGLView::SetupPixelFormat(CDC*pDC);设置像素格式并向视图类 CGLView 中添加两个成员变量: CDC* m_pDC; OpenGL 设备场境 HGLRC m_hRC; OpenGL 渲染场境在 Windows 中,使用结构 PIXELFO 腿ATDEscRIPTOR 来设置像素格式,并提供 ChoosePixelFormat()函数来选择最为匹配的像素格式以及SetPixelFormat()函数来为设备场境设置像素格式。 设置像素格式的步骤如下: PIXELFORMATDESCRIPTOR,像素格式是用这个结构来描述的。 b. 调用函数 ChoosePixelFormat() ,将 填 写 好的 像 素 格 式结 构 传 递给 该 函 数 ,函 数ChoosePixelFormat()返回一个整型的序号。 这个序号标识一个当前设备场境中所能提供的,且与所要求的像素格式最为匹配的像素格式。 c.将这个返回的像素格式序号传递给函数 SetPixelFormat(),使之设置成为当前设备场境的像素格式。 第三步:创建渲染场境。 用 wglreatecontext()函数来创建 OpenGL 的一个渲染场境;用 wglMakeCurrent()函数使给定的渲染场境设置成为当前调用线程的渲染场境,而线程中随后调用的 OpenGL 命令都将通过与该渲染场境相关联的设备场境来实现窗口的场景绘制。 具体实现代码如下: HDC m hDC; HGLRC nl hglRC: m_hglRC=wglCreateContext(m_hDC);创建一个渲染场境 wglMakecurrent(m_hDC, m hglRC);使成为当前调用线程的渲染场境 第四步:添加消息处理函数。 利用 MFC ClassWizard 为 CGLView 类添加消息:帐 _CREATE、 wM_DESTROY、 m,t_SIZE 和删 _TIMER 的响应函数,消息处理函数名依次为: OnCreate()、 onDestroy()、 OnSize()和 OnTimer()。 A. CGLView:: OnCreate()函数 该函数完成的功能是:在视图窗口创建完成后,进行 OpenGLWindows 的初始化工作。 (四 )清理工作 CGLView:: OnDestroy()函数 该函数完成的功能是:在视图窗口被释放时,用于清除当前的渲染场境,并释放设备场境。 具体实现代码如下: wglMakeCurrent(NULL, NULL); wglDeleteContext(m__hglRC);清除渲染场境: ReleaseDC(m_hWnd, m_hDC);释放设备场境 11 C. CGLView:: OnSize()函数 D. CGLView:: OnTimer()函数 该函数完成的功能是:通过定时器每隔一定时间的消息驱动,来实现动态的场景更新。 12 3 拾取技术 拾取算法的研究可大致分为两类 :一类是基于三维空间的射线拾取算法 ,代表算法就是基于 CPU的射线求交拾取技术; 另一类是基于图像空间的拾取算法 ,例如 :基于 GPU 的重绘式拾取技术。 基于射线求交拾取技术 其基本原理是 : 获取屏幕坐标并转换成图形系统的视口坐标,根据不同图形 API(应用程序编程接口 )的实现给该点加上适当的深度值 (如 OpenGL 标准的深度值介于 01),反算出该拾取点的世界空间坐标, 将相机焦点坐标转换为屏幕坐标,过相机位置点向鼠标选中点作一条射线 在三维空间中对射线和物体进行求交 , 离相机位置点最近的 实体就是被选中的实体。 算法的具体实现步骤如下 : (1)初始化 , 获取鼠标点的屏幕坐标 ( x, y), 并将相机焦点坐标转换为屏幕坐标 ; (2)相机焦点深度值 (z 值 )作为鼠标点的深度值 (z 值 ),并 将鼠标点坐标转化为世界坐标 ( XW , YW ,ZW); (3)过相机位置点向点 ( XW, YW, ZW) 作一射线 m, 并分别求射线 m 和投影空间近截面和远截面的交点 A, B, 得到线段 AB。 如果射线 m 垂直于视线 , 则射线 m 和投影空间不相交; (4)依次取出场景实体列表中的每个实体 ,获取该实体的转换矩阵 , 并利用该矩阵将 A、 B 点 的坐标转换为局 部坐标; (5)计算实体的包围盒 , 并判断 AB 和包围盒的相对位置。 如果 AB 和包围盒相交 , 则求该实体和 AB交点的参数值 , 并将该实体标记为选中对象。 如不相交 , 则进行下一个实体的处理。 在上述算法中,判断线段 AB 和实体包围盒的相对位置及线段 AB 和实体的求交是算法实现的关键。 为了提高拾取速度,对传统的包围盒算法和求交算法进行了改进。 判断线段和包围盒的相对位置 实体的最大包围盒是一个各表面平行于坐标平面的六面体,其左下顶点的 x、 y、 z 坐标值为实体所有顶点相应坐标值的最小值,其右上顶点的坐标值为实体所有顶点相 应坐标值的最大值。 采用向量法来判断线段和包围盒相对位置,该算法的基本思想为:将线段看成一个由起点指向终点的向量,求向量各分量和离起点最近的三个包围盒侧面所在平面的交点 ,如果各分量与平面的交点都在包围盒侧面上或者在包围盒内且在各分量上,则该线段和包围盒相交 (本文中我们把线段与包围盒相交和线段在包围盒内这两种情况统称为相交 )。 判断线段与包围盒相对位置的具体步骤如下: (1)判断起点 A 是否在包围盒内,如果在盒内,则线段 AB 和包围盒相交,判断结束;如果不在盒内,则记下该包围盒离起点 A 最近的三个侧面,这三个侧面必 定共点且相互垂直; (2)过起点 A 向各侧面做垂线,得到三个垂足,连接点 A 和各垂足,得到三个向量 a a a3(如 图4 所示 ),并求 a a a3 和向量 AB 各相应分量的比值 ti=ai/AB(i=1, 2, 3)。 如果 AB 的某个分量为零,则比值取 1。 取 t t t3 中的最大值为 tmax,如果 tmax0 或 tmax1,则 AB 和包围盒不相交 ,判断结束; (3)根据 tmax 值求出交点坐标,判断交点是否在包围盒上。 (此处最好给出交点坐标的计算式 ,以及交点是否在包围盒上的判断算式。 )如果交点在盒上,则 AB 和包围盒相交 ,否则, AB 和包围盒相离。 图 4 线段 AB 和包围盒的位置关系图 13 判断线段与包围盒相对位置的算法流程如图 5 所示。 图 5 向量法判断线段和包围盒相对位置算法流程图 根据拾取对象的不同 , 线段和相关对象求交的方法和过程存在差别。 设要和拾取对象求交的线段为 AB, 针。
阅读剩余 0%
本站所有文章资讯、展示的图片素材等内容均为注册用户上传(部分报媒/平媒内容转载自网络合作媒体),仅供学习参考。 用户通过本站上传、发布的任何内容的知识产权归属用户或原始著作权人所有。如有侵犯您的版权,请联系我们反馈本站将在三个工作日内改正。