2D圖形如何運動模擬出3D效果

一.先看看實現效果圖

        (左邊的2d圖片如何運動出右邊3d的效果)html

                                       

 

  引言:windows

    對於這個題目,真的很尷尬,不知道取啥,就想了這個題目,涵蓋範圍很廣,很抽象,算是通用知識點吧。想要了解下面幾個問題的,能夠看看。   函數

     ①2D圖形如何運動出3D空間的效果。工具

     ②3D物體如何渲染成2D圖形到屏幕上。測試

     ③Unity中模型到世界,世界到相機,相機到屏幕的關係。this

     ④如何經過矩陣進行各類風騷(旋轉,縮放,平移,投影等)的變換操做。spa

二.應用知識

    ①向量.net

    ②矩陣,矩陣變換規則3d

    ③透視投影code

三.實現

      問題:圖形不斷變換,經過簡化,圖形的本質是由頂點組合而成,所以,能夠簡化爲頂點不斷變換。不斷意思大概就是每隔一段時間變換,咱們這裏

再簡化,能夠簡化爲變換一次。即問題能夠不斷簡化如圖

      

      對於「頂點變換「,頂點,便是向量,根據矩陣的相關知識,咱們能夠了解到,變換矩陣可使向量獲得指定的變換。所以就是「頂點經過矩陣變換」。

最後,問題的本質即爲頂點和矩陣的之間的交互便可。

1)頂點

      頂點,固然是擁有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;
        }    
    }

2)矩陣

      由於咱們須要變換的是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)模擬變換過程

   由於咱們這邊直接使用點來變換顯示,不夠直觀,可使用由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

======================================================正式進入正題=====================================================================      

4)如何實現3D空間變換效果

         ①首先,咱們先聲明一個三角形,而且給出其三個頂點的座標。

         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;
        }

 

            ②不用三角形,本身嘗試五角星,四邊形之類的變換,效果各類拉風,可是本質仍是都是頂點經過矩陣的變換

相關文章
相關標籤/搜索