[Tutorial][数学向]从零开始的MC特效(七 | 矩阵)

目录:

  • 一、导读
  • 二、矩阵介绍
  • 三、2维向量旋转
  • 四、3维向量旋转

一、导读

  • 本教程需要读者有一定的空间想象能力
  • 本教程使用的 PaperSpigot1.15.2-R0.1-SNAPSHOT 核心
  • 在阅读之前请确保你具有 线性代数Java基础 的知识
  • 本教程会使用 ParticleLib 中所使用的 Matrix 作为工具

二、矩阵介绍

(一) 定义

m \times n 个数排成如下 mn 列的一个表格
A=\begin{bmatrix} {a_{11}}&{a_{12}}&{\cdots}&{a_{1n}}\\\\ {a_{21}}&{a_{22}}&{\cdots}&{a_{2n}}\\\\ {\vdots}&{\vdots}&{\ddots}&{\vdots}\\\\ {a_{m1}}&{a_{m2}}&{\cdots}&{a_{mn}}\\\\ \end{bmatrix}
称为是一个 m \times n 矩阵, 当 m = n 时, 矩阵 A 称为 n 阶矩阵或 n 阶方阵

(二) 运算法则

为了方便, 以下内容我们会尽可能的使用方阵进行解释

和运算

设有矩阵 A 和矩阵 B , 且这两个矩阵并且行列个数相等, 即可进行和运算
A=\begin{bmatrix} {1}\\\\ {3}\\\\ \end{bmatrix}B=\begin{bmatrix} {1}\\\\ {0}\\\\ \end{bmatrix}
则结果矩阵 C
C=\begin{bmatrix} {2}\\\\ {3}\\\\ \end{bmatrix}
你可能发现了, 和运算即为将对应行列进行相加减即可

乘积

这部分的内容你可能会看不明白,我建议你可以看看这个视频

设矩阵 Amn 列的矩阵, 矩阵 Bns 列的矩阵
ABC 矩阵是一个 ms 列的矩阵

矩阵乘法

举个例子:
A=\begin{bmatrix} {1}&{-3}\\\\ {2}&{4}\\\\ \end{bmatrix}B=\begin{bmatrix} {5}\\\\ {7}\\\\ \end{bmatrix}
则结果矩阵 C
C=\begin{bmatrix} {1\times5 + -3\times7}\\\\ {2\times5 + 4\times7}\\\\ \end{bmatrix}=\begin{bmatrix} {-16}\\\\ {38}\\\\ \end{bmatrix}
请牢记: AB \neq BA 由于乘积运算会改变行列个数, 因此不满足我们熟知的乘法交换律
并且我们可以知道, 在矩阵当中, 向量左乘和右乘的得到的结果不是相同的

转置

像这样, 将矩阵的行列互换, 即为向量的转置, 并且记为 A^T
A=\begin{bmatrix} {5}\\\\ {2}\\\\ \end{bmatrix}A^T=\begin{bmatrix} {5}&{2} \end{bmatrix}

三、2维向量的变换(Transformation)

讲了这么多,我们如果不经历实操的话, 那么可能你明天就会忘记掉这些知识, 让我们以一种全新的方式来理解矩阵, 并且我希望你带着这样的一个问题,在矩阵乘法时, 在图像上是发生了怎样的一个变换?

首先我们在二维的XY平面上取一点 (1, 1) ,并且我们建立一个向量, 我们称之为 v 吧, 在这里我将这个向量丢入矩阵当中
v=\begin{pmatrix} {1}\\\\ {1} \end{pmatrix}
HINT:请注意, 矩阵的书写可以为上方的中括号, 也可以为 v 这样的小括号

案例1: 将向量扩大一倍

那么我想有一个问题, 我能不能通过矩阵计算的方式, 将该向量 v 扩大一倍 使其落在 (2, 2), 答案是可以的
让我们建立两个基向量
\hat{i}=\begin{pmatrix} {1}\\\\ {0} \end{pmatrix}\hat{j}=\begin{pmatrix} {0}\\\\ {1} \end{pmatrix}
我们将这两个基向量扩大到原来的两倍, 也就是乘以2, 因此我们可以得到这两个向量这样的变换
\hat{i}=2\begin{pmatrix} {1}\\\\ {0} \end{pmatrix}=\begin{pmatrix} {2}\\\\ {0} \end{pmatrix}
\hat{j}=2\begin{pmatrix} {0}\\\\ {1} \end{pmatrix}=\begin{pmatrix} {0}\\\\ {2} \end{pmatrix}
之后我们将这两个基向量丢进一个矩阵 T 当中
T=\begin{pmatrix} {2}&{0}\\\\ {0}&{2} \end{pmatrix}
如果我们将 Tv, 我们看看可以得到什么
Result=\begin{pmatrix} {2}&{0}\\\\ {0}&{2} \end{pmatrix}\begin{pmatrix} {1}\\\\ {1} \end{pmatrix}
根据矩阵乘积计算我们可以得到
Result=\begin{pmatrix} {2\times1+0\times1}\\\\ {0\times1+2\times1} \end{pmatrix}=\begin{pmatrix} {2}\\\\ {2} \end{pmatrix}
我们惊喜的发现, 我们成功的将原来的向量 v 放大了一倍
难道是巧合吗? 我们可以试试将 (1,1) 改成任何的数, 不难发现这其实是一种通法

我们重新回顾一下上面的内容

  1. 首先我们选取了向量 v 让他变成了一个矩阵
  2. 我们建立了两个基向量, 将其放大一倍, 并且我们按顺序将其丢进一个矩阵 T
  3. 我们用矩阵T乘以矩阵 v,结果得到了上面的内容

因此我们可以认为, 首先我们将这个变换Transformation给定义好, 把他弄得像一个函数一样, 不管代入什么向量矩阵, 我们都可以将其做相同的操作,抽象一下即可得到
Result=\begin{pmatrix} {2}&{0}\\\\ {0}&{2} \end{pmatrix}\begin{pmatrix} {x}\\\\ {y} \end{pmatrix}=\begin{pmatrix} {2\times x+0\times y}\\\\ {0\times x+2\times y} \end{pmatrix}=\begin{pmatrix} {2x}\\\\ {2y} \end{pmatrix}
所以我们可以知道,矩阵其实是一种已变换后的位置的记号罢了, 所有没变化之前的向量都可以通过这个记号进行变化

案例2: 逆时针旋转45°

我们再来引入另外一个案例, 我们想定义一个矩阵, 所有向量通过这个矩阵后, 都可以逆时针旋转90°

我们可以这样来定义这个矩阵, 首先我们将基向量定在 (1,0) 和 (0,1) 想象一下它逆时针旋转90°后是怎样的情形
T=\begin{pmatrix} {1}&{0}\\\\ {0}&{1} \end{pmatrix}\rightarrow\begin{pmatrix} {0}&{-1}\\\\ {1}&{0} \end{pmatrix}=\begin{pmatrix} {cos(90°)}&{-sin(90°)}\\\\ {sin(90°)}&{cos(90°)} \end{pmatrix}
如果你对上述操作有疑问不妨可以回顾参数方程中圆的方程或者可以看看这个教程
\begin{equation} C(t)=\begin{cases} x=cos(t)\\\\ y=sin(t) \end{cases} \end{equation}
并且请关注一下 第一列的向量第二列的向量
\begin{bmatrix} {cos(t)}&{-sin(t)}\\\\ {sin(t)}&{cos(t)} \end{bmatrix}

我们定义一个向量 v 坐落在 (1,2) 这个点
因此可以算出它旋转之后得到的点
Result=\begin{pmatrix} {-2}\\\\ {1} \end{pmatrix}

MC中的实现

为了减少本篇代码量, 与造福全人类的伟大使命, 我这里直接调用 ParticleLib 当中 Matrix 的代码, 你可以直接将该复制到你的项目当中

首先我们可以看到在同文件夹下有一个叫做Matrixs的类, 这个类装载着一些我写的一些预设的矩阵

让我们定义一个放大两倍的矩阵

// 定义一个2行2列的单位矩阵
Matrix T = Matrixs.eyes(2, 2).multiply(2);
// 给定一个 (1, 0, 1) 的向量 由于是
Vector v = new Vector(1, 0, 1);
// 将矩阵作用至向量上, 得到结果
Vector result = T.applyVector(v);
累积矩阵效果

如果我想做这样的一个矩阵, 先扩大2倍, 再逆时针旋转90°是怎样的呢

// 定义一个2行2列的单位矩阵, 并放大两倍的单位矩阵
Matrix T = Matrixs.eyes(2, 2).multiply(2);
// 定义一个旋转90°的矩阵
Matrix rot = Matrixs.rotate2D(90);
// 得到旋转后的矩阵
Matrix newT = rot.multiply(T); // 注意这里, 实际调用时我写成了右乘, 因此不能直接调用 T.multiply(rot)
T.prettyPrinting();
System.out.println("=====");
rot.prettyPrinting();
System.out.println("=====");
newT.prettyPrinting();

请注意上方 multiply 的部分, 写的时候写成了右乘, 如果你觉得别扭你可以自己改源码, 这些都是允许的
如果上面的代码改成 T.multiply(rot) 意思则是, 先旋转90°, 再进行扩大

输出结果

[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]

上方的那两个极小极小的数是由于精度缺失而导致的, 大家可以看成 0 即可, 在实际使用当中, 这个很小的数不会有较大的影响

[2.0, 0.0]
[0.0, 2.0]
=====
[0, 1.0]
[-1.0, 0]
=====
[0, 2.0]
[-2.0, 0]

四、3维向量的变换(Transformation)

其实3维向量的变换就是在 2\times2 阶的方阵, 变成 3\times3 阶的方阵
我们来看一个实际案例

Vector vector = player.getLocation().getDirection();
// 一个处于1~2之间随机的值
double random = new Random().nextDouble() + 1;
// 原点选取在一个玩家眼前向前推动一点点的距离
Arc arc = new Arc(player.getEyeLocation().add(player.getLocation().getDirection().multiply(random)))
        .setStartAngle(0) // 最开始的旋转角度
        .setAngle(180) // 旋转角度
        .setRadius(2) // 半径
        .setStep(10D); // 步进单位
arc.addMatrix(Matrixs.rotateAroundZAxis(30)) // 增加围绕Z轴旋转30°
        .addMatrix(Matrixs.rotateAroundYAxis(-player.getLocation().getYaw())); // 增加围绕Y轴旋转关于玩家视角的变换, 这样就会使得特效一直在玩家眼前
arc.setParticle(Particle.TOTEM)
        .setCount(0)
        .setOffsetX(2 * vector.getX())
        .setOffsetY(2 * vector.getY())
        .setOffsetZ(2 * vector.getZ())
        .setExtra(0.5)
        .show();

具体效果


QQ录屏20220709123831~2.gif
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339

推荐阅读更多精彩内容