澳门在线威尼斯官方 > 电脑操作 > 在Shader之前_基础数学,矩阵专栏

原标题:在Shader之前_基础数学,矩阵专栏

浏览次数:199 时间:2019-11-17

 

在本篇开始前,先向大家推荐一本书:《3D数学基础:图形与游戏开发》
这本书很多游戏相关的数学知识都讲得很细,值得一看。以前学的东西好多都还给老师了,啃这本书,补回了以前的好多知识啊!(本人数学渣,看这本书费了点时间,还好之前学过,能懂在讲什么。。。)

2.矩阵专栏¶

吐槽一下:矩阵本身不难,但是矩阵的写作太蛋疼了 (⊙﹏⊙)汗 还好有Numpy,不然真的崩溃了...

LaTex有没有一个集成了很多常用公式以及推导或者含题库的在线编辑器?

代码裤子:

在线编程系:

数学基础:

Numpy基础:

在线预览:http://github.lesschina.com/python/ai/math/矩阵专栏.html

本文章面向的对象是有3D数学基础但是由于长时间没接触变生疏,或对某些知识点理解尚有问题的人群。

2.1.矩阵的定义¶

矩阵:是一个按照长方阵列排列的复数或实数集合。

通俗讲就是:把数排成m行n列后,然后用中括号把它们括住,这种形式的组合就是矩阵了~ eg:

$begin{bmatrix} &a_{11}&a_{12}&a_{13}&...&a_{1n} \ &a_{21}&a_{22}&a_{23}&...&a_{2n} \ &a_{31}&a_{32}&a_{33}&...&a_{3n} \ &vdots&vdots&vdots&ddots&vdots\ &a_{m1}&a_{m2}&a_{m3}&...&a_{mn} \ end{bmatrix}$

比如上面这个示例就是一个m × n的矩阵(m行n列的矩阵),如果m=n那么就叫做n阶方阵,eg:

$begin{bmatrix} 1&2&3 \ 4&5&6 \ 7&8&9 end{bmatrix}$

这个就是3阶方阵


如果回到中学,老师肯定都是通过一次方程组来引入矩阵(逆天的老师是这么讲的):

$begin{cases}x_1+x_2=-1\2x_1-x_2=4\3x_1+5x_2=-7\end{cases}$ ==> $begin{bmatrix}1&1\2&-1\3&5end{bmatrix}begin{bmatrix}x_1\x_2end{bmatrix}=begin{bmatrix}-1\4\-7end{bmatrix}$

如果你方程组都忘记怎么解的话...好吧还是说下吧:“比如这题,可以先把x2移到右边,这样x1就等于一个表达式了(x1=-x2-1),然后带入第二个表达式就可以解出x1和x2了,一次的其实两个表达式就可以解出了,剩下的你可以把值带进去验证一下”


2.2.矩阵的运算(含幂运算)¶

坐标系

首先要讲的是坐标系,在3D中,坐标系类型有很多,比较重要的是3D坐标系,常见的分两种:左手坐标系和右手坐标系,主要区别是Z轴的方向(我们定义X轴默认正方向为右,Y轴默认正方向为上),左手系Z轴正方向为指向屏幕内部,右手系Z轴正方向为指向屏幕外部:

图片 1

左手系 (图截自《3D数学基础:图形与游戏开发》)

图片 2

右手系 (图截自《3D数学基础:图形与游戏开发》)

Ps:在Unity中,默认使用的是左手坐标系(摄像机的观察空间(以摄像机为原点的坐标系)除外)。

在一个空间中,有了坐标系的存在,就可以确定空间中物体的具体位置。

2.2.1.加、减¶

加减比较简单,就是对应元素相加减 (只有行列都相同的矩阵才可以进行)

就不用麻烦的LaTex一行行打了,咱们用更方便的 NumPy 来演示一下矩阵加法(不懂代码的直接看结果,不影响阅读的)

Numpy有专门的矩阵函数(np.mat),用法和ndarray差不多,我们这边使用经常使用ndarray类型,基础忘记了可以去查看一下:Numpy基础

扩展:矩阵的加法运算满足交换律:A + B = B + A (乘法不行)

In [1]:

import numpy as np

In [2]:

# 创建两个集合
A = np.arange(1,10).reshape((3,3))
B = np.arange(9).reshape((3,3))

print(A)
print("-"*5)
print(B)

 

[[1 2 3]
 [4 5 6]
 [7 8 9]]
-----
[[0 1 2]
 [3 4 5]
 [6 7 8]]

In [3]:

# 加法
A + B

Out[3]:

array([[ 1,  3,  5],
       [ 7,  9, 11],
       [13, 15, 17]])

In [4]:

# 和A+B相等
B + A

Out[4]:

array([[ 1,  3,  5],
       [ 7,  9, 11],
       [13, 15, 17]])

In [5]:

# 减法
A - B

Out[5]:

array([[1, 1, 1],
       [1, 1, 1],
       [1, 1, 1]])

In [6]:

################ 变化来了 ################

In [7]:

# 之前说过 ”只有行列都相同的矩阵才可以进行“ 来验证一下
# 创建一个2行3列的矩阵
C = np.arange(6).reshape((2,3))
D = np.arange(6).reshape((3,2))

print(C)
print("-"*5)
print(D)

 

[[0 1 2]
 [3 4 5]]
-----
[[0 1]
 [2 3]
 [4 5]]

In [8]:

# 2行3列的矩阵 + 3行2列的矩阵
C + D # 不同形状的矩阵不能进行加运算

 

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-8-bc97e29f7e31> in <module>()
      1 # 2行3列的矩阵 + 3行2列的矩阵
----> 2C + D # 不同形状的矩阵不能进行加运算

ValueError: operands could not be broadcast together with shapes (2,3) (3,2) 

In [9]:

C - D # 不同形状的矩阵不能进行减运算

 

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-9-ca5169d0bf6c> in <module>()
----> 1C - D # 不同形状的矩阵不能进行减运算

ValueError: operands could not be broadcast together with shapes (2,3) (3,2) 

 

向量

向量首先要跟标量区分:向量具有方向和大小等概念,而标量只有大小。在Unity中常用的向量有法线、切线等。

在表示形式上,向量可以用一个一维数组表示,比如:
![][1]
[1]:http://latex.codecogs.com/png.latex?a=begin{bmatrix}1&2&3end{bmatrix}

![][2]
[2]:http://latex.codecogs.com/png.latex?a=begin{bmatrix}123end{bmatrix}
横向书写的称为行向量,竖向书写的称为列向量,向量中的每一个分量对应于其n维空间中的分量,上例中分量为x、y、z,至于在使用时是用行还是列,要根据实际情况来确定。(后面会提到)
而在物理中,向量具有其实际意义:比如上述的a向量,表示由一个点到另外一个点位移,上式中,表示点A向x方向移动1个单位,y方向移动2个单位,z方向移动3个单位到达点B。(注意,在坐标系中,位移没有具体的位置,可以在任意位置表示,只表示位移量和方向)

2.2.2.数乘、数除¶

这个也比较简单,就是和每个元素相乘,eg:2×A,A原本的每一个元素都扩大了两倍

数除其实就是乘以倒数(1/x)

In [10]:

print(A)

 

[[1 2 3]
 [4 5 6]
 [7 8 9]]

In [11]:

# 比如2×A,A原本的每一个元素都扩大了两倍
2 * A

Out[11]:

array([[ 2,  4,  6],
       [ 8, 10, 12],
       [14, 16, 18]])

In [12]:

print(A)

 

[[1 2 3]
 [4 5 6]
 [7 8 9]]

In [13]:

# 友情提醒:Numpy里面的运算基本上都是针对每一个元素
A / 2

Out[13]:

array([[0.5, 1. , 1.5],
       [2. , 2.5, 3. ],
       [3.5, 4. , 4.5]])

 

向量运算

向量大小:向量的大小也称为向量的模,表示位移量的大小,以3维向量为例:
![][1]
[1]:http://latex.codecogs.com/png.latex?a=begin{bmatrix}1&2&3end{bmatrix}
该向量的大小计算为:
![][3]
[3]:http://latex.codecogs.com/png.latex?left|aright|=sqrt{12+22+3^2}
即为两点间的距离公式
在Shader中,我们经常要将向量标准化(也称为归一化),也就是把向量的大小置为1,在坐标系中可以理解为以一个点为圆(球)心,半径为1的一个圆(球)
标准化公式如下:
![][4]
[4]:http://latex.codecogs.com/png.latex?a_{normal}=frac{a}{left|aright|}

图片 3

单位圆 (图截自《3D数学基础:图形与游戏开发》)

2.2.3.矩阵乘法¶

矩阵乘法还是要用LaTex演示一下的,不然有些朋友可能还是觉得比较抽象:(大家有什么好用的LaTex在线编辑器可以推荐的)

拿上面那个方程组来演示一下:

$begin{bmatrix}1&1\2&-1\3&5end{bmatrix}begin{bmatrix}x_1\x_2end{bmatrix} ==> begin{cases}x_1+x_2\2x_1-x_2\3x_1+5x_2end{cases}$

稍微变化一下就更形象了:

$begin{bmatrix}1&1\2&-1\3&5end{bmatrix}begin{bmatrix}x_1&y_1\x_2&y_2end{bmatrix} ==> begin{cases}x_1+x_2\2x_1-x_2\3x_1+5x_2end{cases} begin{cases}y_1+y_2\2y_1-x_2\3y_1+5y_2end{cases}==>begin{bmatrix}x_1+x_2&y_1+y_2\2x_1-x_2&2y_1-y_2\3x_1+5x_2&3y_1+5y_2end{bmatrix}$

举个简单的例子:A×B

$begin{bmatrix} 1&2 \3&4 end{bmatrix}begin{bmatrix} 4&3 \2&1 end{bmatrix}=begin{bmatrix} 1*4+2*2&1*3+2*1 \3*4+4*2&3*3+4*1 end{bmatrix}=begin{bmatrix} 8&5 \20&13 end{bmatrix}$

以后记不得怎么乘就自己推一下,值得注意的是:

两个矩阵的乘法仅当第一个矩阵A的列数(column)和另一个矩阵B的行数(row)相等才可以进行计算

你这样想就记得了:$begin{bmatrix} 1&2 \3&4 end{bmatrix}begin{bmatrix} x_1 end{bmatrix} 还原成方程组就是这样begin{cases}1*x_1+2*?\3*x_1+4*?end{cases}\这是什么鬼?至少得这样吧:begin{bmatrix} 1&2 \3&4 end{bmatrix}begin{bmatrix} x_1 \x_2 end{bmatrix}$

In [14]:

# 通过代码看一看
A = np.array([[1,2],[3,4]])
B = np.array([[4,3],[2,1]])

print(A)
print("-"*5)
print(B)

 

[[1 2]
 [3 4]]
-----
[[4 3]
 [2 1]]

In [15]:

# 注意一下,Numpy里面的乘法默认是每个数对应相乘
# 如果是矩阵相乘可以使用dot()方法
# 或者你创建矩阵对象,这样×默认就是矩阵乘法了

A.dot(B) # 矩阵A×矩阵B

Out[15]:

array([[ 8,  5],
       [20, 13]])

 

程序验证了我们上面的运算结果,还得注意一下:

A×BB×A是不一样的,eg:B×A

$begin{bmatrix} 4&3 \2&1 end{bmatrix}begin{bmatrix} 1&2 \3&4 end{bmatrix}=begin{bmatrix} 4*1+3*3&4*2+3*4 \2*1+1*3&2*2+1*4 end{bmatrix}=begin{bmatrix} 13&20 \5&8 end{bmatrix}$

如果你乘着乘着就忘记到底怎么乘,就把右边的矩阵换成x1,x2,然后就会了

In [16]:

print(A)
print("-"*5)
print(B)

 

[[1 2]
 [3 4]]
-----
[[4 3]
 [2 1]]

In [17]:

B.dot(A) # 矩阵B×矩阵A

Out[17]:

array([[13, 20],
       [ 5,  8]])

In [18]:

################ 变化来了 ################

In [19]:

# 来验证一下”两个矩阵的乘法仅当第一个矩阵A的列数(column)和另一个矩阵D的行数(row)相等才可以进行计算“
print(A)
print("-"*5)
print(D)

 

[[1 2]
 [3 4]]
-----
[[0 1]
 [2 3]
 [4 5]]

In [20]:

# A有2列 D有3行
A.dot(D) # 不能乘

 

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-20-c1a9f22a6f8d> in <module>()
      1 # A有2列 D有3行
----> 2A.dot(D) # 不能乘

ValueError: shapes (2,2) and (3,2) not aligned: 2 (dim 1) != 3 (dim 0)

In [21]:

# 你反过来就符合A的列数=D的行数了
D.dot(A)

Out[21]:

array([[ 3,  4],
       [11, 16],
       [19, 28]])

 

向量运算

向量的运算很简单,其实就是各个分类的相加减。
以加法为例(减法相同):
![][5]
[5]:http://latex.codecogs.com/png.latex?begin{bmatrix}123end{bmatrix}+begin{bmatrix}321end{bmatrix}=begin{bmatrix}1+32+23+1end{bmatrix}=begin{bmatrix}444end{bmatrix}

2.2.4.幂乘、幂运算¶

幂乘比较简单,就是每个元素开平方,不一定是方阵

必须是方阵才能进行幂运算,比如A²=A×A(矩阵相乘前提:第一个矩阵A的行=第二个矩阵A的列==>方阵

In [22]:

print(A)
print("-"*5)
print(C)

 

[[1 2]
 [3 4]]
-----
[[0 1 2]
 [3 4 5]]

In [23]:

# 幂乘(每个元素开平方) 
np.power(A,2) # 使用 A**2也一样

Out[23]:

array([[ 1,  4],
       [ 9, 16]])

In [24]:

# 幂乘(不一定是方阵) 
np.power(C,2)

Out[24]:

array([[ 0,  1,  4],
       [ 9, 16, 25]])

In [25]:

################ 方阵幂运算 ################

In [26]:

# A*A*A
np.linalg.matrix_power(A,3)

Out[26]:

array([[ 37,  54],
       [ 81, 118]])

In [27]:

# 不是方阵就over
np.linalg.matrix_power(C,3)

 

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-27-73f04ef7b54c> in <module>()
      1 # 不是方阵就over
----> 2np.linalg.matrix_power(C,3)

~/anaconda3/lib/python3.6/site-packages/numpy/matrixlib/defmatrix.py in matrix_power(M, n)
    137     M = asanyarray(M)
    138     if M.ndim != 2 or M.shape[0] != M.shape[1]:
--> 139raise ValueError("input must be a square array")
    140     if not issubdtype(type(n), N.integer):
    141         raise TypeError("exponent must be an integer")

ValueError: input must be a square array

 

来个小结 + 扩展

矩阵的加法运算满足交换律:A + B = B + A

矩阵的乘法满足结合律和对矩阵加法的分配律:

结合律:(AB)C = A(BC)

左分配律:(A + B)C = AC + BC

右分配律:C(A + B) = CA + CB

矩阵的乘法与数乘运算之间也满足类似结合律的规律;与转置之间则满足倒置的

分配律:c(A + B) = cA + cB

结合律:c(AB) = (cA)B = A(cB)

矩阵乘法不满足交换律 一般来说,矩阵A及B的乘积AB存在,但BA不一定存在,即使存在,大多数时候AB ≠ BA


向量运算几何意义:

a+b:a的起点到b的终点的向量;
c-d:d的终点到c的终点的向量;

图片 4

向量加减 (图截自《3D数学基础:图形与游戏开发》)

2.3.特殊矩阵¶

向量点乘

点乘形式:
![][6]
[6]:http://latex.codecogs.com/png.latex?begin{bmatrix}a_1a_2a_3end{bmatrix}.begin{bmatrix}b_1b_2b_3end{bmatrix}=a_1b_1+a_2b_2+a_3b_3
几何点乘公式:
![][7]
[7]:http://latex.codecogs.com/png.latex?acdot{b}=left|aright|*left|bright|astcostheta
其几何意义为向量a在向量b上的投影(当夹角大于90°时结果为负值)

图片 5

点乘 (图截自《3D数学基础:图形与游戏开发》)

向量点乘结果为一个标量。

2.3.1.零矩阵¶

零矩阵就是所有的元素都是0

$ begin{bmatrix} 0&0&0 \ 0&0&0 \ 0&0&0 end{bmatrix} $

同样的:全1矩阵就是所有元素都是1

$ begin{bmatrix} 1&1&1 \ 1&1&1 \ 1&1&1 end{bmatrix} $

In [1]:

import numpy as np

In [2]:

# 一维
# 可以指定类型 np.zeros(5,dtype=int)
np.zeros(5) # 完整写法:np.zeros((5,))

Out[2]:

array([0., 0., 0., 0., 0.])

In [3]:

# 二维
np.zeros((2,5))# 建议用元组,官方文档都是元组

Out[3]:

array([[0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.]])

In [4]:

# 三维 ==> 可以这么理解,2个2*5(2行5列)的矩阵
np.zeros((2,2,5))

Out[4]:

array([[[0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]],

       [[0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]]])

In [5]:

################ 全1矩阵 ################

In [6]:

# `np.ones(tuple)` 用法和`np.zeros(tuple)`差不多
# 可以指定类型 np.ones(5,dtype=int)
# 一维
np.ones(5) # 完整写法 np.ones((5,))

Out[6]:

array([1., 1., 1., 1., 1.])

In [7]:

# 二维,传一个shape元组
np.ones((2,5))

Out[7]:

array([[1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.]])

In [8]:

# 三维 可以理解为两个二维数组
np.ones((2,2,5))

Out[8]:

array([[[1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1.]],

       [[1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1.]]])

In [9]:

################ 指定值矩阵 ################

In [10]:

# 创建指定值的矩阵:
np.full((3,5),222)

Out[10]:

array([[222, 222, 222, 222, 222],
       [222, 222, 222, 222, 222],
       [222, 222, 222, 222, 222]])

In [11]:

# 创建指定值的矩阵,浮点类型
np.full((3,5),222.0)

Out[11]:

array([[222., 222., 222., 222., 222.],
       [222., 222., 222., 222., 222.],
       [222., 222., 222., 222., 222.]])

 

向量叉乘

叉乘形式:
(其实就是,除当前行外的另外两行组成2*2矩阵,主对角线的乘积减去副对角线的乘积)
![][9]
[9]:http://latex.codecogs.com/png.latex?begin{bmatrix}x_1y_1z_1end{bmatrix}timesbegin{bmatrix}x_2y_2z_2end{bmatrix}=begin{bmatrix}y_1z_2-z_1y_2z_1x_2-x_1z_2x_1y_2-y_1x_2end{bmatrix}
几何叉乘:
向量叉乘结果为垂直于两向量所在平面的向量,叉乘结果向量的方向由两个,取决于坐标系为左手坐标系还是右手坐标系。
方向分辨方法为(a x b为例):
左手系:通过左手法则判断,四指由a向量收向b向量,拇指方向即为结果向量方向。
右手系:通过右手法则判断,四指由a向量收向b向量,拇指方向即为结果向量方向。

图片 6

左手法则/右手法则 (图截自《3D数学基础:图形与游戏开发》)

图片 7

叉乘 (图截自《3D数学基础:图形与游戏开发》)

叉乘结果向量的大小由以下公式给出:
![][8]
[8]:http://latex.codecogs.com/png.latex?left|atimes{b}right|=left|aright|*left|bright|astsintheta

本文由澳门在线威尼斯官方发布于电脑操作,转载请注明出处:在Shader之前_基础数学,矩阵专栏

关键词:

上一篇:没有了

下一篇:基础拓展,Python高级编程总结