繞座標軸以及任意軸的旋轉矩陣的推導

概述

本文主要是針對《3D數學基礎-圖形與遊戲開發》這本書的讀書筆記,這本書前面部分還是講得挺好的,有時間還是建議讀一下。

旋轉矩陣的推導

旋轉矩陣怎麼來的我倒一直都沒有概念,這本書裏面對旋轉矩陣的來歷倒是給了我一些啓發。
首先從二維的旋轉矩陣開始

[cosθsinθsinθcosθ] \begin{bmatrix} cos\theta & sin\theta \\ -sin\theta & cos\theta \\ \end{bmatrix}
推導的方式就是直接找點代入:
想象點(1,0),繞原點逆時針旋轉θ\theta,我們把(1,0)和旋轉矩陣相乘,得到的就是旋轉矩陣第一行的信息,那顯然,第一行就是cosθ,sinθcos\theta,sin\theta,同理,代入(0,1)到旋轉矩陣也能得到旋轉矩陣第二行的值。
在這裏插入圖片描述
不得不承認,這個pdf裏面截的圖要把人看瞎了,但是問題不大,大概是什麼意思還是能明白的。
對於三維旋轉繞座標軸旋轉的矩陣來說,推導也是同理的,代入相關的點的座標即可,圖書裏面有,但是pdf特別糊,就不截了。也可以理解爲三維座標系投影到二維中,然後運用二維的旋轉矩陣公式。
下面就是繞x軸的旋轉矩陣,可以理解爲在YOZ平面進行旋轉,這樣的話直接代入上面的二維的公式也行。
[1000cosθsinθ0sinθcosθ] \begin{bmatrix} 1& 0&0\\0&cos\theta & sin\theta \\0& -sin\theta & cos\theta \\ \end{bmatrix}
對於繞y軸和z軸的矩陣的由來就不再贅述了。

繞任意軸的旋轉矩陣

這個推導看起來還是很複雜的,爲了後面四元數部分的理解,這個推導還是有必要看一下的!
我們假設旋轉軸通過原點,用單位向量n來描述這個旋轉軸,用θ\theta來描述旋轉的角度。我們只需要用n和θ\theta就可以推導出來最後的旋轉矩陣。
實際的推導我們只需要通過書裏面一張圖就很容易去理解了
電子版的圖太糊了,我畫圖簡單的山寨了一份。
在這裏插入圖片描述
交代一下背景,v繞着n旋轉θ\theta
v∥是v在n上的投影,v∥=(v* n)n,v* n等於投影長度,乘上方向的單位矩陣n,也就得到了最後的v∥
v⊥是垂直於v∥的分量,有v⊥=v-v∥ = v-(v* n)n
w是垂直於v∥和v⊥的分量,長度等於v⊥,w=n x v⊥
然後書上就來了v⊥’=v⊥cosθ+wsinθ,這個一開始我還是不是很理解的,後來我把旋轉的那個投影到平面上,也就是如下圖所示,因爲是旋轉,所以v⊥’的長度一定是等於v⊥的,當然也等於w,所以如下圖所示,投影到兩個座標軸上面,於是就有了上面的公式。
在這裏插入圖片描述
在知道了v⊥’的情況下,最後的v’值就是v∥+v⊥’
代入所有的值,就有v’ = (v-(v* n)n)cosθ+(nxv)sinθ+(v* n)n
那麼我們知道了座標變換,該如何去得到變換矩陣呢,這又回到了我們前面提到的思路,分別代入[1,0,0],[0,1,0],[0,0,1]點到變換公式中,得到的三個向量分別作爲矩陣的三行就行了,最後的旋轉矩陣如下所示:
[nx2(1cosθ)+cosθnxny(1cosθ)+nzsinθnxnz(1cosθ)nysinθnxny(1cosθ)nzsinθny2(1cosθ)+cosθnynz(1cosθ)+nxsinθnxnz(1cosθ)+nysinθnynz(10cosθ)nxsinθnx2(1cosθ)+cosθ] \begin{bmatrix} n_x^2(1-cos\theta)+cos\theta & n_xn_y(1-cos\theta)+n_zsin\theta&n_xn_z(1-cos\theta)-n_ysin\theta\\ n_xn_y(1-cos\theta)-n_zsin\theta&n_y^2(1-cos\theta)+cos\theta&n_yn_z(1-cos\theta)+n_xsin_\theta\\ n_xn_z(1-cos\theta)+n_ysin\theta&n_yn_z(1-0cos\theta)-n_xsin\theta&n_x^2(1-cos\theta)+cos\theta \end{bmatrix}

markdown輸入矩陣挺爽的。。。。

在unity實現

基本思路就是給定一條直線,比如說x=y,然後把基於x=y的旋轉矩陣實時傳入着色器,讓物體隨着時間能繞着x=y進行旋轉。
在這裏插入圖片描述
腳本很簡單,裏面包括了我們之前推導的旋轉矩陣,不過值的注意的點是,記得把方向歸一化,這個問題我找了好長時間才發現。

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

public class RotateMatrixTest : MonoBehaviour
{
    public float speed;
    public Material mat;
    public Vector3 n;
    public float currentTheta;
    private Matrix4x4 calculateRotateMatrix(Vector3 n,float theta)
    {
        Matrix4x4 RotationMatrix = new Matrix4x4();
        RotationMatrix[0, 0] = n.x * n.x * (1 - Mathf.Cos(theta)) + Mathf.Cos(theta);
        RotationMatrix[0, 1] = n.x * n.y * (1 - Mathf.Cos(theta)) + n.z * Mathf.Sin(theta);
        RotationMatrix[0, 2] = n.x * n.z * (1 - Mathf.Cos(theta)) - n.y * Mathf.Sin(theta);
        RotationMatrix[0, 3] = 0;
        RotationMatrix[1, 0] = n.x * n.y * (1 - Mathf.Cos(theta)) - n.z * Mathf.Sin(theta);
        RotationMatrix[1, 1] = n.y * n.y * (1 - Mathf.Cos(theta)) + Mathf.Cos(theta);
        RotationMatrix[1, 2] = n.y * n.z * (1 - Mathf.Cos(theta)) + n.x * Mathf.Sin(theta);
        RotationMatrix[1, 3] = 0;
        RotationMatrix[2, 0] = n.x * n.z * (1 - Mathf.Cos(theta)) + n.y * Mathf.Sin(theta);
        RotationMatrix[2, 1] = n.y * n.z * (1 - Mathf.Cos(theta)) - n.x * Mathf.Sin(theta);
        RotationMatrix[2, 2] = n.z * n.z * (1 - Mathf.Cos(theta)) + Mathf.Cos(theta);
        RotationMatrix[2, 3] = 0;
        RotationMatrix[3, 0] = 0;
        RotationMatrix[3, 1] = 0;
        RotationMatrix[3, 2] = 0;
        RotationMatrix[3, 3] = 1;
        return RotationMatrix;
    }
    private void Update()
    {
        Vector3 normalizedN = n.normalized;
        currentTheta += speed;
        if (currentTheta > 360 || currentTheta < -360)
            currentTheta = 0;
        mat.SetMatrix("_RotationMatrix", calculateRotateMatrix(normalizedN, Mathf.Deg2Rad*currentTheta));
    }
}

着色器代碼如下(我們是繞着模型空間來旋轉的):

Shader "Unlit/rotateTest"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        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;
				float4 worldPos : TEXCOORD1;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
			float4x4 _RotationMatrix;
            v2f vert (appdata v)
            {
                v2f o;
				fixed4 rotPos = mul(_RotationMatrix,v.vertex);
                o.vertex = UnityObjectToClipPos(rotPos);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
				
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

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