Shader所需的数学基础--坐标空间注意

Shader所需的数学基础--坐标空间注意

注:图片来源基本来自作者冯乐乐的GitHub。 感谢作者的分享。

矩阵可以表示基本变换:平移、旋转和缩放

使用矩阵变换坐标空间:

1、顶点着色器的基本功能是将模型的顶点坐标从模型空间转换到齐次裁剪坐标空间(前三个顶点变换过程)。

2. 片段着色器还可以获得片段在屏幕空间中的像素位置。

在Unity中,坐标系的旋向性也随着变换而变化:(注:Unity只在观察空间使用右手坐标系,其他都使用左手坐标系)

渲染游戏的过程实际上就是通过逐层处理将每个顶点转换为屏幕上的顶点的过程。

(顶点从模型空间变换到屏幕坐标的过程)

为什么渲染时使用这么多不同的坐标空间:

不同的情况需要使用不同的坐标空间,因为有些概念只有在特定的坐标空间中才有意义并且更容易理解。

在编写着色器的过程中,使用了许多看似难以理解且复杂的数学运算来在不同坐标空间之间进行点和向量的转换。

所有坐标空间理论上都是相等的,计算不会因为从一种坐标空间转换到另一种坐标空间而出错。

在游戏渲染管线中,将顶点或方向向量从一个坐标空间转换到另一个坐标空间的过程:

要定义坐标空间,必须指定其原点位置及其三个坐标轴的方向。 这些值实际上是相对于另一个坐标空间的(都是相对的)。 坐标空间会形成一个层次结构,这个层次结构中的每个坐标空间都是另一个坐标空间的子空间(即每个空间都有一个父坐标空间)。

坐标空间变换实际上就是父空间和子空间之间点和向量的变换。

父坐标空间P

子坐标空间C

1、将子坐标空间中表示的点或向量转换为父坐标空间中的表示

2、将父坐标空间中表示的点或向量转换为子坐标空间中的表示

如何求子坐标空间到父坐标空间的变换矩阵M(C->P):

推导过程:

1、从坐标空间原点出发,子坐标空间原点位置:

2.x轴的向量表示:

那么x轴方向移动a个单位的表达式为:

然后沿y轴方向移动b个单位:

最后,向 z 轴移动 c 个单位:

最终表示为:

3. 求解公式

因此,最终得到子坐标空间到父坐标空间的变换矩阵M(C->P):

它是通过坐标空间P中坐标空间C的原点和坐标轴的向量表示来构造的:将三个坐标轴放入矩阵的前三列,将原点向量放入最后一列,并填充最后一行0 和 1

因为从坐标空间C到坐标空间P的变换和从坐标空间P到坐标空间C的变换是互逆的。

因此,通过求逆矩阵,我们可以得到父坐标空间到子坐标空间的变换矩阵M(P->C):

用逆向思维从这个变换矩阵推断出子坐标空间的原点和坐标轴方向:

例如,当已知从模型空间到世界空间的 4×4 变换矩阵 M(C->P)

1.获得模型x轴在世界空间中的单位向量表示:可以提取这个矩阵的第一列,然后对其进行归一化(归一化可以消除缩放的影响)

2.获得模型y轴在世界空间中的单位向量表示:可以提取这个矩阵的第二列并对其进行归一化(归一化可以消除缩放的影响)

3.获得模型z轴在世界空间中的单位向量表示:可以提取这个矩阵的第三列并对其进行归一化(归一化可以消除缩放的影响)

已知矩阵M(C->P)可以将一个方向向量从坐标空间C变换到坐标空间P,那么只需要用这个矩阵变换x轴(1,0,0,0)即可在坐标空间 C 中,即矩阵乘法:

得到的结果是矩阵M(C->P)的第一列

方向向量的坐标空间变换:

由于向量没有位置,因此可以忽略坐标空间的原点变换。

因此,简单地平移坐标系原点不会对矢量产生任何影响。

由于不需要表达平移变换,向量的坐标空间变换可以用3×3矩阵来表达:

因此,在Shader中,截取变换矩阵的前3行和前3列,就可以对法线方向和光照方向进行空间变换。

注意:如果是正交矩阵,那么不需要求解逆矩阵即可得到父坐标空间到子坐标空间的变换矩阵,因为正交矩阵的逆矩阵就是它的转置矩阵3D交通工具,这意味着不需要执行复杂的逆变换操作就可以获得逆变换。 直接求转置矩阵即可:

如果空间变换矩阵M(C->P)是正交矩阵,则可以为:

例如,对于 x 轴:

1、提取这个正交矩阵的第一列,得到坐标空间C的x轴在坐标空间P中的表示,

2、提取该正交矩阵的第一行,得到坐标空间P的x轴在坐标空间C中的表示。

反之,如果知道坐标空间P的x轴、y轴和z轴(必须是单位向量,否则构造的矩阵将不是正交矩阵)在坐标空间C中的表示,则可以将这些坐标轴依次放入矩阵的每一行,即可得到从C到P的变换矩阵。

顶点的坐标空间变换过程:

在渲染管线中,一个顶点在最终显示在屏幕上之前必须经过多次坐标变换。

顶点最初在模型空间中定义,最后转换到屏幕空间中以获得真实的屏幕像素坐标。

模型空间:(模型空间)

与模型(对象)相关,模型空间也称为对象空间或局部空间

每个模型都有自己独立的坐标空间。 当它移动或旋转时,模型空间也会随之移动和旋转。

方向(自然方向):前、后、左、右

Unity在模型空间中使用左手坐标系

模型空间的原点和坐标通常由艺术家在建模软件中确定。 导入到Unity后,可以在顶点着色器中访问模型的顶点信息,其中包含每个顶点的坐标。 这些坐标是相对于模型空间中的原点(通常位于模型的重心)定义的。

世界空间:(世界空间)

特殊的坐标系,这个空间是开发者关心的最外层的坐标空间。

世界空间可以用来描述绝对位置,指的是在世界坐标系中的位置。

通常世界空间的原点放置在游戏空间的中心

在Unity中,世界空间也使用左手坐标系。 但它的x轴、y轴、z轴是固定的。

调整Unity中Transform组件的Position属性,可以改变模型的位置(这个位置是指模型坐标空间中定义的原点相对于Transform的父节点)

1、模型变换:

顶点变换的第一步是将顶点坐标从模型空间变换到世界空间。

农场游戏中的世界空间。 世界空间的原点位于农场的中心。 左下角展示了牛牛在世界空间中所做的变换。 我们要把妞妞的鼻子从模型空间变换到世界空间。

先进行(2,2,2)的缩放,然后进行(0,150,0)的旋转,最后进行(5,0,25)的平移。

注意:这里变换的顺序是不能互换的,即先缩放,再旋转,最后平移。

构造模型变换的变换矩阵(从右到左):

使用这个变换矩阵对模型的鼻子进行模型变换。 鼻子的模型坐标为(0,2,4):

最后,获得模型鼻子在世界空间中的位置(9,4,18.072)。 浮点数是一个近似值(大约为小数点后 3 位)。 实际值与Unity使用的浮点值的精度有关。

观察空间:(相机空间观察空间)

即:相机的模型空间

相机决定用于渲染游戏的视角,相机位于观看空间的原点。

Unity中观察空间的坐标轴选择:+x轴指向右侧,+y轴指向上方,+z轴指向相机后方。

注意:Unity在模型空间和世界空间中使用左手坐标系,而在观察空间中使用右手坐标系。

因为这符合OpenGL规范,所以在这样的观察空间中,相机的正面指向-z轴的方向。

如果调用Camera.cameraToWorldMatrix、Camera.worldToCameraMatrix等接口来计算模型在观察空间中的位置,需要注意其中的差异。

注意:观察空间和屏幕空间是不同的

观察空间:三维

屏幕空间:2D

从观察空间到屏幕空间需要一个转换操作:投影

2.视图变换:

将顶点坐标从世界空间变换到观察空间,顶点变换的第二步,

从世界空间变换到观察空间:需要知道相机在世界坐标系下的交换信息

农场游戏中相机的观看空间。 观察空间的原点在相机处。 注意,在观察空间中,相机的前进方向为z轴负方向(图中只画出了z轴正方向)。 这是因为Unity在观察空间中使用右手坐标系。 左下角展示的是相机在世界空间中所做的变换。我们要把妞妞的鼻子从世界空间变换到观察空间

获取顶点在观察空间中的位置的两种方式:(得到的变换矩阵是一样的,只是思维方式不同)

方法一:计算观察空间的三个坐标轴在世界空间的表示,然后构造观察空间到世界空间的变换矩阵,然后对矩阵求逆得到世界空间到世界空间的变换矩阵观察空间。

方法二:想象平移整个观察空间,使相机原点位于世界坐标原点,坐标轴与世界空间中的坐标轴重合。

相机变换可以从相机Transform组件获得:先根据(30,0,0)旋转,然后根据(0,10,-10)平移。

进行逆变换,将相机移回到初始状态(相机原点位于世界坐标原点橙光游戏,坐标轴与世界空间中的坐标轴重合)

先按(0,-10,10)平移,再按(-30,0,0)旋转,最后坐标轴重合。 得到变换矩阵:

由于观察空间使用右手坐标系,因此需要反转z分量。

最后可以利用得到的观测变换矩阵对模型进行顶点变换:

获取模型在观察空间中的位置:(9, 8.84, -27.31)

3. 将观察空间转换为裁剪空间

裁剪空间:(齐次裁剪空间裁剪空间)

使用裁剪矩阵(投影矩阵)从观察空间变换到裁剪空间。

目标:促进渲染基元的裁剪。 完全位于该空间内的图元将被保留用于渲染,完全位于该空间之外的图元将被剔除,与该空间边界相交的图元将被剪裁。

1. 透视投影:

查看视锥体平面设置:

Camera 组件的 Field of View (FOV) 属性:更改视锥体的垂直张开角度

剪裁平面:近和远参数控制视锥体与相机的近剪裁平面和远剪裁平面之间的距离。

求视锥体的近剪裁平面和远剪裁平面的高度:

水平信息:通过相机的长宽比获得。

Unity 中相机的长宽比由游戏视图的长宽比以及视口矩形中的 W 和 H 属性决定。

假设当前相机的长宽比为Aspect,定义:

确定透视投影的投影矩阵:(对于观察空间是右手坐标系,用列矩阵在矩阵右侧相乘,变换后的z分量范围在[-w, w之间],如果是DirectX等图形界面,那么变换后的z分量范围必须在[0, w]之间,需要修改投影矩阵)

在透视投影中,顶点乘以投影矩阵后可以从观察空间变换到裁剪空间:

投影矩阵的本质就是对x、y、z分量进行不同程度的缩放(z分量也进行了平移)

缩放的目的是为了方便裁剪

此时w分量不再是1,而是原来z分量的反转。

根据以下不等式判断变换后的顶点是否位于视锥体内。 如果顶点位于视锥体内,则变换后的坐标必须满足:

任何不满足此条件的图元都需要被消除或裁剪

在透视投影中,投影矩阵缩放顶点。 4个关键点经过投影矩阵变换后的结果。从这些结果我们可以看到x、y、z、w分量范围的变化

裁剪矩阵改变了空间惯用手:空间从右手坐标系变换到左手坐标系。距离相机越远,z值越大

2. 正交投影:

正交投影的6个裁剪平面的定义:

由Camera组件中的参数和Game视图的长宽比决定

长方体。

通过Camera组件的size属性将视锥体的高度在垂直方向改变一半。

剪裁平面中的近和远参数控制视锥体距相机的近剪裁平面和远剪裁平面之间的距离。

求视锥体的近剪裁平面和远剪裁平面的高度:

水平信息:通过相机的长宽比获得。

假设当前相机的长宽比为Aspect,定义:

正交投影的投影矩阵:(对于观测空间是右手坐标系,用列矩阵在矩阵右侧相乘,变换后的z分量范围在[-w,w]之间,如果是DirectX等图形界面,那么变换后的z分量范围必须在[0, w]之间,需要修改投影矩阵)

在正交投影中,顶点乘以投影矩阵后可以从观察空间变换到裁剪空间:

注意:正交投影的投影矩阵对顶点进行变换后,其w分量仍然为1。(与透视投影不同)

本质原因是投影矩阵最后一行不同,为齐次划分做准备。

透视投影的投影矩阵的最后一行是 [0 0 -1 0]

正交投影的投影矩阵的最后一行是 [0 0 0 1]

根据以下不等式判断变换后的顶点是否位于视锥体内。 如果顶点位于视锥体内,则变换后的坐标必须满足:(与透视投影相同)

任何不满足此条件的图元都需要被消除或裁剪

在正交投影中,投影矩阵缩放顶点。 4个关键点经过投影矩阵变换后的结果。从这些结果我们可以看到x、y、z、w分量范围的变化

裁剪矩阵改变了空间惯用手:空间从右手坐标系变换到左手坐标系。距离相机越远,z值越大

正交投影变换后的顶点实际上位于立方体内。

视锥体决定了空间(相机可以看到的空间)的裁剪。

视锥体:被六个剪切平面包围。 视锥体的意义在于定义场景中的三维空间。

两种类型:

1.正投影

所有网格的大小相同,平行线始终保持平行

保持物体的距离和角度

长方体

2. 透视投影

离相机越近,网格越大unity 投影矩阵,离相机越远,网格越小。

通过模拟人眼看世界

金字塔形状

侧面的四个剪裁平面将在相机处相交

特殊剪裁平面:近剪裁平面和远剪裁平面。 确定相机看到的深度范围。

如果直接利用视锥体定义的空间进行裁剪,那么不同的视锥体需要不同的处理过程,而且对于透视投影视锥体来说,判断一个顶点是否在金字塔内部比较麻烦。 。 因此,通过投影矩阵将顶点转换为裁剪空间。 (更通用、方便、整洁的裁剪方式)

投影裁剪的目的:

1. 准备投影。

注意:

投影矩阵的名字虽然含有投影二字,但它并不进行实际的投影工作,而是为原地投影做准备。

真正的投影发生在齐次除法过程的后期。

投影矩阵变换后,顶点的w分量将具有特殊的含义。

投影:空间降维。 四维空间投影到三维空间。 投影矩阵实际上并不进行空间降维,而是为实际的投影做准备。 真正的投影发生在屏幕映射过程中,其中二维坐标是通过齐次划分获得的。

2. 缩放 x、y 和 z 分量。 直接使用视锥体的六个裁剪面进行裁剪会比较麻烦。 然而,缩放投影矩阵后,可以直接使用w分量作为范围值。 如果x、y、z分量都在这个范围内,那么就意味着这个顶点在裁剪空间内。

裁剪空间以前使用齐次坐标来表示点和向量。 此时w分量是固定的,该点的w分量为1,方向向量的w分量为0。

将矩阵投影后,齐次坐标的w分量将具有更丰富的含义。

已知使用透视相机,模型在观察空间中的位置(9,8.84,-27.31)unity 投影矩阵,透视投影的参数:FOV为60°,Near为5,Far为40,Aspect为4/3 = 1.333 。 得到对应的投影矩阵:

使用投影矩阵将模型从观察空间变换到裁剪空间

获取模型在裁剪空间中的位置 - (11.691, 15.311, 23.692, 27.31)

得到模型在裁剪空间中的位置后,Unity会通过比较公式来判断模型是否需要裁剪:

结论:模型在视锥体内,不需要裁剪。

3. 将裁剪空间转换为屏幕空间

将视锥体投影到屏幕空间(Screen space),得到变换后的真实像素位置,而不是虚拟的三维坐标。

屏幕空间:二维空间

顶点必须从裁剪空间投影到屏幕空间以生成相应的 2D 坐标。

执行标准齐次除法(透视除法):将齐次坐标系的 x、y 和 z 分量除以 w 分量。

在OpenGL中,这一步之后得到的坐标称为标准化设备坐标(NDC)。

坐标从齐次裁剪坐标空间转换为 NDC。 转变为立方体。

OpenGL:该立方体的 x、y 和 z 分量均在 [-1, 1] 范围内。

DirectX:z分量的范围是[0, 1]。

齐次分割后,透视投影的裁剪空间将转化为立方体

齐次分割后,正交投影的裁剪空间将转化为立方体

齐次除法不会影响正交投影:因为正交投影的裁剪空间实际上是一个立方体,并且由于正交投影矩阵变换后顶点的w分量为1,所以齐次除法不会影响x、y和顶点的 z 坐标。

均匀分割后,透视投影和正交投影的视锥体都变换为同一个立方体。

变换为立方体后,可以根据变换后的x、y坐标映射输出窗口对应的像素坐标。

在Unity中,屏幕空间左下角像素的坐标为(0, 0),右上角像素的坐标为(pixelWidth, PixelHeight)。 由于当前的x、y坐标为[-1, 1],所以这个映射过程就是一个缩放过程​​。

齐次划分和屏幕映射的过程用以下公式表示:

z 分量用于深度缓冲区:

直接存储到深度缓冲区中。 (此操作不是必须的)驱动制造商会根据硬件选择最佳的存储格式。

主要工作在这里完成:将NDC作为齐次除法的分母

稍后它还将用于透视校正插值。

注意:在 Unity 中,从剪辑空间到屏幕空间的转换是在后台完成的。 顶点着色器只需将顶点变换到剪辑空间即可。

例如:模型在裁剪空间中的位置为(11.691,15.311,23.692,27.31),获取屏幕上的像素位置。

假设当前屏幕的像素宽度为 400,高度为 300。

1.首先进行齐次划分,将裁剪空间的坐标投影到NDC中。

2.然后将其映射到屏幕空间

最终得到模型在屏幕上的位置:(285.617, 234.096)

文章来源:https://blog.csdn.net/xzw734173384/article/details/90291789