(左邊的2d圖片如何運動出右邊3d的效果)html
引言:windows
對於這個題目,真的很尷尬,不知道取啥,就想了這個題目,涵蓋範圍很廣,很抽象,算是通用知識點吧。想要了解下面幾個問題的,能夠看看。 函數
①2D圖形如何運動出3D空間的效果。工具
②3D物體如何渲染成2D圖形到屏幕上。測試
③Unity中模型到世界,世界到相機,相機到屏幕的關係。this
④如何經過矩陣進行各類風騷(旋轉,縮放,平移,投影等)的變換操做。spa
①向量.net
②矩陣,矩陣變換規則3d
③透視投影code
問題:圖形不斷變換,經過簡化,圖形的本質是由頂點組合而成,所以,能夠簡化爲頂點不斷變換。不斷意思大概就是每隔一段時間變換,咱們這裏
再簡化,能夠簡化爲變換一次。即問題能夠不斷簡化如圖
對於「頂點變換「,頂點,便是向量,根據矩陣的相關知識,咱們能夠了解到,變換矩陣可使向量獲得指定的變換。所以就是「頂點經過矩陣變換」。
最後,問題的本質即爲頂點和矩陣的之間的交互便可。
頂點,固然是擁有x,y,z三個份量,根據矩陣變換規則,咱們想要使用矩陣對向量進行變換,須要多一個維度,且第四個份量爲1。至於具體緣由這裏不加詳述,
詳情可見:https://blog.csdn.net/zl_gsyy/article/details/73278742。
即,須要設定一個擁有四維向量Vector4.cs
class Vector4 { public double a, b, c, d; public Vector4() { } public Vector4(double a,double b,double c, double d) { this.a = a; this.b = b; this.c = c; this.d = d; } public Vector4(Vector4 v) { this.a = v.a; this.b = v.b; this.c = v.c; this.d = v.d; } }
由於咱們須要變換的是4維向量(頂點,能夠看作1X4的矩陣),因此再根據矩陣變換規則,咱們須要設定矩陣維4x4的。
定義 Matrix4x4.cs
class Matrix4x4 { double[,] ts; public Matrix4x4() { ts = new double[4, 4]; } public Matrix4x4(Matrix4x4 m) { for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { ts[i, j] = m[i, j]; } } } public double this[int x,int y] { get{ return ts[x, y]; } set { ts[x, y] = value; } } /// <summary> /// 用這個矩陣變換一個四維向量,返回另一個向量 /// </summary> /// <param name="v"></param> /// <returns></returns> public Vector4 Mul(Vector4 v) { Vector4 vNew = new Vector4(); vNew.a = ts[0, 0] * v.a + ts[1, 0] * v.b + ts[2, 0] * v.c + ts[3, 0] * v.d; vNew.b = ts[0, 1] * v.a + ts[1, 1] * v.b + ts[2, 1] * v.c + ts[3, 1] * v.d; vNew.c = ts[0, 2] * v.a + ts[1, 2] * v.b + ts[2, 2] * v.c + ts[3, 2] * v.d; vNew.d = ts[0, 3] * v.a + ts[1, 3] * v.b + ts[2, 3] * v.c + ts[3, 3] * v.d; return vNew; } }
根據矩陣變換規則,若是須要變換屢次,屢次變換能夠經過相乘合併爲一個矩陣,好比某頂點須要先平移,再縮放,能夠平移*縮放=合併矩陣。
因此須要在Matrix4x4.cs 中 增長一個矩陣*矩陣獲得另一個矩陣 方法。
public Matrix4x4 Mul(Matrix4x4 m) { Matrix4x4 newM = new Matrix4x4(); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { for (int k = 0; k < 4; k++) { newM[i, j] += this[i, k] * m[k, j]; } } } return newM; }
由於咱們這邊直接使用點來變換顯示,不夠直觀,可使用由3個頂點組成的三角形,來模擬測試效果。因此,這裏再定義一個三角形類Triangles.cs.
直接使用矩陣對該Triangles進行變換,便是對三個頂點進行變換。
class Triangles { public Vector4 A, B, C; private Vector4 a, b, c; //臨時變量使用 public Triangles(Vector4 a,Vector4 b,Vector4 c) { A =this.a= new Vector4(a); B =this.b= new Vector4(b); C =this.c= new Vector4(c); } /// <summary> /// 變換事後原始頂點保留(變換針對原始數據) /// </summary> /// <param name="m"></param> public void Transform(Matrix4x4 m) { this.a = m.Mul(this.A); this.b= m.Mul(this.B); this.c = m.Mul(this.C); } #region 系統內置方法(這邊瞭解一下就行) /// <summary> /// 應用windows窗體應用程序內部方法,根據三個頂點畫出三角形 /// </summary> /// <param name="e"></param> public void Draw(System.Drawing.Graphics e) { e.TranslateTransform(300, 300);//否則會太靠左上角。Windowform的內部功能 e.DrawLines(new Pen(Color.Red, 2f), Get2DPointFArr()); } PointF[] Get2DPointFArr() { PointF[] points = new PointF[4]; points[0] = Get2DPointF(this.a); points[1] = Get2DPointF(this.b); points[2] = Get2DPointF(this.c); points[3] = Get2DPointF(this.a); //咱們這邊須要畫第四個與第一個頂點同樣,系統需求這樣作 return points; } PointF Get2DPointF(Vector4 v) { PointF point = new PointF(); point.X = (float)(v.a / v.d); point.Y = (float)(v.b / v.d); return point; } #endregion }
經過以上,咱們能夠實現經過變換矩陣(平移,縮放,旋轉)來實現簡單變換了。固然經過某種變換的時候首先須要明白對應的哪一個矩陣。參考https://www.cnblogs.com/u3ddjw/p/10282186.html
======================================================正式進入正題=====================================================================
①首先,咱們先聲明一個三角形,而且給出其三個頂點的座標。
Triangles triangles; //注意這邊屏幕左上角爲(0,0)座標原點 Vector4 v1 = new Vector4(0, -0.5, 0, 1); Vector4 v2 = new Vector4(0.5f, 0.5, 0, 1); Vector4 v3 = new Vector4(-0.5f, 0.5f, 0, 1); triangles = new Triangles(v1, v2, v3);
由於咱們這裏給出座標位置是比例座標(模型座標),這種大小固然很小,與屏幕(1320,1080)差距很大,因此咱們須要把它放大道合適的大小。假設就放大250倍恰好是咱們須要的大小。就須要縮放矩陣了,經過https://www.cnblogs.com/u3ddjw/p/10282186.html 查找到縮放矩陣 。
故,我聲明scale =250,
//模型到世界 m_scale = new Matrix4x4(); m_scale[0, 0] = scale; //scale =250,這個數值能夠任意調節,你能夠試試改變這個大小,增強獲得縮放的效果的記憶。 m_scale[1, 1] = scale; m_scale[2, 2] = scale; m_scale[3, 3] = 1; triangles.Transform(m_scale);
最終一張正常的圖出現:
②爲了模擬出直觀的3D效果,再來實現把這個三角形進行旋轉
參考上面方法找到旋轉矩陣表達式,爲了觀察直觀,咱們每隔一小段時間增長2個角度並顯示出來。咱們增長個Timer(就是能夠實現
間隔時間調用方法的工具類,系統通常都會自帶,好比unity中update)
float a = 2; /// <summary> /// 每隔0.02s調用一次 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void timer1_Tick(object sender, EventArgs e) { a++; double angle = a / 360 * Math.PI; m_rotation[0, 0] = Math.Cos(angle); m_rotation[0, 2] = Math.Sin(angle); m_rotation[1, 1] = 1; m_rotation[2, 0] = -1 * Math.Sin(angle); m_rotation[2, 2] = Math.Cos(angle); m_rotation[3, 3] = 1; //由於矩陣變換規則得知屢次變換能夠先將變換矩陣相乘 縮放矩陣*旋轉矩陣 var newTriangle = m_scale.Mul(m_rotation); triangles.Transform(newTriangle); this.Invalidate(); //重繪,必須寫上 }
獲得結果:
上圖老是變寬變宰,下面底部線條沒有感受在3D空間中變換。這不是真正意義3D變換, 僅僅是模型到世界。
③透視投影變換
假設右邊的圈就是咱們須要拍攝的攝像機空間的點,還須要進行透視投影變換到屏幕。故,咱們還須要世界到攝像機,
要讓他從世界到攝像機, 因此咱們還須要架設一個攝像機去拍攝他,假設這個模型在z軸爲0的位置,攝像機假設到z爲負的位置,對於模型而言
在攝像機空間上實際上作了位置平移。因此咱們須要一個能夠轉換爲攝像機空間的矩陣(平移矩陣)。
同上找到平移矩陣,並初始化一個平移矩陣,平移多少位置?仍是先假設250(這邊能夠自行修改數據調節看看效果)。
故在初始化函數中,
//View 世界到相機 m_view = new Matrix4x4(); m_view[0, 0] = 1; m_view[1, 1] = 1; m_view[2, 2] = 1; m_view[3, 2] = 1 * scale; m_view[3, 3] = 1;
目前效果上仍是沒有變換的,雖然平移了攝像機,可是咱們沒有真正的作投影變換。關於投影矩陣,仍是參考https://www.cnblogs.com/u3ddjw/p/10282186.html,
找到投影矩陣。繼續在初始化函數中並實現
//Projection 相機到屏幕 m_projection = new Matrix4x4(); m_projection[0, 0] = 1; m_projection[1, 1] = 1; m_projection[2, 2] = 1; m_projection[2, 3] = 1.0f / scale;
在timer.cs 中的 timer1_Tick 每隔一段時間函數中補充爲
/// <summary> /// 每隔0.02s調用一次 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void timer1_Tick(object sender, EventArgs e) { a+=3; double angle = a / 360 * Math.PI; m_rotation[0, 0] = Math.Cos(angle); m_rotation[0, 2] = Math.Sin(angle); m_rotation[1, 1] = 1; m_rotation[2, 0] = -1 * Math.Sin(angle); m_rotation[2, 2] = Math.Cos(angle); m_rotation[3, 3] = 1; //由於矩陣變換規則得知屢次變換能夠先將變換矩陣相乘 縮放矩陣*旋轉矩陣 var newTriangle = m_scale.Mul(m_rotation); newTriangle = newTriangle.Mul(m_view); newTriangle = newTriangle.Mul(m_projection); triangles.Transform(newTriangle); this.Invalidate(); //重繪,必須寫上 }
這樣,最終咱們看到了第一張圖的效果圖,徹底的3D空間的效果。如今是具備透視變換的三角形在屏幕上不停的旋轉,繞着y軸。
①能夠嘗試改變各個變換矩陣中的值,好比平移矩陣(世界到相機)的值,來看看效果。
private void trackBar1_Scroll(object sender, EventArgs e) { m_view[3, 2] = (sender as TrackBar).Value; } private void trackBar2_Scroll(object sender, EventArgs e) { double value = (sender as TrackBar).Value; m_projection[2, 3] = 1.0f / value; }
②不用三角形,本身嘗試五角星,四邊形之類的變換,效果各類拉風,可是本質仍是都是頂點經過矩陣的變換。