热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

转第十八章矩阵数学(as3.0)

本章我们不去介绍一些新的运动、物理学或渲染图形的方法。我要给大家介绍的是矩阵(Matrix),它给我们提供了一个新的可选方案。矩阵在3D系统中3D点的旋转,缩放以及平移(运

本章我们不去介绍一些新的运动、物理学或渲染图形的方法。我要给大家介绍的是矩阵
(Matrix),它给我们提供了一个新的可选方案。
     矩阵在 3D 系统中 3D 点的旋转,缩放以及平移(运动)中使用得非常频繁。在各种 2D
图形的变换上也很常用。您也许可以回想到 beginGradientFill 方法就是使用矩阵来设置位
置,大小以及旋转比例的。
     本章大家将看到如何创建一个 3D 矩阵系统,用以操作 3D 的影片并且可以看到一些
Flash 中内置的矩阵。我很庆幸现在为止还没有一处提到 Keanu Reeves [译注:基努-里维斯,
尤指电影《黑客帝国》-- The Matrix] 的电影。看看我还能坚持多久。
转 <wbr>第十八章 <wbr>矩阵数学 <wbr>(as3.0)


图 18-1 一个 3×3 矩阵,一个 1×3 矩阵,一个 3×1 矩阵
     矩阵通常都是由一些变量来描述的,如 M。在矩阵中为表示一个特殊的单元,我们使
用的变量里面通常要用行列的值作为脚标。例如,如果图 18-1 中的 3×3 矩阵叫做 M,
那么 M2,3 就等于 6,因为它指向第二行,第三列。
     一个矩阵的单元不仅可以包含简单的数字,也可以是公式和变量。其实电子表格就是一
个大的矩阵。 我们可以用一个单元保存某一列的和,用另一个单元格将这个总和乘以一个分
数,等等。我们看到这样的矩阵应该非常有用。
矩阵运算
    一个电子表格就像一个自由组合的矩阵,我们要处理的矩阵更加有结构, 至于能用它们
做什么以及如何生成都有各自的规则。
    我所见过的大多数矩阵数学的教材都只介绍两种方法的一种。首先学校讲的是矩阵运算
的细节,使用的整个矩阵的几乎都是一些随机的数字。我们学习这些规则,但是不知道为什
么要做这些事情或所得的结果代表什么。就像在玩把数字排列成漂亮形状的游戏。
    第二个方法是详细地描述矩阵的内容但是略过手工操作, “将两个矩阵相乘得到这个
 如结果… …”让读者不知道乘法到底是怎么算的。
    为了保证大家都能了解矩阵是如何工作的,我选择一个两者兼具的方法(折衷) ,从介
绍一些数值矩阵开始,然后描述如何做矩阵乘法。
矩阵加法
    矩阵更为通常的作用是操作 3D 点。一个 3D 点包涵了 x, y, z 坐标。我们可以简单地
将它视为一个 1×3 的矩阵:
    xyz
现在假设要将这个点在空间中移动,或叫做点的平移。我们需要知道每个轴上移动多远。这
时可以将它放入一个转换矩阵(translation matrix)中。它又是一个 1×3 的矩阵:
    dx dy dz
这里 dx, dy, dz 是每个轴移动的距离。现在我们要想办法将转换矩阵加到点矩阵上面。这就
是矩阵加法,  非常简单。我们只需要将相应的单元进行相加形成一个新的包含了每个单元之
和的矩阵。很明显,要让两个矩阵相加,它们的大小都应该是相同的。转换方法如下:
    x y z + dx dy dz = (x + dx) (y + dy) (z + dz)
获得的矩阵可以叫做 x1, y1, z1,转换之后包含了该点的新坐标。让我们用实数来试一下。
假设点在 x, y, z 轴上的位置分别为 100, 50, 75,要让它们分别移动 -10, 20, -35。则应该是
这样的:
    100 50 75 + -10 20 -35 = (100 - 10) (50 + 20) (75 - 35)
因此,当进行加法运算时,所得该点的新坐标就是 90, 70, 40。非常简单,不是吗?大家也
许已经注意到了速度间的相互关系,   每个轴上的速度都加到了另一个矩阵的相应位置上。   公
平交易嘛。
    如果我们有一个较大的矩阵,那么继续使用同样的方法,匹配每个单元。我们不会去处
理大于 3×1 的矩阵加法,但是我会给大家这样一个抽象的例子:
    abc               jkl         (a + j) (b + k) (c + l)
    def         +     mno       = (d + m) (e + n) (f + o)
    ghi               pqr         (g + p) (h + q) (i + r)
  以上就是我们需要知道矩阵加法的一切。 在介绍了矩阵乘法之后,我将展示如何将现有的函
数使用在矩阵 3D 引擎中。
矩阵乘法
      在 3D 转换中应用更为广泛的是矩阵乘法(matrix multiplication),常用于缩放与旋转。
在本书中我们实际上不会用到 3D 缩放,因为例子中的点缩放,影片也没有 3D 的“厚度”,
因此只有二维的缩放。当然,大家可以建立一个可缩放整个 3D 立体模型的更为复杂的引
擎。这就需要写一些根据新的影片大小改变 3D 点的函数。这些已经超出了我们讨论的范
围,但是由于缩放是非常简单的,并且使用矩阵乘法很容易实现,因此我将带大家看一下这
个例子。
使用矩阵进行缩放
    首先,需要知道一个物体现有的宽度,高度和深度 —— 换句话讲,它是三个轴上每个
轴分量的大小。当然可以建立一个 3×1 的矩阵:
      whd
我们知道 w, h, d 代表宽度(width),高度(height)和深度(depth)。下面需要缩放这个矩
阵:
      sx  0  0
      0  sy  0
      0   0 sz
这里 sx, sy, sz 是对应轴上的缩放比例。它们都将是分数或小数,1.0 为 100%,2.0 为
200%,0.5 为 50%,等等。稍后大家会看到为什么矩阵是用这种形式分布的。
    要知道,矩阵乘法是为了让两个矩阵相乘,第一个矩阵的列数必需与另一个矩阵的行数
相同。只要符合这个标准,第一个矩阵可以有任意多个行,第二个矩阵可以有任意多个列。
本例中,由于第一个矩阵有三列(w, h, d),因此缩放矩阵就有三行。那么它们如何进行乘
法运算呢?让我们来看一下这个模式:
            sx 0 0
    w h d * 0 sy 0
            0 0 sz
矩阵的计算结果如下:
     (w*sx + h*0 + d*0) (w*0 + h*sy + d*0) (w*0 + h*0 + d*sz)
删除所有等于 0 的数:
     (w*sx) (h*sy) (d*sz)
非常有合乎逻辑,因为我们是用宽度 (x 轴分量) 乘以 x 缩放系数,高度乘以 y 缩放系数,
深度乘以 z 缩放系数。但是,我们究竟在做什么呢?那些所有等于 0 的数都像被遮盖上了,
因此让我们将这个模式抽象得更清晰一点。
        a  b  c
  uvw * d  e  f
        g  h  i

现在可以看到该模式的结果为:
    (u*a + v*d + w*g) (u*b + v*e + w*h) (u*c + v*f + w*i)
我们将第一个矩阵的第一行(u, v, w)与第二个矩阵每行的第一个元素相乘。将它们加起来
就得到了结果的第一行的第一个元素。在第二个矩阵的第二列(b, e, h)中使用相同的方法
就得到了第二列的结果。
    就要在第二行中重复上述动作,就会得到第二行的结果:
    如果第一个矩阵的行数大于 1,
  u v w   a b c
  x y z * d e f
          g h i
就得到了这个 3×2 的矩阵:
    (u*a + v*d + w*g) (u*b + v*e + w*h) (u*c + v*f + w*i)
    (x*a + y*d + z*g) (x*b + y*e + z*h) (x*c + y*f + z*i)
现在让我们看一些实际中用到的矩阵乘法 —— 坐标旋转。希望通过这个缩放的例子会让它
看起来更加清晰。
使用矩阵进行坐标旋转
首先,要挖出我们的 3D 点矩阵:
    x y z
它保存了该点所有的坐标。当然,还要有一个旋转矩阵。我们可以在三个轴的任意一轴上进
行旋转。我们将分别创建每种旋转的矩阵。先从 x 轴旋转矩阵开始:
    1    0      0
    0   cos   sin
    0  -sin   cos
这里有一些正余弦值, “sin 和 cos 是什么?”很明显,这就是我们要旋转的角度的正余弦
值。如果让这个点旋转 45 度,则这两个值就是 45 的正弦和余弦值。 (当然,在代码中要
使用弧度制)现在,我们让该矩阵与一个 3D 点的矩阵相乘,看一下结果。
          1      0     0
  x y z * 0     cos   sin
          0    -sin   cos
由此得到:
    (x*1 + y*0 + z*0) (x*0 + y*cos - z*sin) (x*0 + y*sin + z*cos)
整理后结果如下:
    (x) (y*cos - z*sin) (z*cos + y*sin)
这句话用 ActionScript 大略可以翻译成:
    x = x;
    y = Math.cos(angle) * y - Math.sin(angle) * z;
    z = Math.cos(angle) * z + Math.sin(angle) * y;
回忆一下第十章,在讨论坐标旋转时,我们会看到这实际上就是 x 轴的坐标旋转。不要惊
讶,矩阵数学只是观察和组织各种公式和方程的不同方法。至此,要创建一个 y 轴旋转的
矩阵就非常容易了:
      cos  0  sin
       0   1   0
     -sin  0  cos
最后,z 轴的旋转为:
      cos sin  0
     -sin cos  0
       0   0   1
这是一个很好的尝试,用 x, y, z 的矩阵乘以每个旋转矩阵的单位,证明所得到的结果与第
十章的坐标旋转公式完全相同。
编写矩阵
      OK,现在大家已经有了足够的基础将这些知识转换为代码了。下面,我们对第十五章
的 RotateXY.as 进行重新转换。这个类中有 rotateX 和 rotateY 两个方法, 用以实现 3D 坐
标旋转。我们要让它们以矩阵的方式工作。
      从 rotateX 函数开始。它会用到小球的 x, y, z 坐标,将它们放入 1×3 矩阵,然后创
建一个给定角度的 x 旋转矩阵。这个矩阵将使用数组的形式表示。最后使用 matrixMultiply
函数让两个矩阵相乘,当然还需要创建这个函数!相乘后的矩阵还要用另一个数组进行保存,
因为我们需要将这些数值再存回小球的 x, y, z 坐标中。下面是新版的方法:
private function rotateX(ball:Ball3D, angleX:Number):void {
  var position:Array = [ball.xpos, ball.ypos, ball.zpos];
  var sin:Number = Math.sin(angleX);
  var cos:Number = Math.cos(angleX);
  var xRotMatrix:Array = new Array();
  xRotMatrix[0] = [1, 0, 0];
  xRotMatrix[1] = [0, cos, sin];
  xRotMatrix[2] = [0, -sin, cos];
  var result:Array = matrixMultiply(position, xRotMatrix);
  ball.xpos = result[0];
  ball.ypos = result[1];
  ball.zpos = result[2];
}
下面是矩阵乘法的函数:
private function matrixMultiply(matrixA:Array, matrixB:Array):Array {
  var result:Array = new Array();
  result[0] = matrixA[0] * matrixB[0][0] +
  matrixA[1] * matrixB[1][0] +
  matrixA[2] * matrixB[2][0];
  result[1] = matrixA[0] * matrixB[0][1] +
  matrixA[1] * matrixB[1][1] +
  matrixA[2] * matrixB[2][1];
  result[2] = matrixA[0] * matrixB[0][2] +
  matrixA[1] * matrixB[1][2] +
  matrixA[2] * matrixB[2][2];
  return result;
}
现在,  这个矩阵乘法的函数是手工写出的一个 1×3 和 3×3 矩阵的乘法,这就是我们后面
用在每个例子中的函数。大家也可以使用 for 循环创建出更为动态的可处理任何大小的矩
阵函数,但是现在我要让代码保持简洁。
      最后创建 rotateY 函数。如果你了解 rotateX 函数, 那么这个函数应该非常显而易见了。
只需要创建一个 y 旋转矩阵来代替 x 旋转矩阵即可。
private function rotateY(ball:Ball3D, angleY:Number):void {
  var position:Array = [ball.xpos, ball.ypos, ball.zpos];
  var sin:Number = Math.sin(angleY);
  var cos:Number = Math.cos(angleY);
  var yRotMatrix:Array = new Array();
  yRotMatrix[0] = [ cos, 0, sin];
  yRotMatrix[1] = [ 0, 1, 0];
  yRotMatrix[2] = [-sin, 0, cos];
  var result:Array = matrixMultiply(position, yRotMatrix);
  ball.xpos = result[0];
  ball.ypos = result[1];
  ball.zpos = result[2];
}
就是这样。大家也可以创建一个 rotateZ 函数,由于我们的例子中实际上不需要用到它,所
以我将它作为练习留给大家完成。
      现在,运行一下 RotateXY.as,与第十五章的版本相比,它们看上去实际是一样的。 AS
                                           


推荐阅读
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • Learning to Paint with Model-based Deep Reinforcement Learning
    本文介绍了一种基于模型的深度强化学习方法,通过结合神经渲染器,教机器像人类画家一样进行绘画。该方法能够生成笔画的坐标点、半径、透明度、颜色值等,以生成类似于给定目标图像的绘画。文章还讨论了该方法面临的挑战,包括绘制纹理丰富的图像等。通过对比实验的结果,作者证明了基于模型的深度强化学习方法相对于基于模型的DDPG和模型无关的DDPG方法的优势。该研究对于深度强化学习在绘画领域的应用具有重要意义。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 背景应用安全领域,各类攻击长久以来都危害着互联网上的应用,在web应用安全风险中,各类注入、跨站等攻击仍然占据着较前的位置。WAF(Web应用防火墙)正是为防御和阻断这类攻击而存在 ... [详细]
  • {moduleinfo:{card_count:[{count_phone:1,count:1}],search_count:[{count_phone:4 ... [详细]
  • Flex根据后台的数据自动生成AdvancedDataGrid
    在企业级应用开发中AdvancedDataGrid和DataGrid使用的非常广。当然最基本的也是最常用的就是数据的直接绑定,固定列头和固定数据的列数。而这往往不能满足需求,企业应用 ... [详细]
  • 必要的时候我们需要将过长的字符串截短为较短长度,比如最多显示4个中文字,多余4个字只显示4个字(英文则应该是8个字母)等情况。直接上函数:functiongetCh ... [详细]
  • ImlookingtograbcookievaluesforthesamedomainwithinaFlashmovie.Isthispossible?我想在Fla ... [详细]
author-avatar
有些事想不到
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有