目录
1、引言
本篇涉及知识点:
- 2D旋转矩阵
- 3D旋转矩阵
- C#顶点变换demo
2、内容详情
平时开发程序,免不了要对图像做各种变换处理。有的时候变换可能比较复杂,比如平移之后又旋转,旋转之后又平移,又缩放借助矩阵变换和矩阵乘法,可以将多个矩阵合成一个,最后用一个矩阵对每个顶点做一次处理就可以实现想要的效果,十分方便。 另外,矩阵乘法一般有硬件支持,比如3D 图形加速卡,处理3D变换中的大量矩阵运算,比普通CPU 要快上1000倍。
2.1、2D矩阵转换
下面是3类基本的2D图形变换。
2.1.1、2D平移矩阵
设某点向
x
x
x方向移动
d
x
d_x
dx,
y
y
y方向移动
d
y
d_y
dy ,
[
x
y
]
\begin{bmatrix} x& y \\ \end{bmatrix}
[xy]为变换前坐标,
[
X
Y
]
\begin{bmatrix} X&Y \\ \end{bmatrix}
[XY]为变换后坐标。
则
X
=
x
+
d
x
,
Y
=
y
+
d
y
X = x+d_x,Y = y+d_y
X=x+dx,Y=y+dy;
以矩阵表示如下:
[
x
y
1
]
×
[
1
0
0
0
1
0
d
x
d
y
1
]
=
[
x
×
1
+
y
×
0
+
1
×
d
x
x
×
0
+
y
×
1
+
1
×
d
y
x
×
0
+
y
×
0
+
1
×
1
]
\begin{bmatrix} x& y &1 \\ \end{bmatrix}\times\begin{bmatrix} 1&0& 0 \\ 0&1&0\\ d_x&d_y&1 \\ \end{bmatrix} =\begin{bmatrix} x\times1 +y\times0 + 1\times d_x& x\times0+ y\times1+1\times d_y&x\times0+y\times0+1\times1 \\ \end{bmatrix}
[xy1]×⎣⎡10dx01dy001⎦⎤=[x×1+y×0+1×dxx×0+y×1+1×dyx×0+y×0+1×1]
=
[
x
+
d
x
y
+
d
y
1
]
=\begin{bmatrix} x + d_x& y+ d_y&1 \\ \end{bmatrix}
=[x+dxy+dy1]
=
[
X
Y
1
]
=\begin{bmatrix} X& Y &1 \\ \end{bmatrix}
=[XY1]
那么平移转换矩阵为:
[
1
0
0
0
1
0
d
x
d
y
1
]
\begin{bmatrix} 1&0& 0 \\ 0&1&0\\ d_x&d_y&1 \\ \end{bmatrix}
⎣⎡10dx01dy001⎦⎤
2.1.2、2D旋转矩阵
在2D环境中,物体只能绕着某点旋转,因为现在暂时不考虑平移。这里我们进一步限制,使其只绕某点旋转。2D中绕原点的旋转只有一个参数:角度 α \alpha α,它描述了旋转量。通常逆时针经常被认为是正方向,顺时针是负方向。旋转相比平移稍稍复杂。
2.1.2.1、2D旋转矩阵公式
设某点与原点连线和
X
X
X轴夹角为
β
\beta
β度,以原点为圆心,逆时针转过
α
\alpha
α度 , 原点与该点连线长度为R,
[
x
y
]
\begin{bmatrix} x&y\\ \end{bmatrix}
[xy]为变换前坐标,
[
X
Y
]
\begin{bmatrix} X&Y\\ \end{bmatrix}
[XY]为变换后坐标。
x
=
R
×
cos
(
β
)
x=R\times\cos(\beta)
x=R×cos(β)
y
=
R
×
sin
(
β
)
y=R\times\sin(\beta)
y=R×sin(β)
X
=
R
×
cos
(
α
+
β
)
=
R
×
cos
(
α
)
×
cos
(
β
)
−
R
×
sin
(
α
)
×
sin
(
β
)
=
x
×
cos
(
α
)
−
y
×
sin
(
α
)
X=R\times\cos(\alpha+\beta)=R\times\cos(\alpha)\times\cos(\beta)-R\times\sin(\alpha)\times\sin(\beta)=x\times\cos(\alpha)-y\times\sin(\alpha)
X=R×cos(α+β)=R×cos(α)×cos(β)−R×sin(α)×sin(β)=x×cos(α)−y×sin(α)
Y
=
R
×
sin
(
α
+
β
)
=
R
×
sin
(
α
)
×
cos
(
β
)
+
R
×
cos
(
α
)
×
sin
(
β
)
=
x
×
sin
(
α
)
+
y
×
cos
(
α
)
Y=R\times\sin(\alpha+\beta)=R\times\sin(\alpha)\times\cos(\beta)+R\times\cos(\alpha)\times\sin(\beta)=x\times\sin(\alpha)+y\times\cos(\alpha)
Y=R×sin(α+β)=R×sin(α)×cos(β)+R×cos(α)×sin(β)=x×sin(α)+y×cos(α)
用矩阵表示:
[
x
y
]
×
[
cos
(
α
)
sin
(
α
)
−
sin
(
α
)
cos
(
α
)
]
=
[
x
×
cos
(
α
)
−
y
×
sin
(
α
)
x
×
sin
(
α
)
+
y
×
cos
(
α
)
]
=
[
X
Y
]
\begin{bmatrix} x& y \\ \end{bmatrix}\times\begin{bmatrix} \cos(\alpha)& \sin(\alpha) \\ -\sin(\alpha) & \cos(\alpha) \\ \end{bmatrix} =\begin{bmatrix}x\times\cos(\alpha)-y\times\sin(\alpha)& x\times\sin(\alpha)+y\times\cos(\alpha) \\\end{bmatrix}=\begin{bmatrix} X& Y \\ \end{bmatrix}
[xy]×[cos(α)−sin(α)sin(α)cos(α)]=[x×cos(α)−y×sin(α)x×sin(α)+y×cos(α)]=[XY]
2D旋转矩阵的公式:
绕坐标中心(自转)旋转
α
\alpha
α角度:
[
cos
(
α
)
sin
(
α
)
−
sin
(
α
)
cos
(
α
)
]
\begin{bmatrix} \cos(\alpha)& \sin(\alpha) \\ -\sin(\alpha) & \cos(\alpha) \\ \end{bmatrix}
[cos(α)−sin(α)sin(α)cos(α)]
任一点的旋转(自转)就是这个点与上面公式的乘法,即向量的乘法(行向量
×
\times
×公式=旋转后的向量)。
2.1.2.1、C#2D旋转矩阵Demo
开始之前我们现在这里放一个效果图:
任务目标:利用C#创建一个窗体应用程序。来绘制三角形,利用上面的2D旋转矩阵旋转公式来让三角形旋转。
创建一个名为MatrixTransform的C#窗体应用程序,添加三角形类Triangle,
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
namespace MatrixTransform
{
/// <summary>
/// 三角形
/// </summary>
class Triangle
{
PointF A, B, C;
public Triangle(PointF A,PointF B,PointF C)
{
this.A = A;
this.B = B;
this.C = C;
}
/// <summary>
/// 画三角形
/// </summary>
public void Draw(Graphics g)
{
Pen p = new Pen(Color.Red, 2);
g.DrawLine(p,this.A,this.B);//直线AB
g.DrawLine(p, this.B, this.C);//直线BC
g.DrawLine(p, this.C, this.A);//直线CA
}
/// <summary>
/// 旋转
/// </summary>
/// <param name="degree">角度</param>
public void Rotate(int degree)
{
float angle = (float)(degree / 360.0f * Math.PI);//角度制转弧度制,需要注意的是这里的360.0f,如果是360可能无法旋转
//根据旋转公式 点坐标(看成向量)和旋转公式相乘
//A点
float newX = (float)(A.X * Math.Cos(angle) - A.Y * Math.Sin(angle));
float newY = (float)(A.X * Math.Sin(angle) + A.Y * Math.Cos(angle));
A.X = newX;
A.Y = newY;
//B点
newX = (float)(B.X * Math.Cos(angle) - B.Y * Math.Sin(angle));
newY = (float)(B.X * Math.Sin(angle) + B.Y * Math.Cos(angle));
B.X = newX;
B.Y = newY;
//C点
newX = (float)(C.X * Math.Cos(angle) - C.Y * Math.Sin(angle));
newY = (float)(C.X * Math.Sin(angle) + C.Y * Math.Cos(angle));
C.X = newX;
C.Y = newY;
}
}
}
添加一个定时器用于刷新三角形,注意设置定时器启用,并设置50ms调用一次。
在窗体中添加以下方法:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MatrixTransform
{
public partial class Form1 : Form
{
Triangle t;
public Form1()
{
InitializeComponent();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.TranslateTransform(300, 300);
t.Draw(e.Graphics);
}
private void Form1_Load(object sender, EventArgs e)
{
Point A = new Point(0,-200);
Point B = new Point(200, 200);
Point C = new Point(-200, 200);
t = new Triangle(A, B, C);
}
//事件
private void timer1_Tick(object sender, EventArgs e)
{
t.Rotate(2);
this.Invalidate();//设置时间无效已重新执行该事件,来从新绘制
}
}
}
然后运行项目,发现三角形开始旋转了。如果觉得三角形有闪烁的感觉可以打开帧缓冲(double buffered)。
2.1.3、2D缩放矩阵
2.1.3.1、2D缩放矩阵公式1
设某点坐标,在
x
x
x轴方向扩大
s
x
s_x
sx倍,
y
y
y轴方向扩大
s
y
s_y
sy倍,
[
x
y
]
\begin{bmatrix} x& y \\ \end{bmatrix}
[xy]为变换前坐标,
[
X
Y
]
\begin{bmatrix} X&Y \\ \end{bmatrix}
[XY]为变换后坐标。
X
=
s
x
×
x
X=s_x\times x
X=sx×x
Y
=
s
y
×
y
Y=s_y\times y
Y=sy×y
用矩阵表示:
[
x
y
]
×
[
s
x
0
0
s
y
]
=
[
x
×
s
x
+
y
×
0
x
×
0
+
y
×
s
y
]
=
[
x
×
s
x
y
×
s
y
]
=
[
X
Y
1
]
\begin{bmatrix} x& y\\ \end{bmatrix}\times\begin{bmatrix} s_x&0 \\ 0&s_y\\ \end{bmatrix}=\begin{bmatrix} x\times s_x +y\times0& x\times0+ y\times s_y \\ \end{bmatrix}=\begin{bmatrix} x\times s_x & y\times s_y \\ \end{bmatrix}=\begin{bmatrix} X& Y &1 \\ \end{bmatrix}
[xy]×[sx00sy]=[x×sx+y×0x×0+y×sy]=[x×sxy×sy]=[XY1]
那么缩放矩阵为:
[
s
x
0
0
s
y
]
\begin{bmatrix} s_x&0 \\ 0&s_y\\ \end{bmatrix}
[sx00sy]
2.1.3.2、2D缩放矩阵公式2
那么沿任意轴
S
S
S缩放,其中
k
k
k为缩放因子,
s
x
s
y
s_xs_y
sxsy为缩放分量,缩放矩阵为:
[
1
+
(
k
−
1
)
×
s
x
2
(
k
−
1
)
×
s
x
×
s
y
(
k
−
1
)
×
s
x
×
s
y
1
+
(
k
−
1
)
×
s
y
2
]
\begin{bmatrix} 1+(k-1)\times s_x^2&(k-1)\times s_x\times s_y \\ (k-1)\times s_x\times s_y &1+(k-1)\times s_y^2\\ \end{bmatrix}
[1+(k−1)×sx2(k−1)×sx×sy(k−1)×sx×sy1+(k−1)×sy2]
2D基本的模型视图变换,就只有上面这3种,所有的复杂2D模型视图变换,都可以分解成上述3个。
比如某个变换,先经过平移,对应平移矩阵A, 再旋转, 对应旋转矩阵B,再经过缩放,对应缩放矩阵C.
则最终变换矩阵 T = ABC. 即3个矩阵按变换先后顺序依次相乘(矩阵乘法不满足交换律,因此先后顺序一定要讲究)。
2.2、3D矩阵转换
2.2.1、3D平移矩阵
那么平移转换矩阵为:
[
1
0
0
0
0
1
0
0
0
0
1
0
d
x
d
y
d
z
1
]
\begin{bmatrix} 1&0& 0&0 \\ 0&1&0&0\\0&0&1&0\\ d_x&d_y&d_z&1 \\ \end{bmatrix}
⎣⎢⎢⎡100dx010dy001dz0001⎦⎥⎥⎤
2.2.2、3D旋转矩阵
沿
x
x
x轴旋转:
[
1
0
0
0
cos
(
α
)
sin
(
α
)
0
−
sin
(
α
)
cos
(
α
)
]
\begin{bmatrix}1&0& 0 \\ 0&\cos(\alpha)& \sin(\alpha) \\0& -\sin(\alpha) & \cos(\alpha) \\ \end{bmatrix}
⎣⎡1000cos(α)−sin(α)0sin(α)cos(α)⎦⎤
沿
y
y
y轴旋转:
[
cos
(
α
)
0
sin
(
α
)
0
1
0
−
sin
(
α
)
0
cos
(
α
)
]
\begin{bmatrix}\cos(\alpha)&0& \sin(\alpha)\\ 0&1&0\\-\sin(\alpha)& 0& \cos(\alpha) \\ \end{bmatrix}
⎣⎡cos(α)0−sin(α)010sin(α)0cos(α)⎦⎤
沿
z
z
z轴旋转:
[
cos
(
α
)
sin
(
α
)
0
−
sin
(
α
)
cos
(
α
)
0
0
0
1
]
\begin{bmatrix} \cos(\alpha)& \sin(\alpha)&0 \\ -\sin(\alpha) & \cos(\alpha) &0\\ 0&0 &1\\ \end{bmatrix}
⎣⎡cos(α)−sin(α)0sin(α)cos(α)0001⎦⎤
2.2.3、3D缩放矩阵
沿任意轴
s
s
s轴缩放,
k
k
k为缩放因子:
N
(
s
,
k
)
=
[
p
′
q
′
r
′
]
=
[
1
+
(
k
−
1
)
×
s
x
2
(
k
−
1
)
×
s
x
×
s
y
(
k
−
1
)
×
s
x
×
s
z
(
k
−
1
)
×
s
x
×
s
y
1
+
(
k
−
1
)
×
s
y
2
(
k
−
1
)
×
s
y
×
s
z
(
k
−
1
)
×
s
x
×
s
z
(
k
−
1
)
×
s
z
×
s
y
1
+
(
k
−
1
)
×
s
z
2
]
N(s,k)=\begin{bmatrix} p' \\q' \\ r'\\ \end{bmatrix}=\begin{bmatrix}1+(k-1)\times s_x^2&(k-1)\times s_x\times s_y & (k-1)\times s_x\times s_z \\ (k-1)\times s_x\times s_y &1+(k-1)\times s_y^2&(k-1)\times s_y\times s_z \\(k-1)\times s_x\times s_z&(k-1)\times s_z\times s_y &1+(k-1)\times s_z^2 \\ \end{bmatrix}
N(s,k)=⎣⎡p′q′r′⎦⎤=⎣⎡1+(k−1)×sx2(k−1)×sx×sy(k−1)×sx×sz(k−1)×sx×sy1+(k−1)×sy2(k−1)×sz×sy(k−1)×sx×sz(k−1)×sy×sz1+(k−1)×sz2⎦⎤
2.2.4、3D透视投影矩阵
透视投影矩阵:
[
x
y
z
1
]
×
[
1
0
0
0
0
1
0
0
0
0
1
1
/
d
0
0
0
0
]
=
[
x
y
z
z
/
d
]
\begin{bmatrix} x& y&z&1\\ \end{bmatrix}\times\begin{bmatrix} 1&0&0&0 \\ 0&1&0&0\\ 0&0&1&1/d\\ 0&0&0&0\\ \end{bmatrix}=\begin{bmatrix} x& y&z &z/d \\ \end{bmatrix}
[xyz1]×⎣⎢⎢⎡100001000010001/d0⎦⎥⎥⎤=[xyzz/d]
$$$$
2.3、变换的种类
2.3.1、变换的种类
2.3.2、变换的组合
其中 P P P表示点, M M M表示矩阵:
P 世 界 = P 物 体 × M 物 体 → 世 界 P_{世界} =P_{物体}\times M_{物体\rightarrow世界} P世界=P物体×M物体→世界
P
相
机
=
P
世
界
×
M
世
界
→
相
机
P_{相机}=P_{世界}\times M_{世界\rightarrow相机}
P相机=P世界×M世界→相机
=
(
P
物
体
×
M
物
体
→
世
界
)
×
M
世
界
→
相
机
=(P_{物体}\times M_{物体\rightarrow世界})\times M_{世界\rightarrow相机}
=(P物体×M物体→世界)×M世界→相机
由数学知识:矩阵满足乘法的结合律
P
相
机
=
(
P
物
体
×
M
物
体
→
世
界
)
×
M
世
界
→
相
机
P_{相机}=(P_{物体}\times M_{物体\rightarrow世界})\times M_{世界\rightarrow相机}
P相机=(P物体×M物体→世界)×M世界→相机
=
(
P
物
体
×
M
物
体
→
世
界
)
×
M
世
界
→
相
机
=(P_{物体}\times M_{物体\rightarrow世界})\times M_{世界\rightarrow相机}
=(P物体×M物体→世界)×M世界→相机
而:
M 物 体 → 相 机 = M 物 体 → 世 界 × M 世 界 → 相 机 M_{物体\rightarrow相机}=M_{物体\rightarrow世界}\times M_{世界\rightarrow相机} M物体→相机=M物体→世界×M世界→相机
P 相 机 = P 物 体 × M 物 体 → 相 机 P_{相机}=P_{物体}\times M_{物体\rightarrow相机} P相机=P物体×M物体→相机