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

3D图形编程指南 6- 视处理

楼主#
更多 发布于:2004-04-27 14:06
<b>目 录
</b>  5.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/5.htm#5.1" target="_blank" ><FONT color=#000000>从世界到屏幕的方法</FONT></A>
   5.1.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/5.htm#5.1.1" target="_blank" ><FONT color=#000000>视系统参数</FONT></A>
   5.1.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/5.htm#5.1.2" target="_blank" ><FONT color=#000000>多边形管道</FONT></A>
   5.1.3 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/5.htm#5.1.3" target="_blank" ><FONT color=#000000>纹理多边形</FONT></A>
  5.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/5.htm#5.2" target="_blank" ><FONT color=#000000>从屏幕到世界的方法</FONT></A>
   5.2.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/5.htm#5.2.1" target="_blank" ><FONT color=#000000>直线方程</FONT></A>
   5.2.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/5.htm#5.2.2" target="_blank" ><FONT color=#000000>平面方程</FONT></A>
   5.2.3 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/5.htm#5.2.3" target="_blank" ><FONT color=#000000>直线和平面的交叉点</FONT></A>
   5.2.4 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/5.htm#5.2.4" target="_blank" ><FONT color=#000000>直线和多边形的交叉点</FONT></A>
   5.2.5 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/5.htm#5.2.5" target="_blank" ><FONT color=#000000>直线和球的交叉点</FONT></A>
   5.2.6 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/5.htm#5.2.6" target="_blank" ><FONT color=#000000>寻找合适的交叉点</FONT></A>
   5.2.7 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/5.htm#5.2.7" target="_blank" ><FONT color=#000000>优化光线追踪</FONT></A>


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

  <B>引言</B>
  有助于构建3D屏幕图象的技术的总和通常指的是“视处理”。以前我们只是假定了某种视图技术,而没有实际地定义。我们假定渲染通过几何图元以及多边形的投影(比如说投影到屏幕上)完成。然而这种方法并不是唯一的。我们通常用“世界到屏幕方法”和“屏幕到世界方法”来区分两种主流视处理方法。图元的投影是世界到屏幕。第二种方法通常叫做光线投射(<I>ray-casting</I>)或光线追踪(<I>ray-tracing</I>)。光线追踪的思想极为不同:对屏幕上的每个像素,我们都计算它在虚拟世界中的表达,直到它同某些表面交叉。在交叉点的表面的颜色就是我们在屏幕上所推测看到的。
  这两种方法都试图模仿自然方式中的视处理现象。在自然中,光源发射粒子束。只要它们被某些对象反射了,则它们的波长组成(颜色)发生了变化。观察者捕捉到一些粒子,并分析它们的颜色,就能够从最后被反射的光子中重新构建对象的图象。尽管,这个处理过程能够表达为一种算法,但这样一来,计算机资源的利用非常低。所有发射的光线中只有一小部分进入观察者的眼睛,剩下的消散在空间中,对产生图象没有任何贡献,因此,对这些的模拟将浪费处理器的时间。
  在计算机图形领域中通常使用的虚拟视处理方法的不同之处在于限制了自然观察方式的复杂性。世界到屏幕方法尝试把视图限制为在场景中可见的一系列对象,并将其作为整体来处理图元的可见性,而不是处理对此有贡献的每一条单个的光线。屏幕到世界的方法则限制了光线的数量。通过从屏幕追溯回去,确保了只考虑从某些光源发出并实际到达了观察者眼睛的那些光线。在本章中我们将考虑这两种方式,以及它们相对的优点和缺点。同时,我们也将分析世界到屏幕的方法对交互式图形应用来说更吸引人的原因,虽然在实现上世界到屏幕的方法比屏幕到世界的方法更复杂。

<B>5.1、从世界到屏幕的方法</B>
  正如在前面已经注意到的,世界到屏幕方法通过从世界到观察者屏幕空间投影图元来创建虚拟世界的图象(参见图5.1)。当考虑组成这个观察世界的变换和处理时, 我们已经分析了在3D世界和观察者摄像机背后的一般思想。


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

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image387.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图5.1 世界到屏幕投影</B></FONT></TD></TR></TABLE>
  3D场景以几何图元、多边形等的形式描述。在场景中的独立对象可能由一定数量的图元组成。既然对象具有在虚拟世界移动的能力这一点可行,就可以方便地使用某些本地坐标系而不是世界坐标系来描述它们的图元。通常说来,本地坐标系在“对象空间”中给出。
  对象假定了在世界空间中某种方位和位置。如果我们知道位置和方位,就可以通过应用某些仿射坐标变换来获得对象在世界空间中的坐标。我们已经在第二章讨论了平移和旋转变换。进一步,一旦有了在世界空间的坐标,我们必须执行投影变换,把这些坐标投影到观察者的屏幕空间中,从而能够开始光栅处理和显示图象。正如我们已经在第二章中看到的,投影变换可以被看作两步走的处理过程。在第一个阶段,以这种方式变换世界空间,这样观察者将假定从坐标原点向Z轴方向看。从这种被变换的世界空间(它被称为“视空间”),很容易应用实际的投影变换并得到在屏幕上的坐标。(参见图5.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/5.1/Image388.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图5.2 对象的变换</B></FONT></TD></TR></TABLE>
  因此,为了显示一些对象的图元,根据对象在世界中的位置和方位,必须执行仿射变换。进一步,取决于观察者的位置和方位,必须应用另一种仿射变换产生在视空间中的坐标。最后,应用投影变换(平行投影或透视投影),就可以得到屏幕空间坐标了。
  在第一步的情况中, 假定对象在世界中的位置以点<I>O</I>(对象空间中的原点映射)描述,对象的方位以三个旋转角<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image389.gif"> — 倾斜((roll)、俯仰(pitch)和偏转(yaw),给定,为了获得世界坐标必须首先以给定的角度应用旋转变换,然后,进一步用在空间中定位对象的点<I>O</I>的坐标应用平移变换:


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image390.gif"></TD></TR></TABLE>
  四个变换矩阵的串联产生了定位(seek)仿射变换。进一步,取决于观察者以及如何把坐标放入视空间,必须执行另一个仿射变换。
  应该注意到,使用三个角度(也称为欧拉角)的方位表达式有些问题。既然这些角以移动轴给定,在一些旋转中,我们可能观察到丢失了自由度的效果,这被称为“<I>gimbal lock</I>(万向节锁定)”。当这种情况发生的时候,角度中的两个突然开始描述同样的旋转。这个问题对用户能交互指定某些对象方位的应用程序来说通常有些复杂。欧拉角对两个指定方位的交叉来说也不是非常好。举例来说,一个动画应用程序的某些对象可能有指定了方位的两个位置,可能需要在给定的两个方位中找出介于中间的那个。如果对欧拉角进行插值,来得到这个中间方位,其结果使运动在视觉效果上不让人满意。当上述问题出现时,四元结构(mechanism of <I>quaternions</I>)可能是一个选择(因为超出了本书的范围,在这里对此不做讨论)。
  尽管点和旋转角可能是一种非常方便地在空间指定对象的方式,但还有许多不同的方式(正如我们在第二章中看到的)用于指定观察者的摄像机。在下一节中,我们将要讨论描述摄像机的各种各样的方式,以及在每种情况中重新得到正确仿射变换的方法。

<B>  5.1.1 视系统参数</B>
  有很多方式可以用来指定视摄像机。为了唯一地描述它,我们需要至少6个不同的参数,对应3D空间中刚体自由度的数目。一个途径是指定观察者所在的点<I><FONT color=#330099>V</FONT></I>的坐标, 以及摄像机方位的旋转角<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image389.gif">。在这种情况下,当我们想获得视空间坐标的时候,必须首先执行把观察者带到世界空间原点的平移变换,紧接着是以顺序<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image391.gif">(偏转、俯仰和倾斜)旋转,这种旋转顺序对世界空间来说更方便。


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image392.gif"></TD></TR></TABLE>
  把这些连接在一起,四个变换矩阵产生了必须的仿射变换。在这一顺序中,在此阶段得到了联合矩阵,以此矩阵描述了从对象空间到世界空间的变换。


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image393.gif"></TD></TR></TABLE>
  作为选择,第二个方法是,摄像机的方位能够通过3个单位向量指定,这三个向量描述了观察方向和矩形屏幕在世界空间中的方位。(参见图 5.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/5.1/Image394.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图5.3 指定摄像机</B></FONT></TD></TR></TABLE>
  我们必须注意到,尽管3个向量包含了9个标量参数,这些参数并不是独立的。我们能够增加6个以上的方程支配其独立性(三个方程指明向量是单位长,三个方程指明向量互相正交)。作为结果,我们只剩下了三个独立的参数用于指定摄像机的方位,这三个参数联合了三个独立的坐标,此三个坐标用于指定观察者的位置,该位置给出了3D空间中刚体的6个自由度。
  在寻找变换中的第一步,对摄像机表度(representation)来说,两个方法是相同的:我们必须应用平移变换将观察者移到世界空间的坐标原点上。对第二步,在这种情况下,我们没有构建旋转矩阵的旋转角。因此,我们不得不以不同的方式来推出这个矩阵。我们必须注意到,旋转是一个线性变换,因此能够以3×3矩阵存储。以这种形式,我们知道,当与其它变换矩阵相乘的时候,必须增加其阶数。如果我们分析得到的信息,我们将会看到,在世界空间中形式为<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image395.gif">的观察方向向量,在视空间中必须映射为<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image396.gif">(回忆一下在此空间中摄像机的方向沿着Z轴);以及<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image397.gif">相应地映射为<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image398.gif">和<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image399.gif">。通过这些事实, 建立方程组不是很困难。


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image400.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image401.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image402.gif"></TD></TR></TABLE>
  这里的<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image403.gif">是一个未知的变换矩阵。上面的矩阵联立在一起,可以被写作:


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image404.gif"></TD></TR></TABLE>
  通过对这个方程求解所有的x,我们得到了要找的变换矩阵, 这个变换矩阵可以把任一点从世界空间中变换到视空间中。
  用于求解类型为<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image405.gif">的低阶矩阵方程的最普通的技术可能是高斯消元法(<I>Gaussian elimination</I>)。通过这种算法,方程以一种方式做变换,系数矩阵<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image406.gif">变成了上三角形式:


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image407.gif"></TD></TR></TABLE>
  这是利用不同的行减去同一常数的倍数而方程的一致性不变的特性完成的。考虑下面这个简单的矩阵方程:


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image408.gif"></TD></TR></TABLE>
  通过从第二行中减去第一行的t倍,我们得到了等价的方程组,其矩阵形式能表示为:


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image409.gif"></TD></TR></TABLE>
  利用这种特性,我们能连续不断地以某种方式在某行中减,这样系数矩阵可以消为三角阵<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image406.gif">。一旦矩阵是上三角阵的形式,就可以简单地得出<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image410.gif">。因为在系数矩阵的最后一行,我们只有一个系数,它被平移为形式为的<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image411.gif">线性方程,由此,我们能推出最后一行的x。然后,我们已经有足够的信息去求解前一个方程。不断重复这个过程,直到所有的x被解出。下面的例程实现了高斯算法。(参见程序清单5.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/5.1/Image412.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>程序清单5.1 使用高斯算法求解线性方程组</B></FONT></TD></TR></TABLE>
  在求解矩阵方程的时候,会遇到一些问题。其中的一些来自数值舍入误差。这个对我们讨论的低阶矩阵不是个大问题。另一些问题的出现是由于方程本身的矛盾。除非我们在构建矩阵方程的时候最初选择的是平行向量,这些问题不应该出现。平行向量转化为线性相关行(等于其它多个行的和或倍数的常量)以及它导致难以找出唯一解。由于线性相关性,当试图在特别位置创建一个0的时候,我们在整个行创建了0。整个行变为0把方程转变为:<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image413.gif">,这个方程有无穷解。
  回到我们原来的问题上,寻找从世界空间到视空间变换的旋转部分,我们现在能够解出以公式表达的矩阵方程,并获得旋转矩阵,旋转矩阵连接在平移矩阵的右侧,这也就是把观察者移到世界空间的原点的仿射矩阵。
  公式化矩阵方程和重新获得变换矩阵的这个方法非常一般化,也能够应用在观察方向不一定与投影平面正交的摄像机上。在特别的情形下,这么做可能能得到更简单的解决方案。
  如果我们知道描述摄像机的所有向量都是单位长,下面的声明为真(当考虑标量积的时候,我们能看到其本质):


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image414.gif">  <IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image415.gif">  <IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image416.gif"></TD></TR></TABLE>
  我们也可以要求所有向量相互正交,在这种情形下,下面的声明为真:


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image417.gif">  <IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image418.gif">  <IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image419.gif"></TD></TR></TABLE>
  如果我们考虑矩阵乘法规则,不难看出,上面的声明描述了在我们构建的方程式中行列乘法的个别情形。注意到必须得出的数值,我们能重新构建矩阵,如下:


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image420.gif"></TD></TR></TABLE>
  正如我们看到的,描述观察者方位的向量的系数应该只被放到矩阵列内,通过列描述必须的变换。
  进一步,当我们知道在视空间内的坐标的时候,应用以矩阵形式表达的投影变换,如果不需要裁剪处理,可以与其它的矩阵连接起来。必须注意到,摄像机的另一个参数,比如说焦距,能够产生部分动态描述,并允许实时变化。
  选择两种方法中的哪一种取决于特定的应用。对移动观察者来说,第一种方法更直观,因为它允许以旋转角度的增减简单地表达在方位上的变换。第二种方法在必须选择精确的观察方向的时候更有用,我们直接地具有指定观察方向的向量。
  我们也必须注意到,视处理有时候使用更一般的公式来表达摄像机,比如说,在Foley, J.D., Van Dam, A., Feiner, S.K.,和 Hughes, J.F.所著的书《<I>Computer Graphics: Principles and Practice</I>》中所描绘的那样。然而,实际应用的多数看起来是为在本节中描述的受限却简单得多的系统而进行选择。
<A>
</A><B>  5.1.2 多边形管道</B>
  多边形是用于使用世界到屏幕方法的对象表度的基本图元。这个图元是最容易进行处理的,尤其在光栅处理阶段。通常情况下,其它图元在该阶段或其它阶段镶嵌到多边形内。我们已经讨论了单个点如何从对象空间变换到屏幕空间。多边形从世界空间到屏幕空间的过程通常叫做“多边形管道(<I>polygonal pipeline</I>)”。尽管存在着由于多重多边形的出现带来的复杂性(某些多边形被其它的遮掩),还是让我们先来集中考虑一个单个的多边形如何遍历所有阶段直到出现在屏幕上。
  我们已经看到,除了变换之外,图元必须经过裁剪阶段。我们必须确保在视体之外没有多余的顶点,如果3D裁剪是近似的,我们必须确保光栅处理例程的所有图元都在屏幕边界之内。为了做到后面这一点,我们要应用2D裁剪算法。
  在前几章,我们已经讨论了在视空间里如何在透视变换前直接执行体裁剪。既然体裁剪的另一个原因是减小屏幕的复杂性,那么它对世界空间的早期裁剪具有潜在的优势。我们将不得不把视体从视空间变换回世界空间应用裁剪,因此可能抛除某些图元,即使在它们被变换到视空间内前。然而在许多情形下,这不实用。对某个物体,我们将不得不沿着平面在空间中内任意导向裁剪,这是相当昂贵的,我们要避免它。而且,如果单个的串联矩阵执行直接从对象空间到视空间的变换,那么在世界空间中的裁剪什么也得不到,因为世界坐标从来不能明确地计算。在某些应用中,比如说,那些绘制室内场景的程序,可能有许多以世界空间形式描述的对象。举例来说,室内的静态元素,例如墙壁或地板,在世界空间中定义。只要移动对象就将在其对象空间中指定对象。在这一类应用中,早期裁剪可能带来潜在的好处,因为一些顶点可能根本不必变换。
  取决于多边形的类型,可能还有额外的工作被完成。我们已经考虑了平面多边形(颜色为常数)、内插明暗处理多边形、线性纹理和最终透视的纹理多边形。对前三个类型没有额外的工作要做,除了每个顶点坐标的不同数值必须被送入管道。对平面多边形来说,只有<I>(x,y,z)</I>坐标。对内插明暗处理多边形来说是<I>(x,y,z,intensity)</I>,对线性纹理的来说是<I>(x,y,z,u,v)</I>,对这两者的组合来说是<I>(x,y,z,intensity,u,v)</I>。
  为了变换的目的,我们必须考虑一下3D空间坐标。另一方面,必须对在顶点上定义的所有值应用裁剪。因为在上面提到的所有值表示了沿着多边形线性变化的参数,把强度或纹理坐标看作空间坐标在裁剪算法中是有效的。在执行多边形扫描变换(scan-conversion)时, 我们已经使用了同样的特性。让我们回想一下,我们已经沿着边内插强度和纹理坐标,以获得多边形左边界或右边界的值,然后再次线性插值,这次是沿着扫描线。
  额外的复杂性来自透视纹理多边形。为了正确地实现透视纹理映射,我们需要使用两个正交单位向量从纹理空间到视空间投影,因为它们要在逆映射方程式中使用。我们在第三章考虑了这种算法。因为透视纹理映射对创建看起来更真实的虚拟世界至关紧要,在下一节中,我们更深入地讨论这个问题的细节。

  <B>5.1.3 纹理多边形</B>
  正如在第三章所讨论的,执行透视纹理映射的算法需要在视空间内多边形平面的方位信息。我们以两个正交单位向量从纹理空间到视空间的映射和纹理空间原点的映射来表度这个信息。
  如果我们以矩形进行处理,恢复纹理映射向量是非常简单的任务。这个巨大的优势来自边的正交属性。(参见图5.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/5.1/Image421.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图5.4 矩形纹理映射向量</B></FONT></TD></TR></TABLE>
  如图5.4所示,我们能以视空间坐标再现纹理映射向量的形式为<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image422.gif">和<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image423.gif">。同样地,点A的坐标可以做为纹理空间原点的映射而起作用。同样的策略也可以用于由两个正交边的三角形。
  然而,如果在多边形中没有正交边的话,问题变得有点复杂。一个策略是把每个多边形描述纹理方向的两个向量和描述纹理空间原点的点集合在一起。从对象空间到视空间中对它们应用变换,结果就得到了正确的映射。然而这个方法,假定对每个多边形存储了冗余的信息。同样的信息能够通过在对象空间和视空间中分析多边形的坐标推演出来。
  让我们回忆一下,对线性纹理多边形来说,我们在每个顶点上保留纹理坐标,这样允许渲染任意形状的多边形。为了裁剪的目的,纹理坐标被看作空间坐标,这样在光栅处理阶段,在每个顶点上,我们都有了正确的纹理坐标,而无论它们是否在裁剪期被创建。尽管这个情形与透视纹理多边形不同,我们还是相应用同样的策略。如果我们在每个顶点上保留纹理坐标,我们可以使用这个信息,利用其在计算纹理映射向量上的优势。
  让我们再考虑一下,为什么矩形在这方面如此的简单?因为矩形的角准确对应纹理的角,但对任意多边形来说则不是这样。(参见图5.5)


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

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image424.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图5.5 多边形和它的纹理</B></FONT></TD></TR></TABLE>
  然而,视空间中任意多边形的顶点都有源于每个顶点的纹理坐标和空间坐标。由此,我们可以通过由顶点坐标建立纹理空间的一些向量来构建这些向量和视空间的通信(参见图5.5)。


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image425.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image426.gif"></TD></TR></TABLE>
  另一方面,我们所要求的是正交纹理空间向量<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image427.gif">和<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image398.gif">的映射,这也可以在不需要复杂计算的矩形的例子中发现。很明显,通过联立上述两个等式,我们得到了一个矩阵等式。求解它,就能得出将纹理空间变换到视空间的变换矩阵<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image428.gif">。


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image429.gif"></TD></TR></TABLE>
  注意到纹理是二维的,上述等式可以变为:


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image430.gif"></TD></TR></TABLE>
  该矩阵等式可以用前一节分析过的高斯消元法求解。一旦该等式被解出,我们就可以得到一个将任何点从纹理空间变换到世界空间的矩阵。既然我们的目的是纹理映射,我们需要得到单位向量的映射,这可以由如下方法获得:


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image431.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image432.gif"></TD></TR></TABLE>
  除这两个以外,我们还需要纹理空间原点<I>O</I>到视空间的映射。尽管我们不是明确地需要它,但我们可以在多边形<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image433.gif">的任何顶点使用该映射,并且可以改变我们在第三章得到的直接映射等式,由此可反映出这么一个事实:我们使用的是任意点,而不是原点:


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image434.gif"></TD></TR></TABLE>
  这样逆映射等式便会稍稍不同:


<TABLE cellSpacing=0 cellPadding=0 width="90%" align=center border=0>

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image435.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/5.1/Image436.gif"></TD></TR></TABLE>
  依赖于在每个顶点上定义的纹理坐标的方法将允许以一种一致的方式来处理线性和透视纹理多边形,只要该方法考虑到数据表示问题。尽管存储在每个顶点的纹理坐标的最终目的不同,它们的存在允许以这两种技术渲染同样的多边形。考虑到线性纹理映射固有的开销比较小,这一点还是很吸引人的。我们只在透视失真不能被忽略的情况下使用透视纹理映射。
喜欢0 评分0
夜落了,风静了,我喜欢一本书,一杯茶,一粒摇曳的烛光...
游客

返回顶部