MVP矩阵代表什么
MVP矩阵分别是模型(Model)、观察(View)、投影(Projection)三个矩阵。
我们的顶点坐标起始于局部空间(Local Space),在这里他成为局部坐标(Local Coordinates),它在之后会变为世界坐标(World Coordinates),观察坐标(View Coordinates),裁剪坐标(Clip Coordinates),并最后以屏幕坐标(Screen Coordinates)的形式结束。过程如下图所示
-------------------------------------------参考《Unity Shader入门精要》----------------------------------------
模型空间
模型空间(Model Space),或者说是对象空间(Object Space)、局部空间(Local Space)。
模型空间原点和坐标轴通常是由美术人员在建模软件里确定好的,原点通常位于模型的中心。
世界空间
世界空间(World Space)是一个特殊的坐标系,因为他建立了我们所关心的最大空间。通常,我们会把世界空间的原点放置在游戏空间的中心。
将顶点坐标从模型空间变换到世界空间中,这个变换通常叫做模型变换。
根据Transform组件上的信息,模型进行了(2,2,2)的缩放,又进行了(0,150,0)的旋转,以及(5,0,25)的平移。注意:这里的变换顺序不能互换。要按照先缩放,再旋转,最后平移,矩阵从右到左依次是缩放——旋转——位移矩阵。因此可以构建出模型变换的变换矩阵:
现在可以对目标物体进行模型变换了:
Pworld:世界空间下的目标点坐标。
Mmodel:模型变换矩阵。
Pmodel:模型空间下的目标点坐标。
也就是说世界空间下目标点位置是(9,4,18.072),注意这里的浮点数都是近似值。实际数值和Unity采用的浮点精度有关。
观察空间
观察空间(View Space)也被称为摄像机空间(Camera Space)。观察空间可以认为是模型空间的一个特例——在所有的模型中一个非常特殊的模型,即摄像机。
在Unity中的观察空间坐标轴选择是:+x轴指向右方,+y指向上方,+z指向的是摄像机后方。这里+z与模型空间和世界空间的+z相反,因为,Unity在模型空间和世界空间选用的是左手坐标系,观察空间使用的是右手坐标系。这是符合OpenGL传统的。
从世界空间变换到观察空间的过程叫做观察变换(View Transform)。
得到顶点在观察空间中的位置有两种方法:
①计算观察空间三个坐标在世界空间下的标识,然后构建出从观察空间到世界空间的变换矩阵,再对该矩阵求逆来得到从世界空间变换到观察空间的变换矩阵。
②想象平移整个观察空间,让摄像机原点位于世界坐标的原点,坐标轴与世界空间中的坐标轴重合即可。
采用第二种方法来计算变换。相机在世界空间中经过缩放->旋转->平移的到最终位置,所以逆变换需要以平移->旋转->缩放的顺序来计算。
由于观察空间是右手坐标系,所以要对z分量进行取反操作。
最后对顶点进行变换。
得到观察空间中目标顶点中的位置——(9, 8.84, -27.31)
裁剪空间
裁剪空间(Clip Space)也被成为齐次裁剪空间,这个用于变换的矩阵叫做裁剪矩阵(Clip Matrix),也被成为投影矩阵(Projection Matrix)。
裁剪空间的裁剪范围由视锥体(View Frustum)来决定,而视锥体又有两种类型,这涉及两种投影:一种是正交投影(Orthographic Projection),另一种是透视投影(Perspective Projection)。
在视锥体6块裁剪平面中,有两块裁剪平面比较特殊,分别是近剪裁平面(Near Clip Plane)和远剪裁平面(Far Clip Plane)。它们决定了摄像机可以看到的深度范围。
投影矩阵将顶点转换到裁剪空间中,其目的有二:
-
- 首先是为投影做准备。这是个迷惑点,虽然投影矩阵的名称包含了投影二字,但它并没有真正进行投影工作,而是再为投影做准备。真正的投影发生在后面的齐次除法(Homogeneous Division)过程中。而经过投影矩阵的变换后,顶点的w分量会具有特殊意义。
- 其次是对x、y、z分量进行缩放。直接使用视锥体的6个才见平面来进行裁剪会比较麻烦。而经过投影矩阵缩放后,我们可以直接使用w分量座位一个范围值,如果x、y、z分量都位于这个范围内,就说明该顶点位于裁剪空间内。
透视投影
视锥体是由6个裁剪面所构成的,在Unity中,这6个裁剪平面则是由Camera组件中的参数和Game视图的横纵比共同决定。
由图看出,可以通过Camera组件的Field of View(简称FOV)属性来改变视锥体竖直方向的张开角度,而Clipping Planes中的Near和Far参数可以控制视锥体的近裁剪平面和远裁剪平面距离相机的远近。这样就可以求出视锥体近裁剪平面和远裁剪平面的高度,即:
现在还缺乏横向的信息。可以通过摄像机的横纵比得到。在Unity中,一个摄像机的横纵比由Game视图的横纵比和Viewport Rect中的W和H属性共同决定。假设前摄像机的横纵比为Aspect,则可以定义:
现在,我们可以根据已知的Near、Far、FOV和Aspect的值来确定透视投影的投影矩阵(P矩阵)。
一个顶点和上述P矩阵相乘后,可以有观察空间变换到裁剪空间中:
从结果看,这个投影矩阵本质就是对x、y和z分量进行不同程度的缩放(z分量还做了一个平移),缩放的目的是为了方便裁剪。注意,此时w分量不再是1,而是原先z分量的取反结果。现在就可以按如下不等式判断一个变换后的顶点是否位于视锥体内。如果一个顶点在视锥体内,那么他变换后的坐标必须满足:
任何不满足上述条件的图元都需要被剔除或者裁剪。
再见矩阵会改变空间转向性:空间从右手坐标系变换到了左手坐标系。这意味着离摄像机越远,z值越大。
正交投影
正交投影中的6个裁剪平面也是有Camera组件中的参数和Game视图的横纵比共同决定的。
正交投影的视锥体是一个长方形,因此计算上相比透视投影来说更加简单。由图可以看出,可以通过Camera组件的Size属性来改变视锥体竖直方向上高度的一般,而Clipping Planes中的Near和Far参数可以控制视锥体的金裁剪平面和远裁剪平面的远近。所以可以求出近远才见平面的高度:
设摄像机的横纵比为Aspect那么:
得到Near、Far、Size和Aspect的值,就可以确定正交投影的投影矩阵,如下:
同样,这里的投影矩阵是建立在Unity对坐标系的假定上面的。
一个顶点和上述投影矩阵相乘后的结果如下:
注意到,和透视投影不同的是,使用正交投影的投影矩阵对顶点进行变换后,w分量仍然为1。
本质:因为投影矩阵最后一行的不同,透视投影的投影矩阵的最后一行是【0 0 -1 0】,而正交投影矩阵的最后一行是【0 0 0 1】。这样做是为了在齐次除法上做准备。下一小节说明。
判断一个变换后的顶点是否为视锥体内使用的不等式和透视投影中的一样,这种通用性也是为什么要是用投影矩阵的原因之一。
屏幕空间
经过投影矩阵的变换后,就可以进行裁剪操作。当完成了所有裁剪工作后,就需要进行真正的投影了,也就是说,我们需要把视锥体投影到屏幕空间(Screen Space)。经过这一步变换,就会得到真正的像素位置,而不是虚拟的三维坐标。
屏幕空间是一个二维空间,因此,必须把顶点从裁剪空间投影到屏幕空间中,来生成对应的2D坐标。这个过程可以理解为两个步骤:
首先,需要进行标准齐次除法(Homogeneous Division),也被成为透视除法(Perspective Division)。实际上就是用齐次坐标系的w分量去除x、y、z分量。在OpenGL中,这一步得到的坐标叫做归一化设备坐标(Normalized Device Coordinates,NDC)。经过这一步可以把坐标从齐次裁剪坐标空间转换到NDC中。经过透视投影变幻后的裁剪空间,经过齐次除法后会变换到一个立方体内。在OpenGL的传统,这个立方体的x、y、z分量的范围都是【-1,1】。在DirectX中,z的分量范围是【0,1】。Unity则选了OpenGL这样的齐次裁剪空间。
对于正交投影来说,他的裁剪空间实际上已经是一个立方体了,w分量是1,所以齐次除法不会对顶点x、y、z产生影响。
经过齐次除法后,透视、正交投影的视锥体都变换到一个相同的立方体内。现在就可以根据变换后的x和y坐标来映射输出窗口的对应像素坐标(pixelWidth,pixelHeight)。
在Unity中,屏幕空间左下角的像素做标识(0,0),右上角像素坐标是(pixelWidth,pixelHeight)。由于当前x和y坐标都是【-1,1】,因此这个映射过程就是一个缩放过程。
齐次除法和屏幕映射的过程可以使用下面的公式来总结:
上面的式子对x和y分量都进行了处理,z分量一般会被用于深度缓冲。一个传统的方式是把
的值直接存进深度缓冲中,但这不是必须的。通常驱动生产商会根据硬件来选择最好的存储方式。此时clipw也并不会被抛弃,他回来后续一些工作中起到重要作用,例如进行透视校正差值。
验证过程可查看《UnityShader入门精要》4.6.8
世界空间的应用
不规则平面的Tilling:以世界坐标当做uv进行采样。
※P矩阵推导过程
--------------------------------------------------参考Games101 P4----------------------------------------------------
链接Lecture 04 Transformation Cont._哔哩哔哩_bilibili 时间轴:43:00
推导前定义的环境:
【camera】
- camera再原点
- 朝向-Z方向
- Y轴为向上方向
正交投影
过程理解:
-
-
- t、r、n、I、b、f是立方体的上下左右前后的范围。
- 将包围盒经过平移->缩放(暂时不考虑旋转),映射到最右边一个标准的立方体上。
- 由于是看向Z轴负方向,所以[f,n],远(f)的点数值小于近(n)的点
- 右手坐标系来看
-
-
- 首先定义了立方体:y轴方向:t&b(上下);x轴方向:l&r(左右);z轴方向n&f(近远)此处n的数值>f(看向-Z方向,n离得近)
- 目的:
-
-
- 把给定的定义好的立方体 映射到[-1,1]3标准立方体
- 把左边的3个坐标轴分开考虑
- x轴:(其他两个轴同理)
- 写出x的范围表达式 → 重点:①映射到[-1,1]范围,②再写成线性表达式(ax+b)a就是缩放矩阵,b就是平移矩阵
-
过程理解:
-
-
- 从右往左看,先看位移矩阵。
- 如果是正常移出的话就是正的,将物体移回原点则前面加个负号。
- (r+l)/ 2则是将对应轴移到原点上,其他轴坐标同理。
- 2/(r-l),因为(r-l)是两点之间的距离,假如覆盖范围为1的话则可以得到(r-l)* x = 1,现在-1到+1区间是覆盖范围是2,所以的(r-l)* x = 2,所以得到x= 2/(r-l)
-
得到:Mortho=
透视投影
透视投影可以理解为将下图左边的平截头体通过“挤压”其右侧大的面,“挤压”成一个标准的立方体,例如图右的标准立方体。这样后续再求透视投影矩阵直接带入正交投影矩阵里就可以了。(因为正交投影矩阵相对来说计算简单)。
依旧是按右手坐标系来算
挤压后的特性:
-
- 近平面上所有点不会变化
- 远平面上所有点的z轴坐标不会发生变化
- 远平面上的中心点任何坐标都不会变化
目的:
-
- 先计算 透视到正交的投影矩阵。
- 再计算投影矩阵的到最终的投影矩阵。
假设从箭头方向看标记的两个点则横截面图如下:
由上图可知,根据相似三角形的特性,可以得到图右的公式。同理可以得到
想要一个挤压前的点转换到挤压后的点坐标,公式可以这样写:
因为Z前后关系不清楚,用unknown代替。
=>新概念引入
齐次坐标下的一个点(x,y,z,1)同时乘以一个常量k!=0,点不变,当k为z时具有同样的特性。
由此特性可以将矩阵中数全部乘以z得到
这样就可以知道:
所以可以知道此投影矩阵一部分数值:
此时回到开始定义的挤压后的特性:
近平面上所有点不会变化。
远平面上所有点的z轴坐标不会发生变化
则可以把矩阵中的z全部转换为n。所以挤压前的点=>挤压后的点
又根据齐次坐标系的性质,同时乘以一个数,点不变,则得到
所以由矩阵的第三行(0 0 A B)乘以得到的矩阵
(因为最后的到的n平方和x、y没有关系,因为是在近平面上计算的,所以前两个数值为0)
上面所计算的可以得到一下线性方程:
此时用到最后一个特性:远平面上的中心点任何坐标都不会变化。
要与上面点的映射一值所以这里需要同时乘以f。
计算所得两个线性方程:
带入可以得到透视到正交的投影矩阵:
Mpersp->ortho=
得到最终的透视矩阵则计算:
Mpersp=MorthoMpersp->ortho
又由于此为标准立方体,则可以得到t = -b,l = -r,且2t得到的是立方体高度h,2l得到的是立方体宽度w,h、w同时也是相机视口高宽,带入上式可化简为:
再次根据下图可以求得w、h、和FOV之间的关系。