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:相关数学基础的主要内容,如果未能解决你的问题,请参考以下文章

渲染基础概念 unity shader 材质相关学习

渲染基础概念 unity shader 材质相关学习

Unity Shader:相关数学基础

Unity Shader基础

Unity Shader基础

Unity Shader初识Shader,基础总结!