会员: 密码:  免费注册 | 忘记密码 | 会员登录 网页功能: 加入收藏 设为首页 网站搜索  
游戏开发 > 程序设计 > 3D图形
GLSL教程:第一章 OpenGL回顾(第一至六节)
发表日期:2007-03-22 14:54:49作者: 出处:  

第一章 回顾OpenGL

译者:樱

  本章是对OPENGL应用程序编程接口(API)的一个简短的回顾,作为以后各章节的基础。本章并不打算做一个详细的描述,如果你已经对OPENGL的时候相当熟悉的话,你可以放心地跳过本章去看后面的各章节了。如果你熟悉其他的3D API的话,本章可以帮助你熟悉OPENGL,并且作为扩展你可以使用OPENGL 着色器语言来编写一些着色器。对OPENGL固定功能的描述是基于OPENGL 1.5说明书的。


1.1 OPENGL的历史

  OPENGL是一个跨平台的工业标准API。这套API的说明书于1992年出台,而第一个实现是在1993年出现的。它由一种叫做Iris GL (Graphics Library)的API广泛兼容,这套API是由Silicon Graphics .Inc设计并支持的。为了建立一个工业标准,Silicon Graphics与其他很多硬件公司合作,创立了一个开放的标准。这就是OpenGL。

  OpenGL在设计上继承了Iris GL的特性,其目的是在尽可能在低的底层提供对图形硬件的访问能力,同时还做到与硬件无关。OpenGL在不同的系统下有不同的实现,比如Macs, PCs, 以及 UNIX系统。它被许多硬件结构支持,除了它自己的帧缓存外,图形硬件还可以提供许多其他的支持, OpenGL在这些硬件上被加速。

  自从1992年6月OpenGL说明手册的第一个版本(v1.0)发行以来,已经被修订了5次,每次都会添加一些新的功能。当前的OpenGL说明手册是1.5版。OpenGL 1.0的第一个统一的版本出现于1993年,1.1版于1997年完成,并且添加了两个重要的功能:顶点数组和纹理对象。OpenGL 1.2的说明手册在1998年发行,同时添加了对3D纹理的支持以及可选的图像功能的子集。2001年完成的OpenGL1.3中,又加入了立方纹理贴图,压缩纹理以及多重纹理等其他功能。OpenGL1.4在2002年也已经完成,加入了自动多纹理(mipmap)生成,附加的混合函数,用于阴影计算的短深度值的内部纹理格式,除此以外,还可以用一个命令来绘制多个顶点,对点的光栅化有更加的控制能力,对模板绕转行为的控制以及纹理渲染能力也获得了提高。OpenGL1.5说明书在2003年7月通过审查并且在同年10月获得出版。它添加了对顶点缓冲对象(VBO)、阴影比较函数,遮挡查询以及哑纹理 (nonpower of texture)的支持。OpenGL 2.0预计能在2004年初完成,它将会把顶点处理器和片原处理器的可编程性的作为核心加入到OpenGL说明手册里。基于该版本的OpenGL,应用程序将会得到使用高级着色器语言去实现他们自己的渲染算法的能力。从OpenGL1.5到2.0的变化可以看出,OpenGL可编程性的引入,揭示了其设计上的根本性的转变。然而,主版本号的改变并不意味着先前版本的OpenGL的能力的削弱。OpenGL2.0将会完全地向下兼容OpenGL1.5——可以在OpenGL1.5下正确运行地应用程序将可以在OpenGL2.0中不加修改就能正确运行。

  Silicon Graphics成立了一个机构来监督OpenGL的发展,这个机构就是OpenGL ARB(OpenGL Architecture Review Board)。这个组织支配着一组细则,该组织的主要任务是引导OpenGL,控制其说明书以及进行一致性测试。最初的时候ARB包括以下成员:SGI, Intel, Microsoft, Compaq, Digital Equipment Corporation, Evans & Sutherland, 以及 IBM。当前ARB则包括以下成员:3Dlabs, Apple, ATI, Dell, Evans & Sutherland, Hewlett-Packard, IBM, Intel, Matrox, NVIDIA, SGI, 以及 Sun Microsystems。


1.2 OpenGL的发展

  在各种各样的OpenGL实现当中,有非常多的功能是以扩展的形式提供的。OpenGL有明确定义的扩展机制,并且硬件销售商可以自由的定义和实现一些特性以展示其硬件的性能。因为OpenGL的根本设计是固定功能的状态机,因此修改OpenGL的唯一方法就是为他提供扩展——当然,也只有 OpenGL实现者可以实现扩展。应用程序没有任何方法去超越OpenGL的提供者提供的功能去扩展新的功能。到今天为止,已经有了近300个扩展。扩展由它们的前缀来识别其提供者(比如说:前缀为SGI的扩展就是由Silicon Graphics, Inc.所提供)。如果一个扩展由多家销售商所支持的话,它就以EXT做为前缀。由ARB彻底评估过的扩展会以ARB作为前缀,该前缀指出这些扩展是被推荐的实现某个功能的扩展。下一步ARB就会把这些扩展加入到OpenGL说明手册当中。也就是说,他们成为核心的一部分了。已经发行的OpenGL的扩展的说明手册可以在其注册站点http://oss.sgi.com/projects/ogl-sample/registry 上找到。

  由某个特别的OpenGL实现所支持的扩展可以通过传递符号常量GL_EXTENSIONS给glGetString而决定。返回字符串包含了所有的该实现所支持的扩展,有些硬件销售商甚至支持了多达100个的扩展。决定所需要的扩展是否存在以及当他们不存在时应该如何处理看起来是有点让人恐惧。 OpenGL扩展的增长速度主要随OpenGL的发展而增加,但是从某种意义上来说,他们自己既是成功者又是受害者。扩展允许硬件销售商非常容易地展示自己硬件的特性,但是呈现在应用程序开发者面前的确实一大堆的非标准的选项!而同时ARB对使扩展成为标准也抱着小心谨慎的态度。

  自OpenGL1.0以来,底层硬件的可编程性一直没有暴露出来。OpenGL最初的设计者,Mark Segal以及Kurt Akeley是这样说的:“这么决定的一个原因是,出于性能的考虑。图形硬件常常被设计成用来执行某一个特定顺序的操作的,以任意的算法改变这些操作是不可行的。”。这个解释在它被写下的1994年或许还是正确的(即使那样那时候仍然有可编程的图形硬件)。但是现在,所有的图形硬件都具有可编程性了。由于 OpenGL扩展数量的增长以及要支持微软 DirectX API的需求,硬件销售商没有任何选择,除了设计可编程图形硬件结构。(正如以后本书章节所要讨论的,提供给应用程序员可编程性是OpenGL高级着色器语言的目的。)


1.3 运行模型

  OpenGL API的主要目标是把图形绘制到帧缓存里,稍微做一点扩展,还要从帧缓存里读取数据。因此它在设计上多少会有点独特,包括支持绘制图像和位图以及绘制三维几何(如点,线,多边形等,他们统称为:图元)。

  OpenGL的运行模型可以以客户-服务器的形式的来描述。一个应用程序(客户端)发出OpenGL命令流,然后由OpenGL实现(服务器端)来解释和执行。应用程序和OpenGL实现可以运行在同一台机器上,也可以运行在两台不同的机器上。一些OpenGL状态被存储在应用程序(客户机)的地址空间上,但是大部分还是存储在OpenGL实现(服务器)的地址空间上。

  虽然所有的指令都被延迟,以进行一个指令缓冲的中间操作过程,但是OpenGL命令处理的形式永远是按照接受他们的先后顺序来执行的。乱序执行 OpenGL命令序列是不被允许的。也就是说,一个图元在它之前的图元没有绘制完成之前是不会被绘制的。这种有序的执行模式也被应用于查询状态或者是读取帧缓存数据。这些命令返回一个值,这些值包含之前所有的命令完全执行的结果。

  当命令发出的时候,对OpenGL的数据绑定就会发生,而不是在运行的时候。当命令发出的时候,传递给命令的数据就会被解释,并且如果需要的话,就会把数据拷贝到OpenGL内存当中。在命令子序列里,应用程序改变了这些数据也不会对那些存储在OpenGL内存中的数据造成影响。


1.4 帧缓存结构

  OpenGL是用来绘图的API,因此OpenGL的最基本的目标是把应用程序所提供的数据变换为能在屏幕上显示的数据。这个过程一般被称作“渲染”。典型地,这个过程会被特殊设计地硬件所加速,但是一些甚至是全部地OpenGL管道都可以通过一个由CPU模拟地软件实现来执行。对于OpenGL 的用户来说,底层如何管理硬件与软件实现是透明的。但不管怎么说,渲染结果总是遵守OpenGL说明书的定义的。用来绘图并且维护显示列表内容的硬件被称作图形加速器。图形加速器一般都有一个预先分配好的内存区域来维护显示列表内容(译者:注意,不一定是主内存)。图像中每个可见的象素都由图形加速器中的一个或更多的字节内存来描述。一个灰度图可能只有一个字节来表述象素的灰度。而一个彩色图像则可能给红,绿,蓝每个分量各分配一个字节。T为了保持在屏幕上不闪烁地显示,这个所谓的显示内存每秒钟要被扫描相当多次。图形加速器通常还有一个叫做“脱屏缓存”(OFFSCREEN MEMORY)的内存区域,这个缓存是无法被显示的,它被用来存储那些不可见的信息。

  OpenGL假设显示内存和脱屏缓存由窗口系统来管理。窗口系统决定内存的哪一部分可以被OpenGL访问以及这些部分是如何组织的。在每个 OpenGL的环境里,都有一个函数集(其包含函数通常不多)来把OpenGL绑定到某个特别的环境上。在MS的Windows环境中,这组函数叫做 WGL。在X窗口系统环境下,这组函数叫做GLX。在苹果机上,这组函数又叫做AGL。在每个环境里,这些函数都提供对图形内存区域的分配/释放操作的支持。分配/释放的数据结构叫做图形上下文(CONTEXT),图形上下文里存储了OpenGL状态,当前图形上下文的选择情况,哪快图形内存是用来绘制的,以及OpenGL与窗口系统之间的指令同步。

  随着OpenGL的渲染而改变内容的那一部分图形内存区域叫做帧缓存(frame buffer)。在窗口系统里,OpenGL通过帧缓存来与窗口通信。窗口系统为OpenGL提供了一组工具来为窗口选择帧缓存特性,而这组工具,通常是系统相关的。典型地,窗口系统也会制定当窗口重叠时OpenGL帧缓存的行为。在没有窗口的系统中,OpenGL帧缓存与整个显示设备通信。


一个支持OpenGL渲染的窗口 (即帧缓存) 可能包含以下的组合:

· 至多4个颜色缓存

· 一个深度缓存

· 一个模板缓存

· 一个积累缓存

· 一个多重采样缓存


  为了能够执行双缓存构架,大多数图形硬件同时支持前后缓存。这将允许应用程序在显示前缓存(可见的)的时候渲染到后缓存(离屏缓存)。当渲染结束的时候,这两个缓存进行交换,以便已经完成渲染的缓存像前缓存一样进行显示,这样渲染就能在后缓存重新开始了。一旦使用双缓存,在绘制过程当中用户将不能看到图像。这种技术通常被用来实现实时交互的平滑动画。

  如果为左眼和右眼各实现一个颜色缓存的话,那么就可以支持立体视觉效果了。双缓存技术由前后缓存来支持。因此一个双缓存的立体视觉将会有4各颜色缓存:前左,前右,后左,后右。一个普通的(非立体的)双缓存窗口将会仅仅有前后两个缓存。一个单缓存的窗口将会只有一个缓存。

  如果绘制3D对象时需要剔除隐藏表面的话,深度缓存是必要的。这个缓存在每个象素上存储了显示对象的深度值。当绘制附加对象的时候,会在每个象素上进行深度比较,这样就能决定新的对象是否可见。

  模板缓存用来进行复杂的掩模(masking)操作。一个复杂的形状可以存储在模板缓存里,然后绘制子序列操作可以使用模板缓存里的内容来决定是否更新象素。

  积累缓存是一个颜色缓存,不过典型地它有比颜色缓存更高的精度。这就允许一些图像通过积累产生一些合成的图像。比如说一个作用就是可以在积累缓存里对一个对象随着他的运动绘制一些帧数。在积累缓存中的象素除以帧数以后,结果图像就展现出了运动模糊效果。相似的技巧也可以用来模拟景深效果以及高质量的全屏抗锯齿。

  而通常的,当一个对象被绘制的时候,对于某个图元是否影像屏幕上的象素,会做一个单独的决议。多重采样缓存正是这样一个缓存,它允许每个渲染的对象在象素内被采样多次,以进行高质量的全屏抗锯齿,而不必对这个对象渲染多次。每个象素内的采样包括:颜色,深度,模板信息。每个象素采样的次数当然是必须的。当窗口包含多重采样缓存的时候,它将不回包括单独的深度或者是模板缓存。随着对象的渲染,颜色样本会被组合生成一个单一的颜色值,然后这个颜色值被传递,并写入到颜色缓存里。因为他们包括窗口中每个象素的多个颜色、深度以及模板样本(通常是4,8或者是16),因此多重采样缓存会消耗相当数量的离屏缓存。


1.5 状态

  OpenGL被设计成用来更新帧缓存内容的状态机。将几何图元、图像、位图对应到象素的处理过程全部由很大数量的状态设定来控制。这些状态是两两正交的——也就是说,设置其中的一个状态并不会影响其他。而这些设置的累加,定义了OpenGL渲染管道的行为以及图元如何在像是设备上变换到象素。

  OpenGL状态被集合在一个叫做OpenGL上下文的数据结构当中。窗口系统特定的API提供了创建和删除OpenGL上下文的方法。另外一些函数可以用来设置OpenGL上下文以及作为指令流渲染目标的OpenGL帧缓存。

  在OpenGL中有大量的服务器端状态仅仅有开和关两个属性。为了打开某个选项,你必须用命令:glEnable传递适当的符号常量给OpenGL。而为了关闭某个选项,你必须用glDisable传递符号常量给OpenGL。客户端状态(比如用于定义顶点数组的指针)可以由命令 glEnableClientState来激活,也可以由glDisableClientState来关闭。

  OpenGL维护一个服务器端的堆栈,用来推入或者弹出任何或者是全部的经过定义的状态值。这个堆栈可以由函数glPushAttrib和 glPopAttrib来操纵。相似的,客户端状态堆栈可以由glPushClientAttrib和glPopClientAttrib来操纵。

  glGet是一个用来查询图形上下文的各个分量的通用的函数。OpenGL为每个状态元素定义了符号常量(例如:GL_CURRENT_COLOR, GL_LINE_WIDTH等),为了获得指定的图形上下文分量的当前值,符号常量应该作为参数传递给glGet。glGet也有一些变体,分别用来返回 integer(整型),float(单精度浮点),double(双精度浮点)以及boolean(布尔类型)类型的状态值。更多复杂的状态可以由特定的“get”函数来获得。例如glGetClipPlane, glGetLight, glGetMaterial等等。错误也可以由函数调用glGetError发现。


1.6 处理管线

  为了实现OpenGL特定的行为,这些多样的操作被定义定义为由一个特定的顺序来执行,因此我们可以认为OpenGL是一个图形处理管道。

  作为开始,让我们先来看一张图表,可以了解一些OpenGL是如何通过OpenGL1.5定义的。图1.1被称作:固定功能的OpenGL (fixed functionality of OpenGL)。这张图标显示了自OpenGL定义以来的基本原理。它试图展示OpenGL管道的主要特性以达到概览的目的。在 OpenGL1.1,1.2,1.3,1.4以及1.5中加入了一些新的特性,但是OpenGL的基本结构并没有改变。我们使用固定工能是因为每个 OpenGL实现都要求具有相同的功能,并且给定的存在于OpenGL说明书中的输入集合,都能得到相同的结果。其中产生的的(内部)操作以及顺序由 OpenGL说明书所定义。

  不过你要注意,OpenGL实现并不要求精确地匹配如图1.1所示地操作顺序。OpenGL实现可以自由的改变操作顺序而与OpenGL说明书所要求的结果保持一致。很多革命性的软件和硬件构架已经被设计出用来实现OpenGL,,并且这些实现看起来并不像图1.1所示的那样。然而,这张图表是我们讨论OpenGL渲染方式的基础,即使底层实现与上图有很大的不同。

图 1.1. OpenGL操作概览

返回顶部】 【打印本页】 【关闭窗口

关于我们 / 给我留言 / 版权举报 / 意见建议 / 网站编程QQ群   
Copyright ©2003- 2024 Lihuasoft.net webmaster(at)lihuasoft.net 加载时间 0.00353