Unity Shader:相关数学基础
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity Shader:相关数学基础相关的知识,希望对你有一定的参考价值。
参考技术A 本文同时发布在我的个人博客上: https://dragon_boy.gitee.io有左手坐标系和右手坐标系两种,Unity使用的是右手坐标系。
我们使用两个或三个以上的实数来表示一个点的坐标,如 。
矢量是指n维空间中一种包含了模和方向的有向线段。矢量的表示方法和点类似,如 。
矢量通常由一个箭头表示,由起点指向终点。矢量常被用于表示相对于某个点的偏移,只要矢量的模和方向保持不变,无论在哪里,都是同一个矢量。
对乘法,将矢量的每个分量和标量相乘即可:
对除法,同理: ,标量需非零。
两个矢量加减法,把对应的分量进行相加或相减即可:
几何意义上,矢量相加即前者起点连接至后者终点,矢量相减即后者终点指向前者终点。
矢量的模即矢量的长度:
单位矢量即模为1的矢量。将某个非零矢量转化为单位矢量的过程称为归一化。
矢量之间的乘法有两种,点积和叉积。
点积公式有两个:
1:
2( 为两个矢量的夹角):
点积的结果在几何意义上是获得矢量 在 上的投影与 的模的乘积,如果二者都是单位矢量,那么点积结果就是前者在后者上的投影。
叉积公式:
下面是另一种表示:
上述表明矢量叉积的结果的模是两矢量构成平行四边形的面积。
在几何意义的上,叉积的结果是一个新的矢量,分别垂直于 和 ,且沿 、 和叉积的方向可构成一个右手坐标系。
矩阵有行列之分,以 矩阵为例:
矢量可以看作时 的列矩阵或 的行矩阵。
将矩阵的每个元素和标量相乘即可。
两个矩阵相乘,要求前一个矩阵的列数等于后一个矩阵的行数,相乘得到的矩阵的行数是第一个矩阵的行数,列数是第二个矩阵的列数。如 的维度是 , 的维度是 ,那么 的维度是 。
矩阵的乘法即前一个矩阵的每一行( 行)与后一个矩阵的每一列( 列)相乘(可看作行构成的矢量和列构成的矢量点积),每次相乘的结果填写在对应的 行 列上。
注意,矩阵乘法不满足交换律,但满足结合律。
方阵即行列数相等的矩阵。
同时,如果除对角线的元素外全是0的方阵称为对角矩阵。
针对上述的对角矩阵,如果对角线的元素全是0的话,那么就称为单位矩阵。
对于一个 的矩阵 ,它的转置 为 的矩阵。转置运算即将原矩阵的行列翻转。原矩阵的 行变为 列, 列变为 行。
注意,矩阵的转置的转置等于原矩阵。
矩阵的串接的转置等于反向串接各个矩阵的转置:
只有方阵才有逆矩阵。
一个矩阵和它的逆矩阵的乘积为单位矩阵:
零矩阵没有逆矩阵。如果一个矩阵有逆矩阵,那么这个矩阵是可逆的,或非奇异的,相反则是不可逆的或奇异的。
如果一个矩阵的行列式为不为0,那么该矩阵就是可逆的(具体不解释)。
注意,逆矩阵的逆矩阵为原矩阵。
单位矩阵的逆矩阵是它本身。
转置矩阵的逆矩阵是逆矩阵的转置:
矩阵串接相乘后的逆矩阵等于反向串接各个矩阵的逆矩阵:
注意,如果某个矩阵或矢量进行了一个矩阵变化,那么使用逆矩阵可以还原这个变换:
如果一个方阵和它的转置矩阵的乘积是单位矩阵,那么这个方阵就是正交矩阵:
将正交矩阵与逆矩阵的概念结合起来,就可以得到,如果一个矩阵正交,那么它的转置矩阵和逆矩阵相等:
上述式子在实际计算中非常有用,因为逆矩阵的计算量很大(涉及伴随矩阵),所以如果能判断某个变换矩阵是正交的话,可以很简单地用转置矩阵代替它的逆矩阵进行计算。
那么如何判断一个矩阵是否正交,我们把一个矩阵的列看作是一个矢量,称为基矢量,那么根据正交矩阵定义:
根据上述等式,得:
, , 都是单位矢量,且两两垂直。那么满足这样条件的矩阵的就是正交矩阵。
在Unity中,常将矢量当做是列矩阵来使用,即放到矩阵的右边来进行乘法。这种情况下,矩阵乘法通常是右乘,即:
变换是将一些数据通过某种方式进行转换的过程。
一个非常常见的变换是线性变换,指的是可以保留矢量加和标量乘的变换。数学公式为:
缩放是一种线性变换,旋转也是一种线性变换,我们可以使用一个 的矩阵就可以对一个三维向量进行线性变换。除此之外还有错切,镜像,正交投影。
但平移变换不是线性变换,我们不能使用一个 的矩阵对一个三维向量进行变换。
由此出现仿射变换,即合并线性变换和平移变换的变换类型。仿射变换使用一个 的矩阵来表示,这样我们需要将矢量扩展到四维空间下,即齐次坐标空间。
对于一个三维向量,我们扩展一个维度,分量称为 。对于坐标, 常设为1,而对于方向,常设为0。因为我们只想对位置进行平移变换,方向不行。
我们使用一个 的矩阵来表示平移、旋转和缩放。一个基础的变换矩阵可以分解为4个部分:
表示旋转缩放的矩阵, 表示平移。
下面是一个平移矩阵(针对点)的例子:
如果是方向矢量的话,由于 分量为0,平移变换不会有影响:
平移变换的逆矩阵就是反向平移得到的矩阵:
平移矩阵并不是正交矩阵。
下面是一个缩放矩阵(针对点)的例子:
对方向矢量缩放:
如果 ,那么这样的缩放为统一缩放,否则为非统一缩放。由于非统一缩放会改变角度和比例信息,所以针对方向向量的缩放不能使用上述的方式。
缩放矩阵的逆矩阵如下:
缩放矩阵不是正交矩阵。
上面的矩阵只适合沿坐标轴方向缩放,如果要在任意方向缩放,就要使用一个复合变换,先缩放轴,再沿坐标轴缩放。
绕x轴旋转:
绕y轴旋转:
绕z轴旋转:
旋转矩阵是正交矩阵。
大多数情况下,将平移、旋转、缩放结合起来的顺序往往是:先缩放,后旋转,再平移,也就是:
注意,针对旋转的顺序,Unity的顺序是zxy,即旋转变换矩阵是:
旋转时的坐标系有两种可以选择:
在上述两种方式中,旋转的结果不一样,Unity是第一种。
模型空间即模型的局部坐标系,在Unity中是左手坐标系。
世界空间被用来描述物体在场景中的位置,在Unity中,世界空间是左手坐标系。
将顶点从模型空间变换到世界空间的变换称为模型变换(Model)。这一变换通常由 组成。
也被称为摄像机空间。
摄像机决定了我们渲染游戏所使用的视角。在观察空间中,摄像机位于原点,在Unity中,+x指向摄像机的右方,+y指向摄像机的上方,+z指向摄像机的后方,即摄像机指向-z轴。
将顶点从世界空间转换到观察空间的变换为观察变换(View)。
为得到顶点在观察空间中的位置,有两种方法:一是计算观察空间的三个坐标轴在世界空间下的表示,然后构建出从观察空间变换到世界空间的变换矩阵,再对矩阵求逆来得到从世界空间变换到世界空间的矩阵。二是想象平移整个观察空间,让摄像机的原点位于世界坐标的原点,坐标轴与世界空间的坐标轴重合即可。
这里使用第二种方法,即想办法让摄像机变换到世界空间的原点即可(注意,由于观察空间使用右手坐标系,因此需要对z分量取反)。
将顶点从观察空间转换到裁剪空间的变换矩阵称为裁剪矩阵,也被称为投影矩阵(Projection)。裁剪空间由视锥体决定。
视锥体是空间中的一块区域,视锥体内的区域是摄像机可以看到的空间。视锥体由六个平面构成,这些平面称为裁剪平面。视锥体有两种类型,透视投影和正交投影。
在视锥体的裁剪平面中有两块很特殊,分别称为近裁剪平面和远裁剪平面,它们决定了摄像机可以看到的深度范围。
投影矩阵有两个目的:
我们利用上图的参数可以计算出近裁剪平面和远裁剪平面的高度:
裁剪平面的宽度我们可以通过横纵比得到。假设摄像机的横纵比为 :
根据上述信息,投影矩阵为:
使用上述投影矩阵后:
可以看到,投影矩阵本质上是对x、y、z坐标进行一些缩放,同时z分量也进行了平移,缩放的目的是为了方便裁剪。此时w分量不再是1,而是-z,通过这个w,我们可以判断一个顶点是否在视锥体内,在的话必须满足下面条件:
透视投影后,空间从右手坐标系变为左手坐标系。
假设横纵比为 ,那么:
正交投影矩阵:
使用正交投影矩阵后:
使用正交投影后,w分量仍是1。
这一步的变换,我们会得到真正的像素位置。
将顶点从裁剪空间投影到屏幕空间有两个步骤:首先进行透视除法,用x、y、z分量除以w分量。这一步得到的坐标被称为NDC,经过透视除法的裁剪空间变换到一个立方体内:
对于正交投影没什么区别。
在Unity中,屏幕空间左下角的像素坐标是(0,0)。接下来就是将NDC缩放值屏幕坐标系。
透视除法和屏幕映射的过程总结如下:
法线是一个方向矢量,垂直于表面,与其垂直的一个矢量被称为切线。
切线由两个顶点之间的差值计算得来,因此可以使用变换顶点的变换矩阵来变换切线。假设不考虑平移变换,变换后的切线为:
不过,如何直接这么变换的话,法线就有可能不垂直于表面了:
下面我们可以来推导一下正确的法线变换矩阵。
首先,切线与法线垂直,即 ,假设变换法线的矩阵为 ,变换后法线仍与切线垂直,那么:
经推导得:
由于 ,如果 ,则上式成立,即 。
如果变换矩阵 是正交矩阵,那么G = M_A->B,当然,这限于只包含旋转变换。如果只包含旋转和统一缩放,那么 。
内置变量: Built-in shader variables
内置方法: Built-in shader helper functions
以上是关于Unity Shader:相关数学基础的主要内容,如果未能解决你的问题,请参考以下文章