目錄:
- 一、導(dǎo)讀
- 二、矩陣介紹
- 三、2維向量旋轉(zhuǎn)
- 四、3維向量旋轉(zhuǎn)
一、導(dǎo)讀
- 本教程需要讀者有一定的空間想象能力
- 本教程使用的 PaperSpigot1.15.2-R0.1-SNAPSHOT 核心
- 在閱讀之前請確保你具有 線性代數(shù) 和 Java基礎(chǔ) 的知識(shí)
- 本教程會(huì)使用 ParticleLib 中所使用的 Matrix 作為工具
二、矩陣介紹
(一) 定義
由 個(gè)數(shù)排成如下
行
列的一個(gè)表格
稱為是一個(gè) 矩陣, 當(dāng)
時(shí), 矩陣
稱為
階矩陣或
階方陣
(二) 運(yùn)算法則
為了方便, 以下內(nèi)容我們會(huì)盡可能的使用方陣進(jìn)行解釋
和運(yùn)算
設(shè)有矩陣 和矩陣
, 且這兩個(gè)矩陣并且行列個(gè)數(shù)相等, 即可進(jìn)行和運(yùn)算
則結(jié)果矩陣 為
你可能發(fā)現(xiàn)了, 和運(yùn)算即為將對應(yīng)行列進(jìn)行相加減即可
乘積
這部分的內(nèi)容你可能會(huì)看不明白,我建議你可以看看這個(gè)視頻
設(shè)矩陣 為
行
列的矩陣, 矩陣
為
行
列的矩陣
則 為
矩陣是一個(gè)
行
列的矩陣

舉個(gè)例子:
則結(jié)果矩陣 為
請牢記: 由于乘積運(yùn)算會(huì)改變行列個(gè)數(shù), 因此不滿足我們熟知的乘法交換律
并且我們可以知道, 在矩陣當(dāng)中, 向量左乘和右乘的得到的結(jié)果不是相同的
轉(zhuǎn)置
像這樣, 將矩陣的行列互換, 即為向量的轉(zhuǎn)置, 并且記為
三、2維向量的變換(Transformation)
講了這么多,我們?nèi)绻唤?jīng)歷實(shí)操的話, 那么可能你明天就會(huì)忘記掉這些知識(shí), 讓我們以一種全新的方式來理解矩陣, 并且我希望你帶著這樣的一個(gè)問題,在矩陣乘法時(shí), 在圖像上是發(fā)生了怎樣的一個(gè)變換?
首先我們在二維的平面上取一點(diǎn)
,并且我們建立一個(gè)向量, 我們稱之為
吧, 在這里我將這個(gè)向量丟入矩陣當(dāng)中
HINT:請注意, 矩陣的書寫可以為上方的中括號(hào), 也可以為 這樣的小括號(hào)
案例1: 將向量擴(kuò)大一倍
那么我想有一個(gè)問題, 我能不能通過矩陣計(jì)算的方式, 將該向量 擴(kuò)大一倍 使其落在
, 答案是可以的
讓我們建立兩個(gè)基向量
我們將這兩個(gè)基向量擴(kuò)大到原來的兩倍, 也就是乘以2, 因此我們可以得到這兩個(gè)向量這樣的變換
之后我們將這兩個(gè)基向量丟進(jìn)一個(gè)矩陣 當(dāng)中
如果我們將 乘
, 我們看看可以得到什么
根據(jù)矩陣乘積計(jì)算我們可以得到
我們驚喜的發(fā)現(xiàn), 我們成功的將原來的向量 放大了一倍
難道是巧合嗎? 我們可以試試將 (1,1) 改成任何的數(shù), 不難發(fā)現(xiàn)這其實(shí)是一種通法
我們重新回顧一下上面的內(nèi)容
- 首先我們選取了向量
讓他變成了一個(gè)矩陣
- 我們建立了兩個(gè)基向量, 將其放大一倍, 并且我們按順序?qū)⑵鋪G進(jìn)一個(gè)矩陣
里
- 我們用矩陣T乘以矩陣
,結(jié)果得到了上面的內(nèi)容
因此我們可以認(rèn)為, 首先我們將這個(gè)變換Transformation給定義好, 把他弄得像一個(gè)函數(shù)一樣, 不管代入什么向量矩陣, 我們都可以將其做相同的操作,抽象一下即可得到
所以我們可以知道,矩陣其實(shí)是一種已變換后的位置的記號(hào)罷了, 所有沒變化之前的向量都可以通過這個(gè)記號(hào)進(jìn)行變化
案例2: 逆時(shí)針旋轉(zhuǎn)45°
我們再來引入另外一個(gè)案例, 我們想定義一個(gè)矩陣, 所有向量通過這個(gè)矩陣后, 都可以逆時(shí)針旋轉(zhuǎn)90°
我們可以這樣來定義這個(gè)矩陣, 首先我們將基向量定在 (1,0) 和 (0,1) 想象一下它逆時(shí)針旋轉(zhuǎn)90°后是怎樣的情形
如果你對上述操作有疑問不妨可以回顧參數(shù)方程中圓的方程或者可以看看這個(gè)教程
并且請關(guān)注一下 第一列的向量 和 第二列的向量
我們定義一個(gè)向量 坐落在
這個(gè)點(diǎn)
因此可以算出它旋轉(zhuǎn)之后得到的點(diǎn)
MC中的實(shí)現(xiàn)
為了減少本篇代碼量, 與造福全人類的偉大使命, 我這里直接調(diào)用 ParticleLib 當(dāng)中 Matrix 的代碼, 你可以直接將該復(fù)制到你的項(xiàng)目當(dāng)中
首先我們可以看到在同文件夾下有一個(gè)叫做Matrixs的類, 這個(gè)類裝載著一些我寫的一些預(yù)設(shè)的矩陣
讓我們定義一個(gè)放大兩倍的矩陣
// 定義一個(gè)2行2列的單位矩陣
Matrix T = Matrixs.eyes(2, 2).multiply(2);
// 給定一個(gè) (1, 0, 1) 的向量 由于是
Vector v = new Vector(1, 0, 1);
// 將矩陣作用至向量上, 得到結(jié)果
Vector result = T.applyVector(v);
累積矩陣效果
如果我想做這樣的一個(gè)矩陣, 先擴(kuò)大2倍, 再逆時(shí)針旋轉(zhuǎn)90°是怎樣的呢
// 定義一個(gè)2行2列的單位矩陣, 并放大兩倍的單位矩陣
Matrix T = Matrixs.eyes(2, 2).multiply(2);
// 定義一個(gè)旋轉(zhuǎn)90°的矩陣
Matrix rot = Matrixs.rotate2D(90);
// 得到旋轉(zhuǎn)后的矩陣
Matrix newT = rot.multiply(T); // 注意這里, 實(shí)際調(diào)用時(shí)我寫成了右乘, 因此不能直接調(diào)用 T.multiply(rot)
T.prettyPrinting();
System.out.println("=====");
rot.prettyPrinting();
System.out.println("=====");
newT.prettyPrinting();
請注意上方 multiply 的部分, 寫的時(shí)候?qū)懗闪擞页? 如果你覺得別扭你可以自己改源碼, 這些都是允許的
如果上面的代碼改成 T.multiply(rot) 意思則是, 先旋轉(zhuǎn)90°, 再進(jìn)行擴(kuò)大
輸出結(jié)果
[2.0, 0.0]
[0.0, 2.0]
=====
[6.123233995736766E-17, 1.0]
[-1.0, 6.123233995736766E-17]
=====
[1.2246467991473532E-16, 2.0]
[-2.0, 1.2246467991473532E-16]
上方的那兩個(gè)極小極小的數(shù)是由于精度缺失而導(dǎo)致的, 大家可以看成 0 即可, 在實(shí)際使用當(dāng)中, 這個(gè)很小的數(shù)不會(huì)有較大的影響
[2.0, 0.0]
[0.0, 2.0]
=====
[0, 1.0]
[-1.0, 0]
=====
[0, 2.0]
[-2.0, 0]
四、3維向量的變換(Transformation)
其實(shí)3維向量的變換就是在 階的方陣, 變成
階的方陣
我們來看一個(gè)實(shí)際案例
Vector vector = player.getLocation().getDirection();
// 一個(gè)處于1~2之間隨機(jī)的值
double random = new Random().nextDouble() + 1;
// 原點(diǎn)選取在一個(gè)玩家眼前向前推動(dòng)一點(diǎn)點(diǎn)的距離
Arc arc = new Arc(player.getEyeLocation().add(player.getLocation().getDirection().multiply(random)))
.setStartAngle(0) // 最開始的旋轉(zhuǎn)角度
.setAngle(180) // 旋轉(zhuǎn)角度
.setRadius(2) // 半徑
.setStep(10D); // 步進(jìn)單位
arc.addMatrix(Matrixs.rotateAroundZAxis(30)) // 增加圍繞Z軸旋轉(zhuǎn)30°
.addMatrix(Matrixs.rotateAroundYAxis(-player.getLocation().getYaw())); // 增加圍繞Y軸旋轉(zhuǎn)關(guān)于玩家視角的變換, 這樣就會(huì)使得特效一直在玩家眼前
arc.setParticle(Particle.TOTEM)
.setCount(0)
.setOffsetX(2 * vector.getX())
.setOffsetY(2 * vector.getY())
.setOffsetZ(2 * vector.getZ())
.setExtra(0.5)
.show();
具體效果
