OpenGL基础技术讲座--体系结构
OpenGL是一套图形标准,它严格按照计算机图形学原理设计而成,符合光学和视觉原理,非常适合可视化仿真系统。
首先,在OpenGL中允许视景对象用图形方式表达,如由物体表面顶点坐标集合构成的几何模型,这类图形数据含有丰富的几何信息,得到的仿真图像能充分表达出其形体特征;而且在OpenGL中有针对三维坐标表示的顶点的几何变换,通过该变换可使顶点在三维空间内进行平移和旋转,对于由顶点的集合表达的物体则可以实现其在空间的各种运动。
其次,OpenGL通过光照处理能表达出物体的三维特性,其光照模型是整体光照模型,它把顶点到光源的距离、顶点到光源的方向向量以及顶点到视点的方向向量等参数代入该模型,计算顶点颜色。因此,可视化仿真图像的颜色体现着物体与视点以及光源之间的空间位置关系,具有很强的三维效果。
另外,为弥补图形方法难于生成复杂自然背景的不足,OpenGL提供了对图像数据的使用方法,即直接对图像数据读、写和拷贝,或者把图像数据定义为纹理与图形方法结合在一起生成视景图像以增强效果。为增强计算机系统三维图形的运算能力,有关厂家已研制出了专门对OpenGL进行加速的三维图形加速卡,其效果可与图形工作站相媲美。
一个完整的窗口系统的OpenGL图形处理系统的结构为:最底层为图形硬件,第二层为操作系统,第三层为窗口系统,第四层为OpenGL,第五层为应用软件。见图1.1所示。
图1.1 OpenGL图形处理系统的层次结构
OpenGL是网络透明的,在客户机/服务器体系结构中,允许本地或远程调用OpenGL。所以在网络系统中,OpenGL在X窗口、Windows或其它窗口系统下都可以以一个独立的图形窗口出现。
由于OpenGL是一个与平台无关的三维图形接口,操作系统必须提供像素格式管理和渲染环境管理。下面以Windows NT操作系统为例具体介绍OpenGL运行的体系结构。
OpenGL在Windows NT上的实现是基于Client/Server模式的,应用程序发出OpenGL命令,由动态链接库OpenGL32.DLL接收和打包后,发送到服务器端的WINSRV.DLL,然后由它通过DDI层发往视频显示驱动程序。如果系统安装了硬件加速器,则由硬件相关的DDI来处理。
OpenGL/NT的体系结构图如图1.2所示。
从程序员的角度看,在编写基于Windows的OpenGL应用程序之前必须清除两个障碍,一个是OpenGL本身是一个复杂的系统,这可以通过简化的OpenGL辅助库函数来学习和掌握;另一个是必须清楚地了解和掌握Windows与OpenGL的接口。
图1.2 OpenGL/NT体系结构
1.3.2 渲染上下文(RC)
OpenGL的绘图方式与Windows的一般的绘图方式是不同的,其区别主要表现在以下三个方面:
(1) Windows采用的是GDI绘图;
(2) OpenGL采用的是渲染上下文RC(Render Context,又称渲染描述表)绘图;
(3) OpenGL使用的是特殊的像素格式。
在Windows中使用GDI绘图时必须指定在哪个设备上下文(Device Context,又称设备描述表)中绘制,同样地,在使用OpenGL函数时也必须指定一个所谓的渲染上下文。正如设备上下文DC要存储GDI的绘制环境信息如笔、刷和字体等,渲染上下文RC也必须存储OpenGL所需的渲染信息如像素格式等。
渲染上下文主要由以下六个wgl函数来管理,下面分别对其进行介绍。
l HGLRC wglCreateContext(HDC hdc)
该函数用来创建一个OpenGL可用的渲染上下文RC。Hdc必须是一个合法的支持至少16色的屏幕设备描述表DC或内存设备描述表的句柄。该函数在调用之前,设备描述表必须设置好适当的像素格式。成功创建渲染上下文之后,hdc可以被释放或删除。函数返回NULL值表示失败,否则返回值为渲染上下文的句柄。
2 BOOL wglDeleteContext(HGLRC hglrc)
该函数删除一个RC。一般应用程序在删除RC之前,应使它成为非现行RC。不过,删除一个现行RC也是可以的。此时,OpenGL系统冲掉等待的绘图命令并使之成为非现行RC,然后删除之。注意在试图删除一个属于别的线程的RC时会导致失败。
3 HGLRC wglGetCurrentContext(void)
该函数返回线程的现行RC,如果线程无现行RC则返回NULL。
4 HDC wglGetCurrentDC(void)
该函数返回与线程现行RC关联的DC,如果线程无现行RC则返回NULL。
5 BOOL wglMakeCurrent(HDC hdc, HGLRC hglrc)
该函数把hdc和hglrc关联起来,并使hglrc成为调用线程的现行RC。如果传给hglrc的值为NULL,则函数解除关联,并置线程的现行RC为非现行RC,此时忽略hdc参数。
传给该函数的hdc可以不是调用wglCreateContext时使用的值,但是,它们所关联的设备必须相同并且拥有相同的像素格式。注意,如果hglrc是另一个线程的现行RC,则调用失败。
6 BOOL wglUseFontBitmaps(HDC hdc, DWORD dwFirst, DWORD dwCount, DWORD dwBase)
该函数使用hdc的当前字体,创建一系列指定范围字符的显示表。可以利用这些显示表在OpenGL窗口画GDI文本。如果OpenGL窗口是双缓冲的,那么这是往后缓冲区中画GDI文本的唯一途径。
一般地,在使用单个RC的应用程序中,在相应WM_CREATE消息时创建RC,当WM_CLOSE或WM_DESTROY到来时再删除它。在使用OpenGL命令往窗口中绘图之前,必须先建立一个RC,并使之成为现行RC。OpenGL命令无需提供RC,它将自动使用现行RC。若无现行RC,OpenGL将简单地忽略所有的绘图命令。
一个RC是指现行RC,这是针对调用线程而言的。一个线程在拥有现行RC进行绘图时,别的线程将无法同时绘图。一个线程一次只能拥有一个现行RC,但是可以拥有多个RC;一个RC也可以由多个线程共享,但是它每次只能在一个线程中是现行RC。
在使用现行RC时,不应该释放或者删除与之关联的DC。如果应用程序在整个生命期内保持一个现行RC,则应用程序也一直占有一个DC资源。注意,Windows系统只有有限的DC资源。
下面介绍两种管理RC与DC的方法。
方法一:RC由WM_CREATE消息响应时创建,创建后立即释放DC;当WM_PAINT消息到来时,程序再获取DC句柄,并与RC关联起来,绘图完成后,立即解除RC与DC的关联,并释放DC;当WM_DESTROY消息到来时,程序只需简单地删除RC即可。如图1.3所示。
图1.3 RC与DC的管理方法一
方法二:RC在程序开始时创建并使之成为现行RC。它将保持为现行RC直至程序结束。相应地,GetDC在程序开始时调用,ReleaseDC在程序结束时才调用。此种方法的好处是在响应WM_PAINT消息时,无需调用十分耗时的wglMakeCurrent函数,一般它要消耗几千个时钟周期。如图1.4所示。
如果应用程序需要使用动画或实时图形,建立采用第二种方法。