1、引言
本篇涉及知識點:
2、內容詳情
平時開發程序,免不了要對圖像做各種變換處理。有的時候變換可能比較複雜,比如平移之後又旋轉,旋轉之後又平移,又縮放藉助矩陣變換和矩陣乘法,可以將多個矩陣合成一個,最後用一個矩陣對每個頂點做一次處理就可以實現想要的效果,十分方便。 另外,矩陣乘法一般有硬件支持,比如3D 圖形加速卡,處理3D變換中的大量矩陣運算,比普通CPU 要快上1000倍。
2.1、2D矩陣轉換
下面是3類基本的2D圖形變換。
2.1.1、2D平移矩陣
設某點向x x x 方向移動 d x d_x d x ,y y y 方向移動 d y d_y d y ,[ x y ] \begin{bmatrix} x& y \\ \end{bmatrix} [ x y ] 爲變換前座標, [ X Y ] \begin{bmatrix} X&Y \\ \end{bmatrix} [ X Y ] 爲變換後坐標。
則 X = x + d x , Y = y + d y X = x+d_x,Y = y+d_y X = x + d x , Y = y + d y ;
以矩陣表示如下 :
[ 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} [ x y 1 ] × ⎣ ⎡ 1 0 d x 0 1 d y 0 0 1 ⎦ ⎤ = [ x × 1 + y × 0 + 1 × d x x × 0 + y × 1 + 1 × d y x × 0 + y × 0 + 1 × 1 ]
= [ x + d x y + d y 1 ] =\begin{bmatrix} x + d_x& y+ d_y&1 \\ \end{bmatrix} = [ x + d x y + d y 1 ]
= [ X Y 1 ] =\begin{bmatrix} X& Y &1 \\ \end{bmatrix} = [ X Y 1 ]
那麼平移轉換矩陣爲 :
[ 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} ⎣ ⎡ 1 0 d x 0 1 d y 0 0 1 ⎦ ⎤
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} [ x y ] 爲變換前座標, [ X Y ] \begin{bmatrix} X&Y\\ \end{bmatrix} [ X Y ] 爲變換後坐標。
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} [ x y ] × [ cos ( α ) − sin ( α ) sin ( α ) cos ( α ) ] = [ x × cos ( α ) − y × sin ( α ) x × sin ( α ) + y × cos ( α ) ] = [ X Y ]
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 s x 倍,y y y 軸方向擴大s y s_y s y 倍,[ x y ] \begin{bmatrix} x& y \\ \end{bmatrix} [ x y ] 爲變換前座標, [ X Y ] \begin{bmatrix} X&Y \\ \end{bmatrix} [ X Y ] 爲變換後坐標。
X = s x × x X=s_x\times x X = s x × x
Y = s y × y Y=s_y\times y Y = s y × 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} [ 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 ]
那麼縮放矩陣爲 :
[ s x 0 0 s y ] \begin{bmatrix} s_x&0 \\ 0&s_y\\ \end{bmatrix} [ s x 0 0 s y ]
2.1.3.1、2D縮放矩陣公式2
那麼沿任意軸S S S 縮放,其中k k k 爲縮放因子,s x s y s_xs_y s x s y 爲縮放分量,縮放矩陣爲 :
[ 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 ) × s x 2 ( k − 1 ) × s x × s y ( k − 1 ) × s x × s y 1 + ( k − 1 ) × s y 2 ]
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} ⎣ ⎢ ⎢ ⎡ 1 0 0 d x 0 1 0 d y 0 0 1 d z 0 0 0 1 ⎦ ⎥ ⎥ ⎤
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} ⎣ ⎡ 1 0 0 0 cos ( α ) − sin ( α ) 0 sin ( α ) 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 ( α ) 0 1 0 sin ( α ) 0 cos ( α ) ⎦ ⎤
沿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 ( α ) 0 sin ( α ) cos ( α ) 0 0 0 1 ⎦ ⎤
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 ) × 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 z × s y ( k − 1 ) × s x × s z ( k − 1 ) × s y × s z 1 + ( k − 1 ) × s z 2 ⎦ ⎤
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 物 體 → 相 機