hyde
路人甲
路人甲
  • 注册日期2003-09-24
  • 发帖数555
  • QQ
  • 铜币1457枚
  • 威望0点
  • 贡献值0点
  • 银元0个
阅读:1655回复:1

3D图形编程指南10续 - 构建3-D应用程序的实践方面

楼主#
更多 发布于:2004-04-27 14:18
<b>9.2.1 OpenGL
</b>  OpenGL很大程度上基于SGI的IRIS GL库。由于后者很流行,人们成立了一个泛行业的检查委员会来维护开放的,与平台无关的图形标准。结果,OpenGL产生了,它适用于很宽范围的硬件平台和操作系统,支持厂商包括SGI、IBM、DEC和Microsoft。
  OpenGL为3D变换、裁剪、照明和光栅处理提供了核心功能。这些功能通常用硬件实现,OpenGL为不同的加速卡提供公共接口。
  OpenGL被设计为状态机(state machine)函数。许多函数的调用建立了一些参数,这些参数可在其它函数子调用中被重新调用,例如设定当前的颜色和当前的纹理贴图用于将要进行的图元光栅处理。这就使GL应用程序可在网络上工作,在网络上,一台计算机上的程序生成了一些GL命令,而命令的翻译工作在另一台计算机上完成。因此,这个库的应用程序接口已经几乎完全程序化了,而且它实际上不需要分配任何数据结构。OpenGL的逻辑结构大致如图9.1所示。


<TABLE cellSpacing=0 cellPadding=2 align=center border=0>

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/9.1/Image197.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图9.1 OpenGL逻辑结构</B></FONT></TD></TR></TABLE>
  正如图9.1所示,GL命令可以通过显示列表累积起来用于将来的处理,也可以立即通过渲染管道发送(甚至可能跳过某些阶段)。求值器阶段允许以简单图元(线段和多边形面片)逼近曲线和表面,这时的后续的阶段可以只对简 单数据进行操作。下一个阶段除了作一些剪裁等简单的操作之外还进行一些关键的操作,如3D转换和光照的计算。剪裁后的图元被传递到光栅器,光栅器计算片断流(带有颜色、深度和纹理信息的象素地址)并把它们发送到下一个处理阶段,即对片断流进行Z-buffer和改变帧缓冲内容等操作的片断运算。
  OpenGL并不局限于任意一种特定的架构或操作系统,为了允许应用程序访问特定的特性并使其符合必要的其他标准,通常会提供附加库。
  既然GL以状态机形式工作, 描述单个图元的几何信息在一系列GL函数调用中传递。这样的序列由调用glBegin启动,在这里指定要描述的图元类型。已有的几种类型包括:GL_POINTS, GL_LINES, GL_TRIANGLES, GL_QUADS, GL_POLYGON 以及少数几个其他类型如:GL_TRIANGLE_STRIP或GL_QUAD_STRIP。glBegin后面是一些指定顶点信息的函数调用。取决于想要的渲染模型,我们可以在一个顶点中定义各种各样的信息。顶点坐标通过glVertex调用确定(为了方便,许多GL函数有替代的形式可用于不同的参数类型或排列)。顶点的当前颜色要调用glColor。也有可能通过对glNormal(用于Phong照明模型)或glTexCoord(用于纹理坐标)的调用以指定顶点法线。在渲染过程初始化后,调用glEnd结束顶点的定义。
  GL中的几何变换用4×4矩阵决定。OpenGL可在渲染过程中实现三种不同的变换。它们是:对象空间到视空间(称为模型-视变换),投影变换和纹理映射变换。对上述的每一种变换都有矩阵堆栈,并且使用GL的应用程序必须在图元被传递到渲染管道前指出要进行哪一种变换。
  如前所述,除了在立即模式下图元在调用glEnd时通过渲染管道传递之外,还有可能以显示列表的形式键入GL命令并在之后执行。在很多情况下产生一个显示列表使得库可以预先计算一些数据,从而加快后面的执行速度。GL也提供了高级例程,其中可以指定更复杂形状进行渲染,而不仅仅是单个的图元。实用库(glu)中提供了完成此任务的函数。
  总的说来,GL实现了宽范围的图形应用程序需要的功能,它具有相对易用,支持(部分)硬件设备,具有可移植性,因此成为一个非常流行的工具。

<B>  9.2.2 Direct3D</B>
  Direct3D是DirectX软件开发包的一部分,它由Microsoft开发,适用于Microsoft的32位操作系统。这个图形库包含了与OpenGL不相上下的功能,同时也可提供对不同图形加速卡的统一访问方式。
  与OpenGL相似,Direct3D由几个层组成,高级的保留模式层能对复杂的几何物体进行控制,而低级的立即模式层则代表真正的多边形渲染管道。图9.2描述了此库的管道实现逻辑结构。


<TABLE cellSpacing=0 cellPadding=2 align=center border=0>

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/9.1/Image198.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图9.2 Direct3D逻辑结构</B></FONT></TD></TR></TABLE>
  使用Direct3D的应用程序通过调用Direct3D API中的特定函数来初始化接口对象,设定模块的状态(如变换矩阵、光源和材质的描述等),还可构造执行缓冲(显示列表),其中包含了几何信息,描述变换的命令和必须执行的进程。执行缓冲遍历执行3D变换、光照和光栅处理的管道模块,然后从另一个叫Direct Draw的库(负责帧缓冲存取)里调用函数。这些模块可以进行纯软件的计算,也可以在具备硬件设备时使用它。
  Direct3D也允许将模块的功能用于渲染之外的其他目的,例如,我们可以调用变换模块以在一些建模工作或边界盒计算中转换顶点集。
  不幸的是,Direct3D(特别是在立即模式中)经常无法从应用程序中隐藏它内部工作细节,这使得它要进行大量的初始化工作并维持很复杂的数据结构。
  尽管它的接口相对庞大一些,但Direct3D确实可以提供对很广范围内的图形硬件的统一访问,保留模式层可以很方便地对一些简单的图形应用进行快速开发。

<B>9.3、应用程序构建策略</B>
  有很多因素影响着一个应用程序的设计。任何工程都会有自己的考虑。在本节中,我们将只考虑不同技术带来的最基本的平衡,以及一些要求所共有的大略解决方案。

  <B>9.3.1 实体(solid body)视处理</B>
  在许多情形下都需要观察实体或一组实体的3D模型。CAD、医学成像、科学可视化只是其中一部分。正如我们在此章第一节所讨论的那样,在选择任何专门的策略之前我们应该首先了解用来表达我们要实现的应用程序之目的不同因素的重要性。之后,根据不同因素的相对重要性来选择算法。
  让我们讨论一下几种不同的可能情况。例如,若我们想要产生高质量的图像或图像序列,性能限制由此被放松,一种可能的方法是使用屏幕到世界的光线跟踪算法。这种算法有性能限制,但是对不同的光线模型和全局光效(如阴影,环境反射等这类对实现视觉真实度极其重要的因素)来讲,使用这种算法是很自然的。
  至于实现,一个使用光跟踪的应用程序可被分解成下面的主要逻辑模块。


<TABLE cellSpacing=0 cellPadding=2 align=center border=0>

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/9.1/Image199.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图9.3 光线追踪算法的逻辑模块</B></FONT></TD></TR></TABLE>
  正如我们看到的,这种方法的最基本的运算操作是寻找从观察者投射的光线与虚拟场景的交叉点,为了实现全局光效我们可能不得不利用与眼睛投射光相同的机制引进附加光。图9.3示出了这种方法的逻辑结构。当然,这种设计已被大大简化,忽略了模块本身的内部结构。例如,如果我们想要进行可视化的场景很复杂并且由许多图元构成,我们也将不得不考虑采用特殊技术在场景遍历模块中对空间分割。一个更详细的模型必须考虑到方方面面的因素,然而,这个方案给出了模型间的相互依赖的表度,并洞察了可能的应用程序结构。
  另外也有一些应用程序可以忽略质量,但是不能忽略性能。很明显,这时光跟踪方法不适用,取而代之,我们应该考虑从世界到屏幕的视处理方法。我们已经看到了使用这种方法显示3D模型所需的步骤。模型的顶点必须经过坐标转换阶段。如果其中包含了透视投影,则必须先进行体剪裁。进一步,组成模型的图元被光栅处理到帧缓冲。世界到屏幕图形管道的一般轮廓如图9.4所示:


<TABLE cellSpacing=0 cellPadding=2 align=center border=0>

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/9.1/Image200.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图9.4 基于世界到屏幕的应用程序逻辑模块</B></FONT></TD></TR></TABLE>
  图9.4仅仅示出了基本的管道,它并未反映应用程序依赖于附加标准的其他重要方面,特别是隐面消除和照明。依靠现有的工具(如OpenGL或Direct3D)我们可以部分或全部地利用这里执行的管道,并把开发工作限制在某些模型上,例如隐面消除和高级的建模任务。
  我们已经讨论过几种隐面消除算法:画家算法、beam树、扫描线算法和Z-buffer算法。除了复杂性因素之外,某些算法有特定的限制,因此只适用于某些场合。而且,不同的算法在渲染管道中占有的位置也不同,举例来说,使用画家算法的BSP树方案只适用于那些形状为静态的由多边形多面体表示的对象。如果对象的形状在动态改变,我们就不得不在运行时重建与之相关联的BSP树,这样做的花费很大且不能达到算法的目的。
  进一步来讲,大多数隐面消除算法对以多边形多面体表示的3D模型都是适合的。如果我们拥有一项技术可以直接光栅化某些种类的图元(如双三次曲面),则只有Z-buffer算法仍然适合。另一方面,Z-buffer算法虽然很普通,但它由软件实现时代价很大,因此,在某些实例中,将表面镶嵌成多边形多面体以避免使用Z-buffer算法会更令人满意。
  如果光线和纹理映射比较复杂,则画家算法不太适用,因为每当新绘制的多边形覆盖前一个多边形时都要损失大量工作。在这种情况下,我们可以使用扫描线算法、beam树算法或修正的Z-buffer算法(它把光线的处理当作一个后续步骤)。扫描线算法能够与用于获得多边形从后到前顺序的BSP树一起使用,用于计算选择哪个多边形最靠近扫描线的当前跨度。beam树也依赖于由预先计算的BSP树导入的顺序。然而,这两种实例都必须做相当大的渲染管道修改。对前者来说,所有的多边形在同时进行渲染,可以依赖于基于边的多边形描述。对后者,屏幕边界裁剪能够很容易地同beam树算法统一,因此稍微简化了管道。
  正如能够看到的,模型表度和可视化策略应该一同被考虑。作为这种考虑的结果,我们可能必须重新调整原始的标准集。如果速度是最重要的因素,我们可能必须提供3D模型表度以适合这种算法数据结构。另一方面,如果对精度和图像质量的考虑更重要,我们可能被迫使用特定的数据结构,并选择相应的隐面消除算法。
  在第八章,我们讨论了在世界到屏幕视处理方法中如何在构架中导入光线和照明效果,正如我们看到的,全局照明效果,例如阴影,对计算来说相当昂贵。在静态照明的情形中,阴影能够被预先计算出来并导入到模型纹理映射或作为独立的、不同照明的图元。在这种方式中处理动态光变得相当昂贵。如果我们使用Z-buffer算法用于隐面消除,我们能够增强该算法来处理能够计算动态阴影的光线Z-buffer。

  <B>9.3.2 室内场景视处理</B>
  室内场景视处理在诸如室内设计这样的CAD应用和视频游戏领域中经常要用到。当然,任何室内场景都可以被看作对象的汇集,然而,正如我们已经讨论过的,室内场景有一些规则的属性能够用来实现比一般情形中更好的性能。举例来说,我们利用这么一个事实,室内场景通常是房间的集合,而房间是凸或近凸的多面体。
  对CAD和可视化游戏来说,产生虚拟场景的交互漫游是非常重要的。因此, 在优先级列表中效率这一条必须非常靠前。
  尽管在一般情形中,把光线跟踪用于实现交互式帧频非常困难,但在特别的情形下,我们可以利用虚拟场景的某些属性以此算法实现适宜的性能。举例来说,在大多数的设置中,漫游不需要所有的6个自由度。毕竟,许多现实的室内场景位于诸如建筑物等的地板上,并且在我们的漫游中并不需要仰视或俯视。表度场景的多边形同样在方位上受限制。要么使用垂直的物体表示墙,要么使用水平的物体表示天花板和地板。在这种假设下,光线跟踪能够被相当大地简化。无需对图像位图上每个像素都投射光线,只对一整列像素投射光线,我们能够从很少的交叉点计算中很容易地找出在列的不同高度上哪些光线产生了交叉。
  如果条件不能放宽到这样,同前节的解决方案一样,我们应该考虑世界到屏幕的解决方法。尽管我们看到的所有这些考虑都针对应用于这种情形的3D实体模型,但通常室内场景中大数量的多边形表度使某些隐面消除技术不再有用,举例来说,多边形排序就是这样。然而,正如我们在隐面消除那一章中看到的,某些场景的物体用整个相互连接的凸体构成来表示房间,这样就能很容易地计算多边形从后到前的顺序。如果体不是凸的,我们必须在每个体上关联BSP树,或者甚至把整个场景构建为一个整体。内部场景通常是静态的这一事实使这种解决方案非常有吸引力。
  既然我们很可能向在多边形上关联纹理贴图来描绘室内环境,并且这类多边形的光栅处理比较昂贵。我们可能必须应用某种技术来限制隐藏的多边形的光栅处理,扫描线算法就是一个例子。另一种技术把体联合为一个列表,从某个点完全可见或部分可见的多边形归入一个体。尽管寻找这类列表在计算上很昂贵,但它能够通过预处理步骤来实现,用存储为数据结构的结果来表示场景。通过限制在每个体中可能要光栅化的多边形的数量,我们能够获得重要的性能。
  同样的,光线可以被限制在特定的区域,这样,光源对区域外的地方就不产生任何效果,或者,甚至更好一些,我们可以把它用于从当前体可见的多边形列表的目的。然而,为了合适的照明,我们需要表达诸如阴影和环境反射这样的全局照明效果。同前面的解决方案一样,我们自己把它限制为静态光和预计算阴影。区域可以用于引入有限的动态光。
  一些室内场景需要表达镜面反射环境。正如我们在第八章看到的,一种解决方案是可以通过场景的几何结构克隆来表示镜面。在这种方式中,渲染在通常的方式上加倍,给出了环境反射的效果。

<B>  9.3.3 室外场景视处理</B>
  室外地形视处理在飞行模拟、视频游戏、地理信息系统(GIS)等的应用中非常必要 。同室内场景的解决方案一样,被建模的对象的特殊本性使某些特殊的方法对其可视化非常有用。
  在大多数情形中,我们不能减少处理这类应用程序所需的自由度数目。在极少见的情形下,如果可以这么做,那么同前一节中提到的解决方案一样,可以考虑光线投射算法。应该注意到,光线投射算法很容易被并行化,既然投射光线的计算是独立的,那么,同合适的特定目的的硬件相结合,可以利用这个事实以光线投射算法实现6自由度和交互地形可视化。
  然而,更普通的是,从世界到屏幕的可视化更适当。我们已经讨论过地形的高度场(height-field)表达。利用这种表达方法,能够通过合适的模型遍历来获得多边形从前到后的顺序。
  在这里涉及到的任何一种应用程序中,可能地形建模都相当大。因此,从观察者所在的位置开始渲染是非常合适的。应该注意到,既然地形相当光滑,则绘制用于覆盖前面经过光栅化的多边形的数量是有限的,尤其是采用背面culling。因此,我们通常不必涉及扫描线隐面消除或者类似的技术,以减少昂贵的多边形光栅处理,因为这种处理过多会导致透支。
  照明和光线在这类应用程序中相当大程度上被预计算,因为地形通常是静态的。使用一些细分策略限制每个光源的影响,从而能够引入本地动态光。在一些情形中,当在地形上模拟水面时,我们可能要表度环境反射。这种效果可以通过相对水平面克隆临近水面的物体来实现。渲染加倍的沿着水面表达反射图像的多边形。
  除了渲染表达地形表面的多边形外,通常也必须显示位于表面上的物体。因此,在3D实体一节中讨论的可视化技术将要与应用程序中处理地形的技术协同起来。

<B>  小结</B>
<FONT face=楷体_GB2312 color=#993300>  总的来说,我们必须强调3D图形应用程序设计原则的重要性。在这个过程中,许多决定都要在程序实现目标的基础上来考虑。为了确保程序运行的速度和图像的品质,就要选择合适的解决方案。为了达到程序的可维护性和重用性的要求,我们可以采取一些特殊的编程技术,如面向对象程序设计和脚本技术。</FONT>
全文完
喜欢0 评分0
夜落了,风静了,我喜欢一本书,一杯茶,一粒摇曳的烛光...
yutian
路人甲
路人甲
  • 注册日期2003-11-21
  • 发帖数69
  • QQ
  • 铜币254枚
  • 威望0点
  • 贡献值0点
  • 银元0个
1楼#
发布于:2004-04-28 22:42
老兄,你能不能压成一个包放上来呀!?
举报 回复(0) 喜欢(0)     评分
游客

返回顶部