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

3D图形编程指南 -3D变换

楼主#
更多 发布于:2004-04-27 13:52
<b>目 录
</b>  2.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.1" target="_blank" ><FONT color=#000000>欧几里得空间,自由度和基本变换</FONT></A>
  2.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.2" target="_blank" ><FONT color=#000000>平移</FONT></A>
  2.3 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.3" target="_blank" ><FONT color=#000000>缩放</FONT></A>
  2.4 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.4" target="_blank" ><FONT color=#000000>在平面内旋转</FONT></A>
  2.5 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.5" target="_blank" ><FONT color=#000000>3D旋转</FONT></A>
   2.5.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.5.1" target="_blank" ><FONT color=#000000>坐标系</FONT></A>
   2.5.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.5.2" target="_blank" ><FONT color=#000000>变换次序</FONT></A>
  2.6 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.6" target="_blank" ><FONT color=#000000>以矩阵形式表达变换</FONT></A>
  2.7 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.7" target="_blank" ><FONT color=#000000>投影变换</FONT></A>
   2.7.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.7.1" target="_blank" ><FONT color=#000000>平行投影</FONT></A>
   2.7.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.7.2" target="_blank" ><FONT color=#000000>透视投影</FONT></A>
  2.8 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.8" target="_blank" ><FONT color=#000000>通过定点算法实现变换</FONT></A>
   2.8.1 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.8.1" target="_blank" ><FONT color=#000000>整型数表示</FONT></A>
   2.8.2 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.8.2" target="_blank" ><FONT color=#000000>定点数运算</FONT></A>
   2.8.3 <a href="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/2.htm#2.8.3" target="_blank" ><FONT color=#000000>定点算法的实现</FONT></A>


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

  <B>引言
</B>  真实的现实由物质组成。物质可以反射光线,使它们本身可以被人看到。在计算机图形意义上,术语中的虚拟现实并不作为物质而存在。它只是在可视化算法和计算机硬件帮助下的某种抽象实体的解析描述,当人们看上去的时候类似物质和现实。
  因此,在计算机图形学上的第一个挑战就是找到一种解析地描述对象的途径,然后是找到一种数学设备来支持可视化算法。几何学和线性代数的方法都在计算机图形学中使用,典型地,我们使用几何学和线性代数来解决问题。在这一章我们将要主要集中地讨论有关不同坐标变换的数学方法。变换是把一个坐标系中的点映射到另一个坐标系的方法。对象在虚拟世界中的运动和投影到屏幕上要使用不同的坐标变换来计算。为了有效地计算,我们也考虑一些实现变换以及计算方法的技术问题。

<B>2.1、欧几里得空间,自由度和基本变换</B>
  在几何学中,物质的形状在根本上基于空间中一些点的坐标。我们在一个局部范围感知到的周围的世界,在数学上对应于欧几里得空间。
  平面上一个单独的点,在欧几里得空间中,可以明确地用笛卡尔坐标指定:<I>(x,y)</I>。(参见图2.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/2.1/Image45.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图2.1 笛卡尔坐标系的2D平面</B></FONT></TD></TR></TABLE>
  同样地,我们周围的世界(包含了三个维),也能够被欧几里得三维空间模拟。在这个空间中一个单独的点被三个坐标指定:<I>(x,y,z)</I>。
  在随后的几章中,我们还要考虑表示虚拟世界的许多方法。这些方法本质上提供了如何确定所描述的对象上点的(<I>x,y,z</I>)坐标的方法。一些表达方法也许明确地指出对象的关键点,比如说多面体上的顶点。
  为了表达虚拟世界的一些参数,我们可以使用简单的标量值。一个实数或一个整数就是一个标量。举例来说,标量可以被用于表达虚拟场景中所有方向上的恒定的照明度。其它的情况除了处理数量级外,也处理方向。传统的物理学例子是强制应用的材质点。这些实体清楚地包含了方向和数量。同样,在虚拟场景中有方向的光也要处理方向和数量。为了表达这些,我们使用向量。一个向量可以被看作有方向的线段,向量的数量等于线段的长度。很显然,有无穷的等方向、等长度的平行线段可以用同一个向量来表示。但是,在这些线段中,只有一个的起点在坐标系的原点。因此,我们可以用一个这样的线段唯一地描述一个向量。在平面上的向量表示为<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image46.gif">,坐标<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image47.gif">被用于指定向量线段的终点,这个向量被假定起始于坐标系的原点。(参见图2.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/2.1/Image48.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图2.2 决定向量坐标</B></FONT></TD></TR></TABLE>
  图2.2也指出, 当我们给定一些有向线段QP,我们可以通过移动线段的原点到坐标系的起点来推出这个线段表示的向量坐标。换句话说,向量坐标可以通过从线段终点P的坐标中减去线段起点Q的坐标计算出来:


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

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image49.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/2.1/Image50.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/2.1/Image51.gif"></TD></TR></TABLE>
  向量加法可以表达几个向量实体的组合效果。乘以标量能改变向量实体的数量和方向,而方位不变。举例来说,用标量-2去乘一个向量,改变了向量的方向,长度也增加了一倍。图2.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/2.1/Image52.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图2.3 向量运算(乘、和、差)</B></FONT></TD></TR></TABLE>
  其它关于向量的运算也可以同样地定义。举例来说,向量减法可以被定义为:与乘以标量-1的向量的和:(参看图2.3)


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

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image53.gif"></TD></TR></TABLE>
  在后面的几章,我们将要遇到向量的两个其它操作:标量积和向量积(即点乘和叉乘)。
  需要指出的是,标量和向量使我们能够创建一个虚拟世界的描述,对象在这个世界中移动和改变方向。如果约束对象体,使所有的点之间的距离不变,则这种刚体在平面上占有3个自由度,在3D空间占有6个自由度。每个自由度都是一个标量参数,它的改变引起了前面所说的系统状态的改变。举例来说,线段上的一个点在任何状态下只有一个自由度(点的位置),它可以通过一个单独的参数来指定,即线性坐标。对2D空间中的刚体来说,自由度沿着两个坐标轴位移(平移)以及沿着原点旋转。刚体从某个原始位置到任何其它位置的变换可以通过这3个参数变量表示出来。在3D空间中,有三种不同的独立的位移可能。每个都沿着三个坐标轴中的某一个移动以及围绕三个不同的坐标轴旋转,这三个坐标轴都与形成坐标系的坐标轴相平行。
  总的来说,刚体的任何变换都可以表示为两种不同的变换类型:平移和旋转。对大多数计算机图形应用来说,合成的对象要保持形状和顶点坐标不变。在这一类应用中,我们最需要的就是刚体变换。然而,如果对象的形状(点之间的距离)变化了,我们同样要考虑其它的变换。可能最普遍的非刚体变换就是缩放,缩放导致了变换后的对象在大小和形状上的改变。
  谈到所有的3D变换,我们要么把它们作为在静态坐标系中的点集来考虑,要么就作为空间中的变换,换句话说,改变的是坐标系。我们将要看到,对一些坐标变换来说首先要考虑的是方便性,但对另一些则是其次要考虑的。
<B>
2.2、平移</B>
  平移变换,正如我们已经简要地讨论过的,是把对象上的点移到一个新的指定的位置(参看图2.4 (a))。我们也许也要考虑随着坐标系(空间)的移动导致的平移变换。(参图2.4 (b))


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

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image54.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图2.4 点集平移和空间平移</B></FONT></TD></TR></TABLE>
  正如图2.4展示的那样,如果我们移动点集,在这些点之间的距离不会改变。任何在2D空间中的移动都被分割为两个独立的分量:沿着X轴的移动和沿着Y轴的移动。平移变换需要的最重要的一点是在虚拟屏幕坐标系上开始移动的起始点(或者说是原点)对应于物理屏幕的中心,物理屏幕通常有一个参考系,原点位于屏幕左上角。程序清单2.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/2.1/Image55.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>程序清单2.1 平移变换</B></FONT></TD></TR></TABLE>
<P><FONT face=楷体_GB2312>  <FONT color=#993300>注意,程序清单2.1是对一个代表了某些对象上的点集的坐标流进行操作。</FONT></FONT>
<B>
2.3、缩放</B>
  另一个要考虑的重要的变换是缩放。它被定义为在点的距离之间成比例地扩大和缩小。或者,我们也可以把缩放看作空间本身的扩大和缩小。(参看图2.5)
</P>
<TABLE cellSpacing=0 cellPadding=2 align=center border=0>

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image56.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图2.5 缩放变换</B></FONT></TD></TR></TABLE>
  这种变换一个显而易见的用处是把一个A×A维的对象放到B×B大小的窗口中。显然,对象上的每个点为了适应窗口要进行适度的缩放。在这种情况下,缩放是通过乘上一个常量<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image57.gif">完成的。也许,我们也要考虑对窗口的大小进行缩放来装入对象。
  通过缩放变换这种定义,它改变了点之间的距离,因此这不是一种刚体变换。
  总地来说,我们可以为每条坐标轴导入单独的缩放因子,执行所谓的非一致缩放而不是一致缩放,后者在每个方向上的因子都是一样的。在这两种情况中,如果因子大于0或小于1,对象被缩小。如果因子大于1,对象被放大。
  缩放实现的方法同平移例程非常类似。(参看程序清单2.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/2.1/Image58.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>程序清单2.2 缩放变换</B></FONT></TD></TR></TABLE>
<B>2.4、在平面内旋转</B>
  在讨论的三种变换中,旋转有一点复杂。首先让我们考虑一下旋转变换的2D情况。这里面要涉及到三角学,尤其是<I>sin</I>和<I>cos</I>函数。用于直角三角形的sin(<I>x</I>)和cos(<I>x</I>)函数的定义如图2.6所示:

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

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image59.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图2.6 直角三角形</B></FONT></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/2.1/Image61.gif">  <IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image60.gif"></TD></TR></TABLE>
  在了解了sin(<FONT face=Symbol>a</FONT> )和cos(<FONT face=Symbol>a</FONT> )之后, 我们就可以根据已知的<I>l</I>来计算x和y分量。


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

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image62.gif">  <IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image63.gif"></TD></TR></TABLE>
  这可以被看作是在已知与坐标原点距离的时候确定该点在两个正交坐标轴上的投影。(参看图2.7)


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

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image64.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图2.7 点在坐标轴上的投影</B></FONT></TD></TR></TABLE>
  让我们考虑一下图2.8的情形,我们以顺时针把坐标系旋转了<FONT face=Symbol>a</FONT> 角。我们必须找出点A以原有坐标系XY中的坐标<I>(x,y)</I>表示的在变换后的新坐标系<I>X'Y'</I>中的坐标<I>(x',y')</I>。


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

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image65.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图2.8 旋转坐标系</B></FONT></TD></TR></TABLE>
  使用上面提到的sin(x), cos(x)公式,我们能够找出x和y(x和y是原始的参考坐标系中的坐标)在新坐标轴X'和<I>Y'</I>上的投影。把x和y在<I>X'</I>轴上的投影相加,同样把x和y在<I>Y'</I>轴上的投影也相加,这两个和就是我们要找的点A在新坐标系<I>X'Y'</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/2.1/Image66.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image67.gif"></TD></TR></TABLE>
  请注意符号<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image68.gif">,从图2.8中可以简单地看出,在该情况下,x被投影到<I>Y'</I>的负轴上。这就是为什么要加个负号的原因。
  另外,我们也可以这么考虑,把旋转变换看作点A本身的旋转,即沿着原点以顺时针旋转,而参考系固定。

<B>2.5、3D旋转</B>
  在上一节我们已经推出了表示平面旋转变换的公式。在3D空间中,我们可以把上面的公式看作是3D变换的特例,即3D空间中的z坐标不受影响,保持不变。因此,我们就可以用同样的方式推出3D空间中的旋转变换公式。
  如果我们在对象上固定一个点作为坐标系的原点,则对象任何围绕着这个点的方位变化都可以被看作是围绕着以该点为原点的坐标系的坐标轴的旋转。换句话说, 围绕这某个点的3D旋转可以通过按一定次序围绕这3个坐标轴旋转来实现,这样,坐标系每个连续的旋转变化都可以从前一阶段得到。
  在推出3D旋转公式时要先考虑几个影响公式的重要问题:

<UL>
<LI>我们使用哪一种参考系?
<LI>哪个方向是旋转的正方向?
<LI>旋转以什么顺序应用? </LI></UL>  在下面的小节中,我们要推出一系列公式,而且将要看到为什么上面几个问题影响公式的形式?

  <B>2.5.1 坐标系
</B>  一般地,我们不必被参考系的几何体所约束,可以自由地选择形成坐标系的坐标轴的方向。不同的科学和工程上的分支,都有着多一些或少一些的开发方便性可用来定制我们的坐标系。在大多数情况下,我们选择的要么是左手坐标要么就是右手坐标(参看图2.9)。在这本书中,我们大多数时候使用左手坐标系的符号。


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

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image69.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图2.9 右手坐标系和左手坐标系</B></FONT></TD></TR></TABLE>
  但是,如果我们确实不想这么做,那我们也可以不遵循这种习惯,除此之外,应用程序可能因为某个理由选择它自己特别的参考系。按照惯例,比如说,Y轴的正方向指向上。但是,要记住,在大多数典型的位图中,Y轴指向下(这是由显示硬件中的显存布局决定的),这也许是在3D空间中选择相应坐标系的部分原因。也有人说,这是为了在飞行模拟器这类的应用程序中测量高度更自然。自然地选择坐标系可以防止我们在调试程序的时候搞糊涂了,实际上也很节省时间。
  我们在这里选择的坐标系是左手坐标系:Y轴向上,X轴向右,Z轴朝向外(屏幕内)。
  我们还要必须定义旋转角。习惯上把<I>XY</I>面绕<I>Z</I>轴的角<I>(<FONT face=Symbol>a</FONT> )</I>称之为倾斜(<I>roll</I>),ZY绕X轴的角<I>(<FONT face=Symbol>b</FONT> )</I>称之为俯仰(<I>pitch</I>),ZX绕Y轴的角<I>(<FONT face=Symbol>g</FONT> )</I>称之为偏转(<I>yaw</I>)。(参看图2.10)


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

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image70.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图2.10 旋转角的正方向</B></FONT></TD></TR></TABLE><B>
</B><B>  2.5.2 变换次序</B>
  定义旋转变换应用的次序是非常重要的, 因为在空间中的点可能被不一定在同一个位置上<FONT face=Symbol>g</FONT> -<FONT face=Symbol>b</FONT> -<FONT face=Symbol>a</FONT>角度所改变,点被同样的角度以不同的次序改变,产生的效果可能不一样。这就是说:旋转的连续应用不能互换。其原因在于我们的假定,每个下一次变换在点已经被进行了面旋转以后进行。换句话说,我们指定的是围绕着移动了的坐标轴的角度。在图2.11中,我们可以看到<FONT face=Symbol>a</FONT> <I>-<FONT face=Symbol>b</FONT> </I>旋转与<FONT face=Symbol>b</FONT> <I>-<FONT face=Symbol>a</FONT> </I>旋转对对象产生的不同位置效果。


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

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image71.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图2.11 连续旋转</B></FONT></TD></TR></TABLE>
  这个事实告诉我们,必须确定执行哪一种连续的旋转。也就是说,确定2D旋转应用的顺序。让我们思考一下调整周围世界的途径:首先是方向,然后我们可以把头向上或向下,最后从这个位置再左右摆动。当我们抬头或低头的时候,我们已经选定了方向。在我们的参考系中,按照如下的方向旋转:首先是偏转(<I>yaw</I>)<FONT face=Symbol>g</FONT> 角, 然后是俯仰(<I>Pitch</I>)<FONT face=Symbol>b</FONT> 角,最后是倾斜(<I>roll</I>)<FONT face=Symbol>a</FONT> 角。
  顺序就是上面所描述的那样,但是当然,世界中个别的旋转应用取决于观察者的方位改变。换句话说,旋转轴在观察者眼睛的中心。如果我们在世界中规定对象的坐标时,把旋转轴同对象本身联系起来,则连续的旋转顺序经常不相同,俯仰对象取决于对象被倾斜到哪一边,最后才进行偏转。
  当然,组合旋转的公式的出处是相同的。让我们来看看在图2.12中描述的第一种情况。


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

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image72.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图2.12 三个连续的旋转</B></FONT></TD></TR></TABLE>
  在3D旋转中描述了9个公式。我们可以对坐标<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image73.gif">应用公式最后就得到了<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image74.gif">。其中涉及到了12个乘法。一个显而易见的问题是:能不能减少乘法的数量呢?让我们来试试通过去掉临时变量<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image75.gif">来修改一下公式。首先,得到以x,y,z表达的<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image76.gif"><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image77.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/2.1/Image78.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image79.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image80.gif"></TD></TR></TABLE>
  然后,使用上面的表达式,我们可以直接地通过使用x,y,z来表达<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image81.gif"><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image82.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/2.1/Image83.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image84.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image85.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/2.1/Image86.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image87.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image88.gif"></TD></TR></TABLE>
  这个计算有9个乘法,当然,找出所有的因子需要另外16个乘法。


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

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image89.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image90.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image91.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image92.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image93.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image94.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image95.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image96.gif"></TD></TR>
<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image97.gif"></TD></TR></TABLE>
  但是,如果我们在旋转变换中需要遍历100个点,原始的方法需要做<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image98.gif">个乘法。新方法只需<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image99.gif">个乘法,因为进行同样变换的点的变换系数只需被计算一次。
  假定我们已经把乘法的数量减至最少,我们就应该考虑加速表达式中其它的运算:即三角函数的计算。sin(x)和cos(x)可以通过级数近似计算得出。支持浮点运算的现代处理器可能对三角函数的计算有特别的内部支持。即便是没有这些支持,也很容易地通过软件来完成。许多函数都可以表示为泰勒(Taylor)级数的形式:


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

<TR align=middle>
<TD>  <IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image100.gif"></TD></TR></TABLE>
  这个表达式可以为<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image101.gif">找出在<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image102.gif">附近的<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image103.gif">的值。但它需要知道在点<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image104.gif">上的导数值。比如,<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image105.gif">是<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image106.gif">的一阶导数,<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image107.gif">是<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image106.gif">的二阶导数,等等。为任意的<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image108.gif">计算导数是我们要进行的稍微复杂一点的工作。为了避免这个问题,我们把<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image102.gif">的值设为0,这样就得到了麦克劳林(Maclaurin)级数:


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

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image109.gif"></TD></TR></TABLE>
  当<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image110.gif">的时候,<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image111.gif">和<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image112.gif">的值是0或者<FONT face=Symbol>±</FONT> 1,为这种特别的级数获得计算三角函数的表达式并不困难。


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

<TR align=middle>
<TD><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image113.gif">  <IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image114.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/2.1/Image115.gif">  <IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image116.gif"></TD></TR></TABLE>  这种近似没有超出精度要求。计算只在紧靠0的附近进行。参见图2.13,注意<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image117.gif">只在很窄的区间内近似等于<IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image118.gif">。


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

<TR align=middle>
<TD colSpan=3><IMG src="mk:@MSITStore:E:\3D图形编程指南.chm::/3D图形编程指南/image/2.1/Image119.gif"></TD></TR>
<TR align=middle>
<TD colSpan=3><FONT color=#cccccc><B>图2.13 近似的sin(x)</B></FONT></TD></TR></TABLE>
喜欢0 评分0
夜落了,风静了,我喜欢一本书,一杯茶,一粒摇曳的烛光...
游客

返回顶部