线性代数:矩阵变换图形(三维平移缩放旋转)

转载自: https://blog.csdn.net/yinhun2012/article/details/79640538


这篇博文我只是准备对上一篇博文的内容进行扩展,因为上一篇我写完二维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#外部参数控制脚本
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在图形变换的作用。


  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值