線性代數:矩陣變換圖形(三維平移縮放旋轉)

        緊接上一篇:http://blog.csdn.net/yinhun2012/article/details/79544205

        這篇博文我只是準備對上一篇博文的內容進行擴展,因爲上一篇我寫完二維xy仿射座標系的變換,這一篇我就擴充到三維xyz仿射座標系的變換推導。

        前面我們已經理解學習完矩陣在圖形學中的作用,所以這一篇我只做純推導和圖形應用演示。

        1.矩陣操作三維仿射座標系平移,如下圖:

        

        三維仿射空間平移無非就是xyz三軸移動,建立齊次座標和4x4矩陣就能推出來了。

        2.矩陣操作三維仿射座標系縮放,如下圖:

        

            縮放也很簡單,無非就是xyz軸縮放因子abc帶入矩陣方程組計算得出。

            3.矩陣操作三維仿射座標系旋轉。

            三維下的旋轉就會複雜一些,不同於二維座標系旋轉只能繞着那個不存在的Z軸正反旋轉(或者說我們在紙上畫一個XYZ三維仿射座標系,但是Z軸垂直於紙面我們看不到,那麼以XY爲座標軸的二維座標系就只能繞着Z軸旋轉,因爲我們習慣性把旋轉角按逆時針標記(三角函數中規定逆時針旋轉爲正角),這個前面我們討論三角函數說過了,所以順時針旋轉我們也能通過轉換得到逆時針旋轉的θ角度值,那麼也就是說XY二維座標系的旋轉就是繞着Z軸逆時針旋轉),此時三維XYZ座標系的旋轉就變成了XY繞着Z逆時針旋轉,XZ繞着Y逆時針旋轉,YZ繞着X逆時針旋轉,現在我們依次來推導:

            ①XY繞Z軸逆時針旋轉,如下圖:

            

這裏我們依舊是建立3x3矩陣T和已知量來解線性方程組。

            ①XZ繞Y軸逆時針旋轉,這個時候就要注意了,因爲圖形學有左右手座標系之分,簡單來說就是Z軸是向內還是向外的區別,我們可以觀察得到unity的座標系是左手座標系,也就是Z軸向內,如下圖:

            

            那麼我們建立矩陣和已知量的推導就變成如下圖:

            

            ①YZ繞X軸逆時針旋轉,如下圖:

            

        推導比較簡單所以我直接發簡寫了,小夥伴可以自己繪畫推導一下。

        講了這麼多,那麼接下來就進入圖形學程序的測試了,畢竟搞了一堆紙面知識,要是不應用到圖形學程序上,那豈不是“紙上談兵”,如下圖:

        

下面是爲測試圖形變換所寫的cgshader,這裏我解釋一下,仿射座標系是一個抽象概念性質的東西,我們無法直接寫代碼使用matrix變換仿射座標系,但是我們可以變通一下,寫cg代碼控制仿射座標系原點所在的圖形的每個頂點進行變換,這樣同樣達到矩陣變換的目的(注意程序中角度值一般都是使用弧度值進行計算的,在unity中你需要將degree2radian後進行參數傳遞

        

Shader "Unlit/TransformationUnlitShader"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_T_xyz("XYZ_Translation",vector) = (0,0,0,1)
		_S_xyz("XYZ_Scale",vector) = (0,0,0,1)
		_R_xyz("XYZ_Rotate",vector) = (0,0,0,1)
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			vector _T_xyz;  //xyz軸移動量
			vector _S_xyz;  //xyz軸縮放量
			vector _R_xyz;  //xyz軸旋轉量,分量數值爲角度值
			
			v2f vert (appdata v)
			{
				v2f o;
				//構建平移矩陣
				float4x4 _Mat_T = float4x4(1,0,0,_T_xyz.x,
											0,1,0,_T_xyz.y,
											0,0,1,_T_xyz.z,
											0,0,0,1);
				//構建縮放矩陣
				float4x4 _Mat_S = float4x4(_S_xyz.x,0,0,0,
											0,_S_xyz.y,0,0,
											0,0,_S_xyz.z,0,
											0,0,0,1);
				//構建旋轉矩陣

				//x軸旋轉  
				float4x4 _Mat_R_x = float4x4(1, 0, 0, 0,
											0, cos(_R_xyz.x), -sin(_R_xyz.x), 0,
											0, sin(_R_xyz.x), cos(_R_xyz.x), 0,
											0, 0, 0, 1);
				//y軸旋轉  
				float4x4 _Mat_R_y = float4x4(cos(_R_xyz.y), 0, sin(_R_xyz.y), 0,
											0, 1, 0, 0,
											-sin(_R_xyz.y), 0, cos(_R_xyz.y), 0,
											0, 0, 0, 1);
				//z軸旋轉  
				float4x4 _Mat_R_z = float4x4(cos(_R_xyz.z), -sin(_R_xyz.z), 0, 0,
											sin(_R_xyz.z), cos(_R_xyz.z), 0, 0,
											0, 0, 1, 0,
											0, 0, 0, 1);
				//首先我們平移
				float4 vx = mul(_Mat_T,v.vertex);  //mul爲矩陣乘法,vertex爲模型的網格座標點
				//然後我們縮放
				vx = mul(_Mat_S, vx);
				//然後我們旋轉
				vx = mul(_Mat_R_x, vx);
				vx = mul(_Mat_R_y, vx);
				vx = mul(_Mat_R_z, vx);
				//vx = mul(_Mat_R_z,mul(_Mat_R_y,mul(_Mat_R_x,vx)));  //或者直接寫成這種形式
				o.vertex = UnityObjectToClipPos(vx);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 col = tex2D(_MainTex, i.uv);
				return col;
			}
			ENDCG
		}
	}
}


         shader代碼中vertex頂點函數中,構建了平移,縮放,旋轉的矩陣,參數由外部vector傳遞,如下圖:


        然後寫好c#外部參數控制腳本

        

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TransformationCtrl : MonoBehaviour
{

    public Renderer mRender;
    public bool bTranslate;
    public bool bScale;
    public bool bRotate;

    private Material mMat;

    private Vector4 mTranslate;
    private Vector4 mScale;
    private Vector4 mRotate;

    private float mTime;

    void Awake()
    {
        mMat = mRender.material;
    }

    void Start()
    {

    }

    void Update()
    {
        //平移
        if (bTranslate)
        {
            mTime += Time.deltaTime;
            if (mTime < 5.0f)
            {
                mTranslate = new Vector4(1.0f * mTime, 0.5f * mTime, 1.5f * mTime, 1);
                mMat.SetVector("_T_xyz", mTranslate);
            }
            else
            {
                mMat.SetVector("_T_xyz", new Vector4(0, 0, 0, 1));
                mTime = 0.0f;
                bTranslate = false;
            }
        }
        //縮放
        if (bScale)
        {
            mTime += Time.deltaTime;
            if (mTime < 5.0f)
            {
                mScale = new Vector4(4.0f * mTime / 5.0f + 1, 2.0f * mTime / 5.0f + 1, 1.0f * mTime / 5.0f + 1, 1);
                mMat.SetVector("_S_xyz", mScale);
            }
            else
            {
                mMat.SetVector("_S_xyz", new Vector4(1, 1, 1, 1));
                mTime = 0.0f;
                bScale = false;
            }
        }
        //旋轉
        if (bRotate)
        {
            mTime += Time.deltaTime;
            if (mTime < 5.0f)
            {
                mRotate = new Vector4(720.0f * mTime / 5.0f * Mathf.Deg2Rad, 1080.0f * mTime / 5.0f * Mathf.Deg2Rad, 360.0f * mTime / 5.0f * Mathf.Deg2Rad, 1);
                mMat.SetVector("_R_xyz", mRotate);
            }
            else
            {
                mMat.SetVector("_R_xyz", new Vector4(0, 0, 0, 1));
                mTime = 0.0f;
                bRotate = false;
            }
        }
    }
}

        簡單的update動畫,但是形象的演示了matrix用於圖形變換的計算。

        可能有小夥伴目前不懂cgshader,不要急,我們學習完基本數學博客後,立馬就會進入C for Graphic和圖形學理論,這裏我們只是驗證一下matrix在圖形變換的作用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章