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

3D图形编程指南2 - 硬件接口

楼主#
更多 发布于:2004-04-27 13:49
<b>目 录
</b>  1.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/1.htm#1.1" target="_blank" ><FONT color=#000000>3D应用程序与硬件的交互作用</FONT></A>
   1.1.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/1.htm#1.1.1" target="_blank" ><FONT color=#000000>在计算机屏幕上显示图像</FONT></A>
   1.1.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/1.htm#1.1.2" target="_blank" ><FONT color=#000000>事件反应</FONT></A>
  1.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/1.htm#1.2" target="_blank" ><FONT color=#000000>使用不同的体系结构</FONT></A>
   1.2.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/1.htm#1.2.1" target="_blank" ><FONT color=#000000>MS-DOS</FONT></A>
   1.2.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/1.htm#1.2.2" target="_blank" ><FONT color=#000000>MS-Windows</FONT></A>
   1.2.3 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/1.htm#1.2.3" target="_blank" ><FONT color=#000000>X11</FONT></A>
   1.2.4 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/1.htm#1.2.4" target="_blank" ><FONT color=#000000>NeXTStep</FONT></A>
   1.2.5 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/1.htm#1.2.5" target="_blank" ><FONT color=#000000>MacOS</FONT></A>


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

<B>  引言</B>
  3D计算机图形能够作为一个领域存在,这要归功于目前的计算机硬件。为计算机人工合成场景可视化而开发的模式、算法和技术因此也基于并受限于当前可获得的硬件能够实现的能力。在这一章,我们将要讨论交互式3D应用程序的基本原理,特别是它们的结构如何能被典型的计算机系统所支持。正如“交互式3D图形”这个词所指出的,有两个组件要考虑:“图形”— 代表计算机屏幕上的图像,以及“交互”— 在应用程序执行期间对用户的输入起反应。其它的3D图形应用程序的组件(在后面的章节中讨论)包括虚拟世界、图元渲染、隐面消除、光线等等,并不直接依赖于计算机设备的细节(除了某些基本算法,例如光栅,直接在硬件中实现)。然而显示或输入设备接口的细节通常依赖于硬件的特性。我们在这一章的第一部分将要讨论基本的3D应用程序同硬件的交互作用。管理应用程序执行的已有的计算机平台和操作系统,也介绍它们的细节和特性。在最后一部分我们要调查一下涉及到几种流行平台和操作系统的一些细节。

<B>1.1、3D应用程序与硬件的交互作用</B>
  图形应用程序的主要目标是在计算机屏幕上合成再现创建的图像。我们要讨论现今主要的技术—光栅图形技术。这种思想是把图像细分为规则的小块, 最普通的是矩形小块,每一块都有它自己个别的颜色(参见图1.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/1.1/1-1.jpg"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图1.1 光栅图像 </B></FONT></TD></TR></TABLE>
  由于计算机图形应用程序的多样性,与用户能够保持持续交互所必需的一点是特别的兴趣。一些交互式的计算机绘图程序必须经常在合成屏幕上描述运动,或者是观察者沿着场景移动,或者是场景本身移动,或者两者兼而有之。这由显示带有一定次序的称之为帧(frame)的图像来实现 。每一帧描述了场景的些微状态改变。只要在一个短时间内足够数量的帧被显示,对观察者来说,就得到了平滑运动的印象。
  在显示的时候,帧必须被完全绘制。在大多数场合,我们不想看到多边形或线段一个接一个地显示在屏幕上构建单帧的过程。而且,如果帧要被拍在电影胶片上,所有的像素必须在同样长的时间中可见。否则,胶片曝光会不均匀。通常的做法是在离屏(off-screen)缓冲中绘制新帧,同时前一个绘制完的帧就储存在主显示设备的缓冲区中,从而它被显示出来。一旦新的帧绘制完毕,离屏缓冲的内容就传递(也许是blit,位块传输)到显示设备的存储器内,对下一帧重复这个过程。不同的系统处理访问显存方式的确是不同的,对此不要惊讶。在早期的操作系统,比如说MS-DOS,显示设备的存储器可以被直接访问到。另一方面,当对MS-Windows、X-Windows、NeXTStep、MacOS或者甚至是使用某些DOS扩展器的应用程序编程时,直接写显存一般是不可能的。内存保护模式阻止我们这么做。因此,要调用不同的操作系统资源和使用某些低级的工作处理涉及硬件的接口。
  一个交互式的3D应用程序,除了能够在屏幕上渲染帧之外,还要必须对外部事件做出反应,比如说键盘敲击或鼠标移动,还要运用当前事件信息在未来的帧中导入改变。举个例子来说,按下箭头键改变场景被观察的角度,等等。处理外部事件通常依赖于执行流程(execution flow)——被特定操作系统执行的组成计算机程序代码的片段的次序。执行流程和应用程序结构依赖于操作系统。
  基于系统的“事件”通常假定代码必须依照发生事件的函数执行。换句话说,如果键被按下,同这个键关联的代码被执行。这种方法在NeXTStep中被用到了极处。MS-Windows则在较小的范围使用它,X-Windows也是这样。在另一方面,MS-DOS和UNIX不使用事件机制,除非我们自己操作。代码从入口处被执行,除非我们退出(或者操作系统不能承受下去)。
  因此,依赖于平台,处理外部事件应该充分地以不同方式实现。我们或者使用特定操作系统的各种各样的规定,或者可能的话,如果允许,直接访问硬件。
  让我们确定一下图形应用程序的哪些方面要必须面对硬件,以及有关交互作用的本质。后面,我们将要简要地讨论这些交互作用对目前的平台和操作系统来说如何被实现。

  <B>1.1.1 在计算机屏幕上显示图像
  </B>我们已经提到,显示在光栅显示设备内的图像存储在计算机存储器中。提供图形用户界面(GUI)的操作系统会为应用程序分配一些物理屏幕(窗口)的端口。为了创建窗口并随后在它的窗口上显示图像,图形应用程序必须同操作系统通讯。没有GUI的操作系统需要应用程序直接同显示硬件通讯,在这个工作上不提供或只提供很少的帮助。在那些情况下,应用程序必须把显示设备切换到合适的模式下,然后,为了显示生成的图像,还要把离屏缓冲的内容移到显示设备的存储器中。
  我们能够区分几种交互作用的类型,为了显示图像,图形应用程序应该同操作系统或硬件一起完成这些作用。其中最重要的是:打开和关闭图形输出以及图像的位块传输,这些可以被作为函数来完成。应用程序其它的模块用于屏蔽硬件以及同离屏缓冲通讯并调用前述的函数。
  这个范围,除了作为应用程序模块设计的起点外,也允许离屏缓冲保留来自显示设备的不同的数据格式。当然,同样的格式意味着位块传输函数不必执行有关格式转换的附加工作,但是在许多情况下,能够使用方便图形算法的数据格式渲染图像然后把它转换到能被显示设备理解的格式,是非常有帮助的。
  离屏缓冲包含了通常被称之为图像位图(bitmap)或颜色图(colormap)——表达独立像素的颜色数值数组的内容。至于如何把数值同位图中的每个像素关联起来,有许多可以被想到的途径。颜色值可以由位于不同位图部分(面)的位(bit)组成——即所谓的“平面(planar)”格式, 或者由包含单个像素颜色规范的字节或双字节组成 — 即所谓的“平坦(flat)”模式或“线性(linear)”模式 。图1.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/1.1/1-2.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图1.2 图像位图中的平面(planar)模式对平坦(flat)模式</B></FONT></TD></TR></TABLE>
  不同的显示设备保留不同的代表显存中位图的格式。一些设备可能支持几种格式。平坦模式对软件来说一般更方便,这是因为为了设置一个像素只需一个个别的存储器被访问。但是,深入地来说,硬件级访问可以转变为显卡完成的几种显存访问, 既然显卡通常同颜色通道(color channel)一起工作,因此,可能内部地支持图像的平面显示。然而,我们还是集中在平坦格式,因为现代图形设备似乎支持所有外部的交互作用。
  像素的颜色通常被描述位纯红、绿、蓝三种颜色(RGB模式)的联合。在第八章我们讨论它的物理原因,目前,我们假设这是合理的方案。因此,为了描述一个颜色,必须分配某些位表示这三个分量的强度。如果我们选择每个分量使用5位,则每个纯色可以得到32个强度级,描述单个的像素至少需要15位。这种情况组成的颜色值数组形成了平坦位图。在位图中的第一个值通常最左上角的像素,下一个单元值代表第一行的第二个像素等等。一行一行地包含了所有的像素。(参见图1.3)


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

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/1.1/1-3.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图1.3 显存存储RGB值 </B></FONT></TD></TR></TABLE>
  在实践中这种方法有一个不利的地方:空间需求。一个320×200的小屏幕, 每像素2个字节(这是包含15位的最小字节数),将会需要128K的存储空间。随着显示设备分辨率的加大,显存空间的需求也越多。只要内存是非常珍贵的硬件, 这种空间问题就通常是通过虚拟内存的方式解决。同RGB模式比较起来,我们有时候宁愿存储一个表的索引(在这里称之为调色板(palette)),这种方式可以使用较少的显存。调色板模式最适用的模式是256色模式(8位)。
  当然,它假设放置到位图内的索引比起整个的RGB值占用较少的位。举例来说,用8位索引而不是16位。其结果是存储器的需求以某种代价减少了, 但是,由于通过8位索引只有256个项可以被寻址,因此,在3万余种可能的强度值的联合中,每个个别的时刻我们只能得到256个不同颜色。空间对灵活性,这通常是个平衡。(参见图1.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/1.1/1-4.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图1.4 显存存储调色板索引</B></FONT></TD></TR></TABLE>
  RGB方案和调色板方案需要应用程序一些不同的处理。我们也注意到既然调色板更喜欢以硬件完成, 则颜色组件的位的大小,同位图格式的不同之处一样,一个平台与另一个平台是不一样的,需要应用程序在把它们存储到调色板之前调整强度。
  尽管在格式和硬件上存在着多样性,交互式3D应用程序显示图像的高级方面多数集中在相当小的例程集内部。举例来说,我们可以选择程序清单1.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/1.1/1-5.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>程序清单1.1 显示设备接口函数原型</B></FONT></TD></TR></TABLE>
  上面第一个函数,HW_init_screen,在某些系统执行的时候是初始化某种屏幕方式,对另一些系统来说可能是分配一个窗口。加入文本字符串display_name是适应一些支持多重显示的结构(比如说象X11那样)。
  HW_blit和HW_close_screen函数的目的正如它们的名字所示:前者复制图像位图到物理设备存储器,后者销毁窗口或恢复原始的屏幕设置。
  对这类接口函数来说,有许多变化和扩展的可能。这些更改依赖于对象的硬件类型。举例来说,如果我们要适应支持基于调色板的格式的硬件,我们或许需要初始化调色板的例程以及也许在运行中实时地选择另一个调色板或者甚至同时保留几种调色板。为了让操作系统支持图形用户界面,我们可以随窗口几何结构改变初始化屏幕函数的参数。一些应用程序可能需要多重窗口,这就需要进一步修改上面提到的例程,例如保留几个离屏缓冲区和以目的窗口的名字作为位块传输函数的变参。然而,构建接口函数集的基本途径是相同的。首先要确定计划中的图形应用程序从硬件中需要什么,然后尝试在一个模块中隔离硬件相关方面,无疑地,对所有的目标平台来说,完成的方式
是不同的。

  <B>1.1.2 事件反应
</B>  一个交互式应用程序通过按一定顺序产生帧同用户进行通讯。用户通讯通过输入设备的动作返回到应用程序。让我们确定一下对应用程序接口来说需要些什么样的交互作用。
  首先要考虑的是3D应用程序工作有什么不同。在一些交互式游戏中,事件在屏幕上不断地发生。无论输入设备是否有动作,游戏者仍然能够看到地面加速接近或者可怕的怪物把他撕成碎片。这就意味着这类应用程序所有的时间都在渲染帧。用户完成输入设备的动作只引起一些描绘3D世界内部变量以及观察场景的参数的改变。这样,世界状态和观察参数的改变导致应用程序在未来渲染不同的帧。
  其它的3D应用程序不包括持续的运动。它们只在响应来自输入设备的时候渲染新帧,其它时刻空闲。举个例子来说,在用计算机辅助设计(CAD)工具构建的可视对象, 按下箭头键会导致应用程序动作和随着改变的观察角度渲染场景,否则程序就处于空闲状态。
  在这两种应用程序中显而易见的不同是,第一种情况下,3D世界的状态能被观察者和应用程序本身改变,导致了持续不断的产生帧。第二种情况,只有观察者能改变状态,只有这种改变发生的时候显示才被刷新。总之,我们需要不断执行代码某些片段的能力,以及响应发生的事件并执行其它一些片段的能力,直到这两种应用程序控制流程关联起来。
  在前面我们已经讨论过不同的操作系统提供给应用程序不同的管理控制流程的能力。基于GUI的系统通常包括事件处理器的专用措施 — 即函数,当一个事件随着某个输入设备发生的时候,这些函数被调用。其它的系统可能没有这种机制,需要程序员直接地查询特定输入设备的状态和根据当前状态执行不同的动作。既然完成方法相当不同,我们可以把操作系统的交互作用统一到某个高级层次,比如说,创建下面的接口函数集:(参见程序清单1.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/1.1/1-6.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>程序清单1.2 控制流程函数原型</B></FONT></TD></TR></TABLE>
<B></B>  第一个接口函数期望指向两个处理函数的指针,这两个函数在应用程序运行期间被执行。其中一个处理函数设计为不断地被执行,另一个只响应外部事件。第二个接口函数被传递给HW_init_event_loop的两个函数调用,用于终止执行。
  然我们再回到两种图形应用程序的描述。当我们不打算在所有的时间中都渲染帧的时候,application_main可能根本不包含任何代码,application_event_handler将会首先响应事件,改变应用程序内部的状态,然后渲染帧。
  HW_init_event_loop可能作为application_main无条件调用的内循环完成,然后检查发生的事件,如果的确有事件等待处理,application_event_handler被调用。这种执行策略的本质解释了为什么函数和其相关方案的名字叫做“事件循环”。上面提到的第二个函数标记退出事件循环,比如说,改变循环状态的变量。
  程序清单1.2中的接口函数集应该伴随着事件分类约定,例如键的枚举 。这一点对应用程序相关的移植性是非常重要的,因为,通常不同的操作系统和硬件平台在键的枚举上有不同的约定。
  当然,上面提到的接口函数是非常小的。它应该根据计划的应用程序以一系列方式被扩展。举例来说,我们可以支持更复杂的事件集,例如鼠标事件或为不同事件类型的多重事件响应处理。如果应用程序保留了多重显示窗口,也许还有必要为每个窗口分配单独的事件处理集,而且,可能的话,提供在不同窗口代码响应之间的通讯程序。
  这些接口函数,同显示设备接口函数一样,为应用程序屏蔽了为图形应用程序概念上的需要而提供功能的特定操作系统和硬件的细节。当然,这些功能的完成,一个操作系统与另一个操作系统是不一样的,在下一节中我们将要看到更深的细节。

<B>1.2、使用不同的体系结构</B>
  在上一部分中我们讨论了关于直接面向操作系统硬件的图形应用程序的方面。我们已经定义了大多数图形应用程序交互作用所必需的类型。注意到接口函数必须依赖于操作系统以不同的方式完成。接下来让我们讨论一下几种已有的操作系统的执行特性。
  我们遇到的不同之处包括:存在或不存在图形用户界面,系统支持的执行流程类型,以及应用程序输入/输出设备访问能力。某些复杂因素还来自于编译器的不同。许多编译器有不同的特性,在某种程度上方便了编程,但不是通用语言规范的一部分。另外通常还有由于对标准解释的一点不同引起的轻微差异。
  或许,在C编译器上最有名的不同是由处理器引起的。基本数据类型的位的大小的不同之处来自于处理器寄存器存储结构。举例来说,一些编译器假定int类型的值必需存为32位,其它的编译器却只分配16位。在许多情况下,精确地知道数据类型中位的大小是非常重要的,我们可以以已知的长度定义几种不同的类型,并特别地依赖手头的编译器来解释。
  在这部分,我们考虑几种不同的操作系统在我们感兴趣的两个方面的一些特性和特征:显示图像和处理外部事件。还要注意到,每种操作系统都有很长的手册。下面讨论的只是基本的概述,并不能给出如何编程的足够的技术信息。

喜欢0 评分0
夜落了,风静了,我喜欢一本书,一杯茶,一粒摇曳的烛光...
游客

返回顶部