3D数学--矩阵知识

时间:2022-03-08 16:11:35
本帖最后由 hunter_wwq 于 2013-7-24 14:04 编辑

开此贴是贴出自己在学如题所示知识点时所做的笔记,旨在跟我一样菜的小朋友们共同学习探讨一下这方面的知识,当然,绝对绝对欢迎大牛们来指点指教一下!!!

这是笔记附件:
3D数学--矩阵知识 3D数学-矩阵知识.docx (714.47 KB, 下载次数: 91) 

下面也贴出笔记的内容,图片和格式之类的我就不意义复制粘贴过来了,太麻烦了,等下次把这块知识都弄完了后再把格式改一下!
        任务
        了解矩阵相关的基础知识
        掌握矩阵在3D中的具体应用
        清楚矩阵和逆矩阵各自的应用
        在osg中是如何进行矩阵运算的
1.        http://www.360doc.com/content/11/0906/15/7317486_146206322.shtml
2.        概念:方阵的行列式
>行列式与矩阵变换导致的尺寸改变相关,其中行列式的绝对值与面积(2D)、体积(3D)的改变相关,行列式的符号说明了变换矩阵是否包含镜像或投影。
>矩阵的行列式还能对矩阵所代表的变换进行分类。如果矩阵的行列式为0,那么该矩阵包含投影。如果矩阵的行列式为负,那么该矩阵包含镜像。

有关矩阵的行列式的概念:
>方阵M的行列式记作|M|或“det M”,非方阵矩阵的行列式是未定义的。
非方阵矩阵即行列长度不等的矩阵。
计算矩阵行列式的方法:
>将主对角线和反对角线上的元素各自相乘,然后用主对角线元素的积减去反对角线元素的积。
例:2X2阶矩阵行列式的定义:

2X2阶矩阵行列式计算示意图:

3X3阶矩阵行列式的定义:

3X3阶矩阵行列式计算示意图:

行列式的一些重要性之如下:

3.        概念:逆矩阵
矩阵求逆运算只能用于方阵。
并非所有的矩阵都有逆。
>方阵M的逆,记作M-1,也是一个矩阵。当M与M-1相乘时,结果是单位矩阵。
>奇异矩阵的行列式为0,非奇异矩阵的行列式不为0,所以检测行列式的值是判断 矩阵是否可逆的有效方法。
>对于任意可逆矩阵M,当且仅当v=0时,vM=0。
逆矩阵的重要性质如下:

>矩阵的逆在几何上非常有用,因为它使得我们可以计算变换的”反向“或”相反“变换 ---- 能”撤销“原变换的变换。所以,如果向量v用矩阵M来进行变换,接着用M的逆M-1进行变换,将会得到原向量。这很容易通过代数方法验证:

逆矩阵可撤销之前所做的变换。
4.        概念:余子式、代数余子式、标准伴随矩阵
>余子式是一个矩阵,而代数余子式是一个标量。
>假设矩阵M有r行c列,记法M{ij}表示从M中除去第i行和第j列后剩下的矩阵。显然,该矩阵有r-1行,c-1列,矩阵M{ij}称作M的余子式。
>对方阵M,给定行、列元素的代数余子式等于相应余子式的有符号行列式。
>从矩阵中任意选择一行或一列,对该行或列中的每个元素,都乘以对应的代数余子式。这些乘积的和就是矩阵的行列式。例如,任意选择一行,如行i,行列式的计算过程如公式9.4所示:

例,重写3X3矩阵的行列式:

>M的”标准伴随矩阵“记作”adjM“,定义为M的代数余子式矩阵的转置矩阵。
例3X3阶矩阵:

计算M的代数余子式矩阵:

M的标准伴随矩阵是M的待述余子式矩阵的转置:

>一旦有了标准伴随矩阵,通过除以M的行列式,就能计算矩阵的逆。
>其表示如公式9.7所示:

5.        概念:正交矩阵
>正交矩阵对我们非常有用,因为很容易计算它的逆矩阵。
>很多情况下,我们可以提前知道矩阵是如何建立的,甚至了解矩阵是仅包含旋转、镜像呢,还是二者皆有(记住:旋转和镜像矩阵是正交的)。
>根据定义,当且仅当 M MT = I 时M是正交的。
>(1)当且仅当一个向量是单位向量时,它与自身的点积结果是1。
>(2)当且仅当两个向量是互相垂直时,它们的点积为0。
>所以,若一个矩阵是正交的,它必须满足下列条件:矩阵的每一行都是单位向量,矩阵的所有行互相垂直。
>如果M是正交的,则MT也是正交的。
当矩阵M为正交矩阵时,则该矩阵的逆矩阵为M的转置矩阵。

矩阵正交化:
>构造一组正交基向量(矩阵的行)的标准算法是施密特正交化。它的基本思想是,对每一行,从中减去它平行于已处理过的行的部分,最后得到垂直向量。
例,以3x3矩阵为例,和以前一样,用r1、r2、r3代表3x3阶矩阵M的行。正交向量组r1'、r2'、r3'的计算如公式9.9所示:

>现在r1'、r2'、r3'互相垂直了,它们是一组正交基。当然,它们不一定是单位向量。构造正交矩阵需要使用标准正交基,所以必须标准化这些向量。注意,如果一开始就进行标准化,而不是在第2步中做,就能避免所有除法了。
6.        概念:4D齐次空间
4D坐标的基本思想:
>实际的3D点被认为是在4D中w=1"平面"上。4D点的形式为(x, y, z, w),将4D点投影到这个"平面"上得到相应的实际3D点(x/w, y/w, z/w)。w=0时4D点表示"无限远点",它描述了一个方向而不是一个位置。
>在4D中,仍然可以用矩阵乘法来表达平移,如公式9.10所示,而在3D中是不可能的:

>记住,即使是在4D中,矩阵乘法仍然是线性变换。矩阵乘法不能表达4D中的"平移",4D零向量也将总是被变换成零向量。这个技巧之所以能在3D中 平移点是因为我们实际上是在切变4D空间。与实际3D空间相对应的4D中的"平面"并没有穿过4D中的原点。因此,我们能通过切变4D空间来实现3D中的 平移。

#Q: 何为线性变换?

#Q: 位置矩阵P和转换矩阵T的区别在哪?
#A: 对模型位置做变换,一定是P后乘T。
位置矩阵P:  
转换矩阵T:  
转换矩阵的最后一列一定为 
平移转换矩阵的模板为: 
故对于转换矩阵T,上边3X3部分是旋转/缩放部分,最下一行是平移部分。逆向利用这些信息,能将任意4X4矩阵分解为线性变换部分和平移部分。将平移向量 记做t,将上边3X3部分记做RS,则T可简写为:


>当一个形如[x, y, z, 0]的无穷远点乘以一个包含旋转、缩放等的变换矩阵,将会发生预期的变换。结果仍是一个无穷远点,形式为[x, y, z, 0]。

一个无穷远的点经过包含平移的变换后得到:

其结果和没有平移的变换结果是一样的!
>换句话说,4D向量中的w分量能够"开关" 4x4 矩阵的平移部分。这个现象是非常有用的,因为有些向量代表“位置”,应当平移,而有些向量代表“方向”,如表面的法向量,不应该平移。从几何意义上说,能将第一类数据当作"点",第二类数据当作"向量"。
>使用4x4矩阵的一个原因是4x4变换矩阵能包含平移。当我们仅为这个目的使用4x4矩阵时,矩阵的最后一列总是[0, 0, 0, 1]T。
7.        概念:一般仿射矩阵
通过4X4矩阵我们可以构造包含平移在内的一般仿射矩阵。例如:

8.        概念:透视投影
>正交投影也称作平行投影,因为投影线都是平行的(投影线是指从原空间中的点到投影点的连线)。
>3D中的透视投影仍然是投影到2D平面上,但是投影线不再平行,实际上,它们相交于一点,该点称作投影中心。
如图所示:

>因为投影中心在投影平面前面,投影线到达平面之前已经相交,所以投影平面上的图像是翻转的。当物体远离投影中心时,正交投影仍保持不变,但透视投影变小了。
9.        概念已经理得差不多了,现在来做osg中的矩阵变换
大概有以下几个概念需要理一下:
1)        矩阵与逆矩阵之间的互换
2)        矩阵与逆矩阵在三维中的应用
3)        有关旋转\平移\缩放矩阵
4)        投影矩阵
10.        首先看osg::Matrix类
1)        平移相关方法

  1.                 // 获得平移的向量
  2. inline Vec3d getTrans() const;

  3. // 将矩阵置为一个平移矩阵
  4. void makeTranslate( const Vec3d& v );

  5. // 生成一个平移矩阵,静态函数
  6. inline static osg::Matrix translate( const Vec3d& v );

  7. // 前乘/后乘平移矩阵
  8. inline void preMultTranslate( const Vec3d& v );
  9. inline void postMultTranslate( const Vec3d& v );

  10. // 设置现有矩阵中的平移向量,即[3][0],[3][1],[3][2]
  11. void setTrans( const Vec3d& v );

  12. // 从现有矩阵中分离出平移向量,旋转向量及缩放向量
  13. void decompose( osg::Vec3f& translation,
  14. osg::Quat& rotation,
  15. osg::Vec3f& scale,
  16. osg::Quat& so ) const;
复制代码
2)        透视投影的平截头体相关方法

  1. // 采用平截体设置来创建一个透视投影矩阵
  2. inline static Matrixd frustum(double left,   double right,
  3.                                       double bottom, double top,
  4.                                       double zNear,  double zFar);

  5. // 获取透视投影矩阵的平截体设置
  6. bool getFrustum(double& left,   double& right,
  7.                         double& bottom, double& top,
  8.                         double& zNear,  double& zFar) const;
复制代码
3)        透视投影相关方法

  1. // 获取透视投影矩阵的相关参数
  2. bool getPerspective(double& fovy,  double& aspectRatio,
  3.                             double& zNear, double& zFar) const;

  4. // 将矩阵置为透视投影矩阵
  5. void makePerspective(double fovy,  double aspectRatio,
  6.                              double zNear, double zFar);

  7. // 生成透视投影矩阵,为静态方法
  8. inline static Matrixd perspective(double fovy,  double aspectRatio, double zNear, double zFar);
复制代码
4)        正交投影相关方法

  1. // 获得正交投影的相关参数
  2. bool getOrtho(double& left,   double& right,
  3.                       double& bottom, double& top,
  4.                       double& zNear,  double& zFar) const;

  5. // 将矩阵置为正交投影矩阵
  6. void makeOrtho(double left,   double right,
  7.                        double bottom, double top,
  8.                        double zNear,  double zFar);

  9. /** Set to a 2D orthographic projection.
  10. * See glOrtho2D for further details.
  11. */
  12. inline void makeOrtho2D(double left,   double right,
  13.                                 double bottom, double top)

  14. // 生成一个正交投影矩阵
  15. inline static Matrixd ortho(double left,   double right,
  16.                                     double bottom, double top,
  17.                                     double zNear,  double zFar);
复制代码
5)        视点视向相关方法

  1. // 获取模型观察矩阵的位置和方位
  2. void getLookAt(Vec3f& eye,Vec3f& center,Vec3f& up,
  3.                        value_type lookDistance=1.0f) const;

  4. // 生成一个模型观察矩阵,用作设置相机
  5. // #Q: 模型观察矩阵如何用在相机上?相机有什么方法来接受模型试图矩阵?
  6. #A: osg::Camera类提供了设置观察矩阵的方法,setViewMatrix
  7. inline static Matrixd lookAt(const Vec3d& eye,
  8.                                      const Vec3d& center,
  9.                                      const Vec3d& up);

  10. // 将矩阵置为模型观察矩阵
  11. void makeLookAt(const Vec3d& eye,const Vec3d& center,const Vec3d& up);
复制代码
6)        除转换相关及以上相关外的其他方法

  1. // 生成一个单位矩阵
  2. inline static Matrixd identity( void );

  3. // 求逆矩阵
  4. inline static Matrixd inverse( const Matrixd& matrix);

  5. // 将矩阵置为参数矩阵的逆矩阵
  6. inline bool invert( const Matrixd& rhs);

  7. // 判断是否是单位矩阵
  8. bool isIdentity() const;

  9. // 将矩阵置为单位矩阵
  10. void makeIdentity();

  11. // 矩阵乘法
  12. void mult( const Matrixd&, const Matrixd& );
  13. void preMult( const Matrixd& );
  14. void postMult( const Matrixd& );

  15. // operator function
  16. ……
复制代码
7)        #Q: 有多少东西是可以用矩阵来表示的?还有什么东西是不能用矩阵来表示的?
#A: 正交投影、透视投影、模型观察均可用矩阵来表示;
8)        #Q: 矩阵都用在了什么地方?
看osg::Camera类
11.        看osg::Camera类
1)        跟矩阵相关的方法

  1. // 将本地矩阵转换为世界矩阵,此方法可重载
  2. virtual bool computeLocalToWorldMatrix(Matrix& matrix,NodeVisitor*) const;

  3. // 将世界矩阵转换为本地矩阵,此方法可重载
  4. virtual bool computeWorldToLocalMatrix(Matrix& matrix,NodeVisitor*) const;

  5. // 取得投影矩阵,可对其进行修改
  6. osg::Matrixd& getProjectionMatrix();

  7. // 取得投影矩阵
  8. const osg::Matrixd& getProjectionMatrix() const;

  9. // 取得投影矩阵的正交投影参数
  10. bool getProjectionMatrixAsOrtho(double& left, double& right, double& bottom, double& top, double& zNear, double& zFar) const;

  11. // 取得投影矩阵的平面截体参数
  12. bool getProjectionMatrixAsFrustum(double& left, double& right, double& bottom, double& top, double& zNear, double& zFar) const;

  13. // 取得投影矩阵的透视投影参数
  14. bool getProjectionMatrixAsPerspective(double& fovy,double& aspectRatio, double& zNear, double& zFar) const;

  15. // 设置投影矩阵
  16. inline void setProjectionMatrix(const osg::Matrixd& matrix);

  17. // 以平面截体参数设置投影矩阵
  18. void setProjectionMatrixAsFrustum(double left, double right, double bottom, double top, double zNear, double zFar);

  19. // 以透视投影参数设置投影矩阵
  20. void setProjectionMatrixAsPerspective(double fovy,double aspectRatio, double zNear, double zFar);

  21. // 取得模型观察矩阵,可对模型观察矩阵进行修改
  22. osg::Matrixd& getViewMatrix();

  23. // 取得模型观察矩阵
  24. const osg::Matrixd& getViewMatrix() const;

  25. // 获取模型观察矩阵的逆矩阵
  26. Matrixd getInverseViewMatrix() const;

  27. // 以eye,center,up方式设置模型观察矩阵
  28. void setViewMatrixAsLookAt(const osg::Vec3d& eye,const osg::Vec3d& center,const osg::Vec3d& up);

  29. // 以eye,center,up方式取得模型观察参数
  30. void getViewMatrixAsLookAt(osg::Vec3d& eye,osg::Vec3d& center,osg::Vec3d& up,double lookDistance=1.0) const;

  31. // 设置模型观察矩阵
  32. inline void setViewMatrix(const osg::Matrixd& matrix);
复制代码
由上可知相机中可设置投影矩阵参数和模型观察矩阵参数。
#Q: 那在相机中这两个参数的作用是什么呢?
#A: 在osg::Camera类中这两个参数的定义如下:

  1. osg::Matrixd _projectionMatrix;
  2. osg::Matrixd _viewMatrix;
复制代码
在场景视景器中有一个默认的主属相机,osgViewer::Viewer中。
相机默认带有投影矩阵和模型观察矩阵这两个参数。
#Q: 那么由谁来操作这个相机呢?
看一下相机操作器类osgGA::CameraManipulator
12.        看osgGA::CameraManipulator类
1)        跟操作相机相关的方法有:

  1. /** update the camera for the current frame, typically called by the viewer classes. 
  2. Default implementation simply set the camera view matrix. */
  3. virtual void updateCamera(osg::Camera& camera) { camera.setViewMatrix(getInverseMatrix()); }
复制代码
此类唯一直接操作相机的方法就是updateCamera,设置模型观察矩阵。
可见相机操作器类是通过模型观察矩阵来操作相机的。
接下来看一下此类中有哪些方法跟模型观察矩阵有关。。。
2)        跟模型观察矩阵有关的方法并没有直接见到,从#1)中看到方法updateCamera中有调用getInverseMatrix方法,但是在osgGA::CameraManipulator中getInverseMatrix和getMatrix方法均为纯虚拟方法:

  1. /** get the position of the manipulator as 4x4 Matrix.*/
  2. virtual osg::Matrixd getMatrix() const = 0;

  3. /** get the position of the manipulator as a inverse matrix of the manipulator, typically used as a model view matrix.*/
  4. virtual osg::Matrixd getInverseMatrix() const = 0;
复制代码
如此上两方法的注释可知,均为获取操作器的位置,并以4X4矩阵来表示。而方法getInverseMatrix一般用作模型观察矩阵。
#Q: 为什么纯虚拟方法可以被调用?如updateCamera方法所示。
猜测:在osgGA::CameraManipulator中跟模型观察矩阵相关的参数有:

  1. osg::Vec3d        _homeEye;
  2. osg::Vec3d        _homeCenter;
  3. osg::Vec3d        _homeUp;
复制代码
先看着三个参数在哪些方法中被用到。
三个参数默认赋值为:

  1. _homeEye.set(0.0,-1.0,0.0);
  2. _homeCenter.set(0.0,0.0,0.0);
  3. _homeUp.set(0.0,0.0,1.0);
复制代码
这三个参数都跟home这个关键字有关,那么跟home相关的其他都有:

  1. /** Compute the home position.
  2. 这个计算会考虑相机的视区的大小和模型的尺寸,把相机挪到足够远的位置来使得模型在整个屏幕的区域;如果参数camer为空,则相机到场景区的距离就无法计算,此时将基于模型的尺寸采用一个默认的值
  3. #Q: 但是计算好的值并没有去改变相机的一些属性,而是改变的_homeEye, _homeCenter, _homeUp,那么它是如何做到使相机的位置移动到足够远呢?
  4. #A: 具体的如何去影响估计还是要靠setByMatrix,setByInverseMatrix和getMatrix,getByInverseMatrix方法,因为最终还是要归到updateCamera方法上对camera的设置。
  5. */
  6. virtual void computeHomePosition(const osg::Camera *camera = NULL, bool useBoundingBox = false);

  7. /** Get the manually set home position. */
  8. virtual void getHomePosition(osg::Vec3d& eye, osg::Vec3d& center, osg::Vec3d& up) const
  9. {
  10. eye = _homeEye;
  11. center = _homeCenter;
  12. up = _homeUp;
  13. }

  14. /**
  15. Move the camera to the default position.
  16. May be ignored by manipulators if home functionality is not appropriate.
  17. */
  18. virtual void home(const GUIEventAdapter& ,GUIActionAdapter&) {}

  19. /**
  20. Move the camera to the default position.
  21. This version does not require GUIEventAdapter and GUIActionAdapter so may be
  22. called from somewhere other than a handle() method in GUIEventHandler.  Application
  23. must be aware of implications.
  24. */
  25. virtual void home(double /*currentTime*/) {}

  26. /** Manually set the home position, and set the automatic compute of home position. */
  27. virtual void setHomePosition(const osg::Vec3d& eye, const osg::Vec3d& center, const osg::Vec3d& up, bool autoComputeHomePosition=false)
  28. {
  29. setAutoComputeHomePosition(autoComputeHomePosition);
  30. _homeEye = eye;
  31. _homeCenter = center;
  32. }
复制代码
#Q: _homeCenter, _homeEye, _homeUp具体起到一个什么作用?
#A: 这个也是用来辅助getInverseMatrix方法用的,具体怎么用,还是要靠自己来定义。
#TODO(continue): 可自定义一个漫游器来测试一下。
路径漫游下的相机控制:
http://www.52vr.com/bbs/forum.php?mod=viewthread&tid=23815
osg操纵器解析:
>重写getMartix(),和getInverseMatrix()方法
http://lzchenheng.blog.163.com/b ... 353620106710534514/
>要编写一个好的操纵器,必须首先重载setNode()和home()方法,根据根节点的包围球,确定视点的初始位置,然后,根据视点的初始位置和用户的操作(移动、旋转等操作),重载getInverseMatrix()和getMatrix()方法,构建观察矩阵或物体的位置姿态矩阵,这两个矩阵互为逆矩阵。