緊接上一篇: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在圖形變換的作用。