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

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

楼主#
更多 发布于:2004-04-27 14:17
<b>目 录
</b>  9.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/9.htm#9.1" target="_blank" ><FONT color=#000000>常规设计方法</FONT></A>
   9.1.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/9.htm#9.1.1" target="_blank" ><FONT color=#000000>面向对象编程</FONT></A>
   9.1.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/9.htm#9.1.2" target="_blank" ><FONT color=#000000>脚本</FONT></A>
  9.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/9.htm#9.2" target="_blank" ><FONT color=#000000>构建工具</FONT></A>
   9.2.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/9.htm#9.2.1" target="_blank" ><FONT color=#000000>OpenGL</FONT></A>
   9.2.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/9.htm#9.2.2" target="_blank" ><FONT color=#000000>Direct3D</FONT></A>
  9.3 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/9.htm#9.3" target="_blank" ><FONT color=#000000>应用程序的构建策略</FONT></A>
   9.3.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/9.htm#9.3.1" target="_blank" ><FONT color=#000000>实体视处理</FONT></A>
   9.3.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/9.htm#9.3.2" target="_blank" ><FONT color=#000000>室内场景视处理</FONT></A>
   9.3.3 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/9.htm#9.3.3" target="_blank" ><FONT color=#000000>室外场景视处理</FONT></A>


<HR align=center width="95%" noShade SIZE=1>

  <B>引言</B>
  在前几章我们已分析过许多3D计算机图形技术,这些技术允许我们在计算机屏幕上模拟并显现虚拟场景。然而,所有这些算法必须通过结构化的方法在真实的计算机程序里结合起来。在渲染管道中每一层都必须同其它层有效地结合起来。在管道渲染中每一层都必须相对于数据结构和表度虚拟场景的代码来构造。隐面消除算法必须被恰当的置于管道中等等。
  尽管有效率地编写易读易组织的计算机程序经常被认为是艺术而不是科学。不过在这一章里,我们仍然尝试分析在这方面有所帮助的一般规则。
  自从第一台计算机及其编程机制发明以来,相当多的注意力投入了对设计和编写计算机程序有帮助的范例(paradigm)中。最早的抽象概念允许把程序分离为独立的子程序或函数,随后则是构造派生数据类型(如来自于整型或字符型这样的基本数据类型的结构和数组)的机制。晚些出现的结构化编程则定义了编程语言应提供的最少流控制声明。相对近些时候,面向对象的编程范例开始提供用于代码共享和再利用的工具。
  尽管总是有很多承诺说通过应用范例在相当程度上使编程任务变得容易。但在某种意义上,他们仅仅能部分实现。某些在今天编写的一些程序就象计算机刚出现时所编写的代码一样模糊和难以理解(有时这种情形甚至更为恶化)。现在可利用的工具和策略无法使我们再次亲身体验分析和思考的必要性,这一点并不奇怪。无法正确地去分析和思考,仅依赖于复杂的工具会导致难以置信的调试问题,以及性能上的降低和不能用的产品。
  让我们考虑一下3D计算机图形领域内用来分析、设计、实现计算机程序的常规方法以及某些特殊的构建应用程序的例子。

<B>9.1、常规设计方法</B>
  不同的应用程序有不同的目的和目标。为了发展的目的,依靠应用程序的价值或其他客观因素,将分配不同数量的资源。产品本身是以特定的市场为目标,因此,基于特定的硬件平台。结合应用程序领域的特性,这些将作为确定作用过程的考虑因素,作用的结果,就是实现策略和应用设计。
  在我们开始考虑设计和实现策略之前,从某些公共标准的观点来评估应用程序的目标总是有帮助的。尽管这些标准并不是严格的,但在大多数项目中其中的一些有可能遇到。例如,应用程序完成的品质,及运行效率或速度就是两个重要标准。适应性,比如以不同数据格式工作的能力,是另一个标准。依赖于特殊环境和项目的长期目标,复用性和可移植性的考虑同样重要。
  例如,对于游戏,性能标准可能是很重要的,而适应性可能次之。在另一方面,计算机辅助设计领域的应用程序可能不要求高的帧速。然而,高效率、适应性及可移植性可能会被给予很高的评价。光现实主义(Photorealism),以及因此而产生的图象质量,可能正是人们在交互式游戏中所想要得到的,但它没有效率和实现交互性所需的帧速那么重要。没有达到较好的图象质量可能会使游戏吸引力差一些,然而,帧速过慢却会使游戏无法使用。另一方面,产生用于广告或电影的动画序列的应用程序大多都要求可能的高质量图象,而其产生的速度则次之。
  一旦我们分配了各个标准的重要性,就有可能开始为选定的设置考虑可用的可选实现方法。事实上,在一个场景中不可能使所有的标准都满意。它们大多相互依赖,差不多经常是鱼和熊掌不能兼得。例如,品质和速度经常无法同时做到。然而,重要性要素分配的真正目的是指导开发走在最重要的方向上,为了整个项目而牺牲次要的因素。
  为了达到某些标准,诸如质量和效率,我们必须选择适当的算法。在前几章我们已详细的考虑了这一点。其他标准,如适应性、复用性和可移植性都依赖于选择的算法,但在编写和构造代码中也可能要用特别的方法。
  在第一章,当讨论到硬件相关内容时,我们已看到了实现应用程序的移植性的方法。我们把应用程序分离为硬件相关和硬件独立两部分,并定义二者间的接口。第一部分,我们称之为硬件接口,其实现要依赖于特殊的硬件,而硬件独立部分则是可移植的部分。
  产生可重用的代码并引入必要的适应层也要求以特殊的方法构造应用程序代码。对这一点,利用指定的编程范例通常是有帮助的。在这一方面,我们将考虑面向对象的编程和脚本方法。前者允许我们编写可重利用及可维护的代码。我们将在通过提供必要的适应层把数据存取关联起来讨论后者,后者同时也描述了实时选择或实时过程。

  <B>9.1.1 面向对象编程</B>
  面向对象的编程首先提出了构造应用程序的一种方法学框架。在这种方法学中,同具有分离的数据结构和程序的命令形式的编程相反,数据和代码被紧密的捆绑在一起。当使用面向对象的编程时,我们依赖于某种语言(如C++或Objective C 等)支持这一范例的特定特性。这两种语言都用额外的结构扩展了基本的C语言。我们并不将集中于这些语言的语法和其它细节,而是集中于范例的总体观点及如何与构造3-D图形应用程序相关。
  在代码和数据的统一中,重要的概念是抽象数据类型、继承及多态性。
  在诸如C或Pascal语言中有许多预定义的数据类型, 如整型、字符型或浮点型。数据类型的派生规则已存在,但在某种意义上它们的能力是有限制的。尽管我们能描述一种新的数据(比如构建整型数组)类型的存储要求。然而,新的数据类型可能因为不能恰当描述适用它的运算而存在不足。
  抽象数据类型的概念能够改善这种情形。在面向对象的语言中我们即可定义新类型的存储也可定义该类型的特殊运算。在这篇文章中类型也指的是类。类的单个的类实例通常被称为对象。这就是“面向对象编程”的由来。在C和C++中描述类的特殊方法在程序清单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/Image196.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>程序清单9.1 在Objective C和C++中定义的抽象数据类型</B></FONT></TD></TR></TABLE>
  从图9.1中可看到这两种语言,尽管有不同的语法, 但它们在提供的功能上是相似的。也许,两者间概念上的最大不同在于,Objective C在运行时动态地确定有关类型。而C++大多是静态处理。因为前者经常提供另外的适应层,尽管可能导致一定的代价,但这些代价在Objective C中是非常小的。
  运用抽象数据类型的架构,举例来说,我们能通过把数据的存储定义为一种包含两个浮点数的结构来创建复数,并依赖现有浮点数的运算,比如说加法和乘法,完成必要的复数代数支持。进而这种数据类型能够同基本数据类型一样用于整个程序中。
  这种方法对程序的结构是非常有益的。程序将更易读。而且还可以为每一种数据类型提供有效的运算作为其公共接口,使用这种类型的程序部分而无需理会类型的内部结构。封装的数据类型内部结构在将来可能会改变,但因为这种类型的使用对其公共接口是有限的,适当地实现这种改变,不会影响使用它的代码。对应用程序的可维护性来说,这种考虑也是非常重要的。编写得好的类型对将来项目中得重利用也是很好的准备。
  在3-D图形应用程序的例子中,我们遇到了许多种使用抽象数据类型显然有利的情形。这类情形经常与定点数和以矩阵形式描述的3-D变换联系在一起。这两种过程都有定义完善的运算集和利于以类来实现。在这种封装的方式中,实体、场景等的模拟表现也对此提供了机遇。
  使用抽象数据类型存在着一定的风险。尽管同命令式语言相比,使用面向对象的语言编译产生的代码的效率并非很差,但其效率损失的风险来自于我们所进行的尝试,有时候,就是编程。如果我们尝试封装一切,以及滥用动态分配和小对象的消除,花费将变得很大。举例来说,有时候我们试图把3D顶点封装为类。尽管这是有效的,而且在3D编辑器的情形中有所帮助,但渲染管道构建可能因此而效率受损。顶点通常在流中处理,相对单个顶点来说,它在计算数量上不重要。
  一般地,抽象数据类型有助于构建应用程序的高级层这一点是稍微有些让人怀疑的。对低级层应用这一点可能有益,但应该小心谨慎地去完成。
  面向对象语言也提供了其它的类型间相互作用的重要原则。一个类型可以继承一个父类或甚至几个父类的属性。这就允许相对不同的模块共享某种功能。举例来说,可以通过这种方式组织类型来描述不同的3D模块,这样描述多边形实体的类型继承自轮廓类型。这两种类型都依赖于顶点集,实体类型能够继承对该顶点及应用变换的功能。另一个重要原则是多态性。这种性质允许我们编写应用于不同类型对象的通用算法。多态类型共享某些函数接口,但可以不同的方式实现这种功能。举例来说,处理不同3D模型的类型是多态的,这就意味着,他们全部具有了渲染其自身的能力。渲染取决于不同的类型而被完成,但使用这种功能的算法无需理会细节,可以处理通用模型,只需知道什么时候开始进行。
  举例来说,这种方法可以切实可行地用于飞行模拟中的场景可视化。这些应用程序适于依赖表面被分割为正方形单元之处的数据结构,其中的每个可能包含某些表达对象的3D模型,该对象位于描述的场景区域。自然地,对象可能有不同的类型,要么是表现能量线塔的网状结构,或者表现建筑甚至表现跑道上灯光点集的多边形模型。渲染场景的算法必须首先光栅化表面多边形,然后是位于该多边形上的对象。如果表现3D模型的类型在渲染上是多态的,编写的场景类型就能够不必理会模型内部结构,只取决于公共多态功能接口而使其渲染自身。
  总之,面向对象编程对实现代码重用标准来说是非常重要的工具。如果正确地运用它,对创建清晰易懂的代码是非常有帮助的,同时,也提供了代码共享和提高应用程序可维护性的机会。

<B>  9.1.2 脚本</B>
  在前一节中,我们已经讨论了如何通过恰当地构造和使用面向对象的范例来改善代码的可重用性。此种方法可以更加容易地重新调整一个应用程序以实现不同的目的,从这个意义上讲,它还有助于增加程序的灵活性。在这一节中,我们将要涉及到灵活性的不同概念。在不同的场合下我们需要重新讨论一个应用程序在运行时作了些什么。我们经常想要应用程序来控制具有不同复杂程度的数据结构。有时也需要改变一些程序上的做法,例如,以一段动画或计算机游戏的形式显示出来的某些对象的运动路径。
  自然,如果需要作改变时都要重新编译程序那就太局限了。一个显而易见的解决方法是:把改变的元素从可执行代码中分离出来,将它们的表度单独存放在磁盘上。这些表度由可执行程序在运行时读取并用程序中预定义的方式来解释。
  用来描述不同虚拟场景的数据可被存为二进制格式。这种方法保证了在内存中能以较高效率对数据结构进行重组,但同时它可能限制了数据的可移植性。以二进制格式存储的信息由于不能立刻理解它的意思也会较难于管理,因此,需要一些特殊的实用工具集来生成和处理数据。特别地,当信息的数量不是很大时,用文本格式存储信息会更好。
  回想一下会发现一种程序设计语言会提供这组功能。在C或Pascal这类编程语言中,我们可以描述需要的数据结构和函数,通常人们都会通过某些小型语言来得到相似的方法并表征应用程序中的改变部分。这些语言经常被称作脚本。应该注意,我们通常想使用脚本来处理一些非常特殊的功能,因此没有必要引入隐藏在现代程序设计语言后的所有的复杂的东西。
  为了定义一种语言,我们必须设计并恰当地表达它的语法和语义。语法描述了语言中所有合法的表达式,语义则描述了解释表达式的意义和效果。语法是用来描述语言的句法的一系列正式的规则。对很多实践中的目的来说,使用Backus-Naur 形式来表征语言的语法会很方便。这种表征方式使用语言中的其他元素描述不同的元素,甚至会使用递归方式,元素以其自身定义。例如,在后面的一个非正式的语法范例中,术语function被描述为四个字符中的一种(字符间的竖线代表“或”)。

  <FONT color=#330099>function = + | - | * |
  expression = number | (expression function expression)
  LANGUAGE = expression

</FONT>  语言本身是被最后一条语句定义为某些一个接一个的表达式(符号{...}表示0或者在大括号中的条件下任意事件。)这种语法描述了一系列算术表达式,例如:(1+1),((1+2)/2)等。我们能利用语法来进行句型分析以认可符号字符串的合法性并对合法的序列进行必要的操作。每一个规则都可被作为单独的函数来实现,这些函数能够解释正确的语言元素。因此符号串:(1+1)将首先被规则3分析, 它调用规则2,经过两次递归调用后发现此表达式是合法的,解释例程会同时构造一个内部表度来表达解释后的表达式的语义。在上述示例的情形下可能会有一个逆符号或不相干的表度以便对算术表达式进行赋值。我们并不是用手工进行句型分析编码,而是利用一些句型分析生成器如:yacc(一种编译器的编译器)等。当我们指定的语法与上述例子不同时,句型分析生成器会产生用以计算机语法分析的C代码。利用这样的一个工具以及一个扫描生成器(如lex(flex)),它可以产生一个扫描例程C代码以便把输入的文本流分割为词汇单元也就是所谓标记, 我们就能大大减少为建立一个脚本语言所需的编码工作。
  为了实现3D图形应用,我们可以利用脚本来表征某些动画物体不断变化的动作。一个与我们先前看到的相似的脚本可能会表征一些算术表达式,这些表达式用来描述某些实体是如何依赖于应用程序的不同状态来改变其方位角的。在需要改变动作时,可以只修改一些外部的文本文件便可达到目的。
  我们亦可利用脚本来描述表征虚拟场景的数据结构。例如:下面的语法描述了一种语言,它与C中描述静态数据的语言相似。

  <FONT color=#330099>type = int | [number]type | {type {type}}
  variable = number | [variable {variable...}] | {variable {variable...}}
  statement = type name type | var type name variable
  LANGUAGE = statement {statement...}

</FONT>  在这种语言里,我们可以把数据类型描述为type array [5]int(即一个包含五个整数的数组),或type triple {int int int}(表示一种包含三个整数的结构), 也可以描述为数据本身,如:var array A [1 2 3 4 5]。许多应用程序都使用了脚本技术。例如:采用光跟踪算法的可视化包经常用它们自己的语言来描述虚拟场景。同样的方法也适用于计算机游戏,脚本描述运行时各种不同的行为。

<B>9.2、构建工具</B>
  我们在前几章中讨论的算法是很多不同的3D应用程序的基础。象3D转换和光栅处理之类的任务必须被几乎所有使用从世界到屏幕视处理的应用程序来完成。与某些算法和处理一样,许多任务已经有不同的软件开发工具或库来实现,还有一些直接由硬件实现。当可以得到这些工具或硬件的时候,开发工作将变得容易许多。
  既然许多3D图形算法相对来说很简单明了,而且在完全不同的应用中都得到了非常普遍的应用,那么采用硬件来实现一些基本的功能的想法自然具有很大的吸引力。诸如平面光栅处理,明暗处理和纹理映射多边形,3D变换中的Z-buffer等功能都通常被置于图形加速卡上。甚至一些通用处理器有时也提供对图形应用程序特别有用的操作,例如用于数据向量相乘的单指令乘法。它们使得可同时利用几个参数进行同一操作。使用这些指令可以并行计算RGB光照的纯分量从而大大加快了渲染任务的速度。针对Intel的Pentium处理器的MMX(多媒体扩展)指令就实现了这种方法。
  通常来讲,硬件特性的数量和种类变动很大,因此这种设备并没有一个通用的接口。幸运的是,现有的软件开发工具和库经常能充当硬件通用接口。这些库可以在一台最小配置的计算机上进行纯软件渲染,或者在具备辅助硬件时使用硬件。下面几节中我们将要讨论两种库的大致结构:OpenGL和Direct3D。应当注意,这里的描述只是大概,我们应该把注意力集中在少数基本概念上。
喜欢0 评分0
夜落了,风静了,我喜欢一本书,一杯茶,一粒摇曳的烛光...
qqpp521
路人甲
路人甲
  • 注册日期2007-06-04
  • 发帖数6
  • QQ
  • 铜币113枚
  • 威望0点
  • 贡献值0点
  • 银元0个
1楼#
发布于:2007-12-02 18:32
<img src="images/post/smile/dvbbs/em01.gif" />
举报 回复(0) 喜欢(0)     评分
游客

返回顶部