http://blog.csdn.net/silangquan/article/details/9970673

提要

在图形的计算中,比如旋转、缩放、平移、投影等操作,矩阵都扮演着极其重要的角色,它是操作图元的基本工具。虽然很多的图形API已经封装好了这些矩阵操作,但是理解这些矩阵操作的原理会非常非常有帮助,比如说我们可以通过一些矩阵的快捷计算来加速你的代码。

如果你有一些线性代数的基础,看下面的内容的时候也不会很轻松,因为有点难且比较没意思,如果没有修过这门课,最好把线性代数这本书拿来看看,因为这些东西真是基础中的基础,而且非常的重要。

齐次记法(Homogeneous Notation)

空间一个点对应的是一个空间的位置,一个向量对应一个方向,两者都可以用一个三维向量 V = (Vx, Vy, Vz)来表示.

这两者如果对于变换(比如旋转,缩放),用一个 3*3 矩阵就可以搞定,但对于平移变换就不适用了,因为位置变换对于向量是没有意义的,而对于点才是有意义的。

齐次记法就是用来解决这个问题的。

所谓齐次记法就是用n+1维矢量表示n维矢量。

在齐次记法下,空间点记为 = (Px, Py, Pz, Pw), 其中Pw = 1,4行x1列。

空间向量记为 = (Vx, Vy, Vz, Vw),其中Vw = 0。 4行x1列。

当出现Pw!=0 且Pw != 1时,就需要将坐标齐次化了,做法是同除以Pw,记为(Px/Pw, Py/Pw, Pz/Pw, 1).

齐次记法下的变换矩阵如下所示:

给定一个移动变换矩阵

对于一个向量  = (Vx, Vy, Vz, Vw)和 相乘之后各值不变(TV)。

对于一个点   = (Px, Py, Pz, Pw)和 相乘之后结果变为 (Px+tx, Py+ty, Pz+tz, 1). (TP

齐次坐标带来的便利:提供了用矩阵运算把二维、三维甚至高维空间中的一个点集从一个坐标系变化到另一个坐标系的有效方法。

基础变换

基础的变换包括平移,旋转,缩放,切变,反射,投影等,下面一个个来看。

平移变换

上面已经提到了,平移矩阵用T来表示:

tx,ty和tz分别表示向x,y,z方向移动的距离,如图

注意这个仿射矩阵(Offine Tranform Matrics)对于空间向量是没有作用的。

其逆矩    ,表示向相反的方向移动。

旋转矩阵 Rotating

旋转变幻是指绕着一个轴旋转一定的角度,绕x,y,z旋转的旋转矩阵可以记为:

逆阵   , 表示绕同一个轴按相反的方向旋转相同的角度。

旋转矩阵的行列式都为1,因为它是正交矩阵。

关于图形(或物体)绕自身的某点旋转,其真实的过程是先将物体移动到旋转点与坐标原点相重合的位置,再将图形绕原点旋转,然后再进行平移变换,平移到原先的位置。

整个矩阵计算过程为   

缩放变换 Scaling

缩放就是放大和缩小,其矩阵表示为

如果 Sx = Sy = Sz,则称为等比变换(uniform),否则就不是(nonuniform)。

其逆阵   ,表示按相反的方式进行缩放。

Sx,Sy,Sz中有一个为负数,则改矩阵就是反射矩阵,如果刚好有两个因子为 -1, 则图形旋转  。反射矩阵通常需要特殊对待,比如,对于一个三角形,经过反射变换,顶点的顺序就可能会改变,这就会影响到面的法线,光照和背面消隐等算法就会受影响。可以通过计算左上角 3*3 矩阵的行列式的值来进行判断,若行列式的值为负,则是反射矩阵。

切变变换 Shearing

切变变换可以用于游戏中,制作出爆炸的时候画面抖动的效果,一共有六种:

第一个下标表示要改变的坐标轴,第二个下标表示沿着那个坐标轴变换。相关的矩阵也可以由此得出:第一个下标决定行,第二个决定列,则有:

效果如下:

其逆阵:

级联变换 Concatenation of Transforms

由于矩阵乘法是没有交换率的,所以矩阵相乘的顺序非常重要,比如 S(2, 0.5, 1)和 , 根据它们执行的顺序不同,得到的结果也会不一样。

将多个矩阵整合到一起的另一个好处是提高了效率,一般的顺序时 TRS。

欧拉变换 Euler TransForm

欧拉变换可以将物体旋转到任意的方向,一个欧拉变换可以分为三个分量 h(ead), p(ich), r(oll),记为E(h,p,r)。

其实就是三个旋转矩阵的级联矩阵:,由于都为对称阵,其逆阵   =  

使用欧拉变换的时候会出现一个很蛋疼的问题-gimbal lock,可以看看这个视频- youtube video explaining gimbal lock

还会出现的一个问题就是两个欧拉角之间的插值问题。

为了避免万圣节锁,一个方法是设定好旋转轴的旋转顺序。

另一中方法是使用四元组。

模型矩阵,视口矩阵和投影矩阵  The Model, View and Projection Matrices

模型是由一系列的顶点构成的,顶点的坐标是相对于模型的中心来定义的,如果某个顶点的坐标值是(0,0,0),就意味着这个顶点在模型的正中间。

现在假设世界坐标在,模型的左边,则模型左边对应于世界坐标需要乘以一个平移矩阵,这个矩阵就是model matrix(模型矩阵)。

人们在操作这个模型的时候,需要对其进行一些变换,就需要将其每个定点移动到原点。

通过下图中黑色的箭头,就是将模型移动到原点。

这个变换矩阵就是model matrix(模型矩阵)

这个过程可以描述为:

接下来是View Matrix(视口矩阵)。

当你站在一座山的前面,想从各个角度来观察这座山的时候,你可以选择跑到不同的位置去看,也可以选择...移动整座山。这在现实生活中看似不行,但在图形学中,这一切都是可行的。

现在在整个世界中只有一个model,当需要观察这个物体的时候,需要一个摄像机来进行观察,假设摄像机初始化在原点,经过一个平移矩阵移动,

  1. glm::mat4 ViewMatrix = glm::translate(Tx, Ty ,Tz);

这个矩阵就是View Matrix(视口矩阵),对应的就是世界坐标远点到摄像机的变换矩阵,过程可以由下图描述。

这里提一下glm中的一个神奇的lookat函数~超强的生成 View Matrics

  1. glm::mat4 CameraMatrix = glm::LookAt(
  2. cameraPosition, // the position of your camera, in world space
  3. cameraTarget,   // where you want to look at, in world space
  4. upVector        // probably glm::vec3(0,1,0), but (0,-1,0) would make you looking upside-down, which can be great too
  5. );

整个阶段的描述如下:

接下来是Projection matrices(投影矩阵)

经过前面的Model Matriix 和 View Matrix的变换,现在处在的就是摄像机空间,也就意味着(0,0)上的点就会出现在屏幕的最中央,但是并不是两个坐标就可以决定顶点是否显示,我们不能忽略 Z 坐标,也就是顶点距离摄像机的位置。

在透视投影中,根据顶点的坐标值,当Vx,Vy的值相同的时候,Vz的值越大,顶点就越在中间,可以参考下图。

一个4*4的矩阵可以用来描述投影:

  1. glm::mat4 projectionMatrix = glm::perspective(
  2. FoV,         // The horizontal Field of View, in degrees : the amount of "zoom". Think "camera lens". Usually between 90° (extra wide) and 30° (quite zoomed in)
  3. 4.0f / 3.0f, // Aspect Ratio. Depends on the size of your window. Notice that 4/3 == 800/600 == 1280/960, sounds familiar ?
  4. 0.1f,        // Near clipping plane. Keep as big as possible, or you'll get precision issues.
  5. 100.0f       // Far clipping plane. Keep as little as possible.
  6. );

通过投影矩阵变换,模型从照相机坐标变为了齐次坐标,过程描述如下:

GLSL实战MVP

我们知道,OpenGL中自带了一些接口函数,可以很方便的定义视口,投影矩阵等,但如果使用GLSL的话,所有顶点的位置都是由 *.vert  中的代码来确定,下面我们就来实践一下刚才学习的Model,View,Projection。

首先是两个简单的Shader:

basic.vert

  1. #version 400
  2. layout(location = 0) in vec3 vertexPosition_modelspace;
  3. // Values that stay constant for the whole mesh.
  4. uniform mat4 MVP;
  5. void main(){
  6. // Output position of the vertex, in clip space : MVP * position
  7. gl_Position =   MVP * vec4(vertexPosition_modelspace,1);
  8. }

就是将模型坐标与MVP矩阵相乘。

basic.frag

  1. #version 400
  2. // Ouput data
  3. out vec3 color;
  4. void main()
  5. {
  6. // Output color = red
  7. color = vec3(1,0,0);
  8. }

接着时初始化shader,vao

  1. void CGL::compileShader()
  2. {
  3. static const GLfloat g_vertex_buffer_data[] = {
  4. -1.0f, -1.0f, 0.0f,
  5. 1.0f, -1.0f, 0.0f,
  6. 0.0f,  1.0f, 0.0f,
  7. };
  8. static const GLushort g_element_buffer_data[] = { 0, 1, 2 };
  9. GLuint vertexbuffer;
  10. glGenBuffers(1, &vertexbuffer);
  11. glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
  12. glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
  13. glEnableVertexAttribArray(0);
  14. glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
  15. glVertexAttribPointer(
  16. 0,                  // attribute. No particular reason for 0, but must match the layout in the shader.
  17. 3,                  // size
  18. GL_FLOAT,           // type
  19. GL_FALSE,           // normalized?
  20. 0,                  // stride
  21. (void*)0            // array buffer offset
  22. );
  23. if( ! prog.compileShaderFromFile("shader/basic.vert",GLSLShader::VERTEX) )
  24. {
  25. printf("Vertex shader failed to compile!\n%s",
  26. prog.log().c_str());
  27. exit(1);
  28. }
  29. if( ! prog.compileShaderFromFile("shader/basic.frag",GLSLShader::FRAGMENT))
  30. {
  31. printf("Fragment shader failed to compile!\n%s",
  32. prog.log().c_str());
  33. exit(1);
  34. }
  35. if( ! prog.link() )
  36. {
  37. printf("Shader program failed to link!\n%s",
  38. prog.log().c_str());
  39. exit(1);
  40. }
  41. if( ! prog.validate() )
  42. {
  43. printf("Program failed to validate!\n%s",
  44. prog.log().c_str());
  45. exit(1);
  46. }
  47. prog.use();
  48. }

然后是初始化Uniform变量:

  1. void CGL::setUniform()
  2. {
  3. // Projection matrix : 45° Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units
  4. glm::mat4 Projection = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
  5. // Camera matrix
  6. glm::mat4 View       = glm::lookAt(
  7. glm::vec3(3,3,3), // Camera is at (4,3,3), in World Space
  8. glm::vec3(0,0,0), // and looks at the origin
  9. glm::vec3(0,1,0)  // Head is up (set to 0,-1,0 to look upside-down)
  10. );
  11. // Model matrix : an identity matrix (model will be at the origin)
  12. glm::mat4 Model      = glm::mat4(1.0f);
  13. // Our ModelViewProjection : multiplication of our 3 matrices
  14. glm::mat4 MVP        = Projection * View * Model; // Remember, matrix multiplication is the other way around
  15. prog.setUniform("MVP",MVP);
  16. prog.setUniform("modelMatrics",Model);
  17. }

渲染一下:

参考

wiki.变换矩阵 - http://zh.wikipedia.org/wiki/%E5%8F%98%E6%8D%A2%E7%9F%A9%E9%98%B5#.E4.BB.BF.E5.B0.84.E5.8F.98.E6.8D.A2

The Matrix and Quaternions FAQ - http://www.cs.princeton.edu/~gewang/projects/darth/stuff/quat_faq.html

Matrices - http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/

Real-Time Rendering 3rd

Fundamentals of Computer Graphics 2rd

Real-Time Rendering (2) - 变换和矩阵(Transforms and Matrics)的更多相关文章

  1. Real-Rime Rendering (2) - 变换和矩阵(Translation and Matrics)

    提要 在图形的计算中,比如旋转.缩放.平移.投影等操作,矩阵都扮演着极其重要的角色,它是操作图元的基本工具.虽然很多的图形API已经封装好了这些矩阵操作,但是理解这些矩阵操作的原理会非常非常有帮助,比 ...

  2. 图形变幻矩阵 Transforms

    https://developer.apple.com/library/mac/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d ...

  3. NYOJ298 点的变换 【矩阵乘法经典】

    任意门:http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=298 点的变换 时间限制:2000 ms  |  内存限制:65535 KB 难度:5 ...

  4. NYOJ 298-点的变换(经典矩阵解决点平移、缩放、翻转和旋转)

    题目地址:NYOJ 298 思路:该题假设用对每一个点模拟的操作.时间复杂度为O(n+m),结果肯定超时.然而利用矩阵乘法能够在O(m)的时间内把全部的操作合并为一个矩阵,然后每一个点与该矩阵相乘能够 ...

  5. OpenGL 的空间变换(上):矩阵在空间几何中的应用

    在使用 OpenGL 的应用程序中,当我们指定了模型的顶点后,顶点依次会变换到不同的 OpenGL 空间中,最后才会被显示到屏幕上.在变换的过程中,通过使用矩阵,我们更高效地来完成这些变换工作. 本篇 ...

  6. OpenCASCADE Coordinate Transforms

    OpenCASCADE Coordinate Transforms eryar@163.com Abstract. The purpose of the OpenGL graphics process ...

  7. transformations 变换集合关系 仿射变换

    http://groups.csail.mit.edu/graphics/classes/6.837/F03/lectures/04_transformations.ppt https://group ...

  8. Matrix4x4矩阵 api

    Matrix4x4 矩阵api介绍 Namespace: UnityEngine Description 描述 A standard 4×4 transformation matrix. 一个标准的4 ...

  9. 【转载】Unity中矩阵的平移、旋转、缩放

    By:克森 简介 在这篇文章中,我们将会学到几个概念:平移矩阵.旋转矩阵.缩放矩阵.在学这几个基本概念的同时,我们会用到 Mesh(网格).数学运算.4x4矩阵的一些简单的操作.但由于克森也是新手,文 ...

随机推荐

  1. octotree神器 For Github and GitLab 火狐插件

    Code tree for GitHub and GitLabExtension to show code tree for GitHub and GitLab. Useful for develop ...

  2. java 断言工具类

    1.断言工具类 package com.sze.redis.util; import java.util.Collection; import java.util.Map; import com.sz ...

  3. 【HackerRank】 Chocolate Feast

    Little Bob loves chocolates, and goes to the store with $N money in his pocket. The price of each ch ...

  4. 【leetcode刷题笔记】Sudoku Solver

    Write a program to solve a Sudoku puzzle by filling the empty cells. Empty cells are indicated by th ...

  5. 【鸟哥的Linux私房菜】笔记3

    正确地开机 最好不要使用root账号登陆!GNOME图形界面 View items as a list X WindowShell 文本交互界面bash是Shell的名称,Linux的默认壳程序就是b ...

  6. Linux基础四---系统监控&硬盘分区

    ---恢复内容开始--- 一系统分区 1.top [参数] -b 批处理 -c 显示命令完全模式 -I 忽略失效过程 -s 保密模式 -S 累积模式 -i<时间> 设置间隔时间 -u< ...

  7. [SCOI2013]火柴棍数字(背包)

    题目 做饭 由于越高位越好,我们先得出能组成的最高位 \(f[i][j][k]\)表示从低到高位第\(i\)位,手里拿着\(j\)根火柴,第\(i\)位是否为\(0\)所需要的最少火柴 我们转移仅需得 ...

  8. Go reflect反射

    Go语言中的反射非常强大,可以对string, int, struct, func...进行反射,使用起来也比较简单. 示例1:反射函数 package main import ( "fmt ...

  9. Adroid真机调试

    几次想学Android,都因为启动模拟器调试时太慢而放弃. 今天终于搞通了真机调试,记录一下: 1)USB线把手机和电脑连接. 2)Adroid手机启用USB调试. 3)命令行运行 adb devic ...

  10. Mybatis-config.xml配置文件详解

    1.官方给出的案列: 注意:这些配置在文件中的顺序非常重要!必须严格按照上图中出现的顺序定义 2.properties属性 该属性主要作用就是引入外部的properties是文件,文件格式为xxx=x ...