坐标空间的变换
需要定义一个坐标空间,必须原点+三个坐标轴的方向,而这些定义的数值都是相对于另一个坐标空间的。
每一个坐标空间都是另一个坐标空间的子空间——每一个空间都有一个父坐标空间,对坐标空间的变换实际上就是在父子空间之间对点和矢量进行变换。
父坐标空间P+子坐标空间C+子坐标空间在父坐标空间下的原点位置Oc以及三个单位坐标轴Xc、Yc、Zc。
常见需求:
①将处于子坐标空间下的点或矢量Ac转换到父坐标空间下的表示Ap:Ap=Mc➡pAc。
②将处于父坐标空间下的点或矢量Bp转换到子坐标空间下的表示Bc:Bc=Mp➡cBp。
Mc➡p代表从子空间变换到父空间的变换举证,Mp➡c是其逆矩阵。
求解Mc➡p
已知:子坐标空间在父坐标空间下的原点位置Oc以及三个单位坐标轴Xc、Yc、Zc,子坐标空间中的一个点Ac=(a,b,c),通过变换矩阵Mc➡p获得Ap。
Ap=Oc+aXc+bYc+cZc
当Mc➡p是正交矩阵时可通过转置获得其逆矩阵即Mp➡c。
当知道Mc➡p是一个正交矩阵的时候,它的第一列代表坐标空间c的x轴在坐标空间p中的表示,它的第一行代表坐标空间p的x轴在坐标空间x中的表示。(已知子到父,列为子在父,行为父在子,即列表示的方向=变换方向)。
如果知道了坐标空间B的三个坐标轴(必须是单位矢量——保证正交性)在空间A下的表示,按行放即得到A到B的变换矩阵。(已知某在某,按行放得到反向变换矩阵,按列放得到正向变换矩阵)。
模型空间(model space)
也被称为对象空间(object space)或局部空间(local space)。unity在模型空间中采用左手坐标系,+x、+y、+z对应自然方向的右、上、前向(常用不是必须)。顶点着色器中访问到的模型的顶点信息中,顶点坐标是相对于模型空间中的原点的。模型空间随模型变换。
世界空间(world space)
世界空间用来描述游戏中的绝对位置,同样使用左手坐标系,但是xyz轴是固定不变的。调节transform组件中的position改变模型是相对于该模型的父节点的模型坐标空间,若没有父节点则是相对于世界坐标空间。
顶点变换的第一步:模型空间->世界空间
这个变换通常被称为模型变换(model transform),根据模型的transform组件中的信息即可构造变换矩阵。缩放-》旋转-》平移。
观察空间(view space)
也被称为摄像机空间(camera space),观察空间中,摄像机位于原点,其坐标轴的选择可以是任意的,在unity中,观察空间的坐标轴选择是:+x——右方,+y指向上方,+z指向摄像机的后方(采用的是右手坐标系,即摄像机正前方指向的是-z轴方向)。
顶点变换的第二步:世界空间->观察空间
这个变换通常被称为观察变化(view transform),要得到摄像机的transform组件信息,想象摄像机原点与世界原点重合,坐标轴与世界中的也重合,就可以完成这个变换,这样对相机transform的信息进行逆变换。平移-》旋转。同时由于使用的坐标系不同,z分量需要进行取反操作,才能得到最终的变换矩阵。
裁剪空间(clip space)
也被叫做齐次裁剪空间,这个被用于变换的矩阵叫做裁剪矩阵(clip matrix)/投影矩阵(projection matrix)。视锥体(view frustum)决定保留的图元部分。视锥体由裁剪平面构成(6面clip planes),视锥体有两种类型:正交投影(orthographic projection-保留距离和角度)/透视投影(perspective projection-模拟真实人眼看到的)。
通过使用投影矩阵将顶点变换到裁剪空间中以简化裁减的工作。需要注意投影矩阵并没有进行投影变换。经过投影矩阵的变换后,顶点的w分量会具有特殊的意义。主要完成对xyz分量的缩放,进行缩放后,可直接使用w分量作为范围判断是否xyz分量都处于范围内来判断是否在裁剪空间中。
透视投影中的投影矩阵
已知相机参数中的Field of View(FOV)代表锥体竖直方向(FOV Axis)的张开角度,Near和Far代表近裁剪平面和远裁剪平面距离相机的远近
可得到远近剪裁平面的高度:nearClipPlaneHeight=2·Near·tan ( FOV / 2 ) 、farClipPlaneHeight=2·Far·tan ( FOV / 2 ):
参数Viewport Rect中的W和H属性决定纵横比Aspect:Aspect=nearClipPlaneWidth/nearClipPlaneHeight=farClipPlaneWidth/farClipPlaneHeight=宽/高。
需要注意,在unity中该透视投影建立在观察空间为右手坐标系、使用列矩阵在矩阵右侧进行相乘、变换后z分量将在[-w,w]之间的情况,可通过Near、Far、FOV、Aspect可以确定透视投影的裁剪矩阵为:
这个投影矩阵的本质就是对xyz分量进行了不同程度的缩放(z分量还做了平移),同时,顶点的w分量不再是1,而变换成为-z,根据变换后的新顶点,判断xyz分量是否处于[-w,w]范围内即可。同时需要注意裁剪矩阵改变了旋向性:空间从观察空间的右手坐标系改为裁剪空间的左手坐标系,即在裁剪空间下,z值越大代表离摄像机越远。
正交投影中的投影矩阵
已知Size属性代表视锥体竖直方向上高度的一半,Near和Far参数含义不变
可得到远近剪裁平面的高度:nearClipPlaneHeight=2·Size、farClipPlaneHeight=nearClipPlaneHeight:
参数Viewport Rect中的W和H属性决定纵横比Aspect:nearClipPlaneWidth=Aspect · nearClipPlaneHeight=farClipPlaneWidth。
需要注意,在unity中该透视投影建立在观察空间为右手坐标系、使用列矩阵在矩阵右侧进行相乘、变换后z分量将在[-w,w]之间的情况,通过Near、Far、Size、Aspect可以确定正交投影的裁剪矩阵为:
和透视投影区别在于,w分量变换后还是1(为了齐次除法准备),根据变换后的新顶点,判断xyz分量是否处于[-w,w]即[-1,1]范围内即可。同时需要注意裁剪矩阵也改变了旋向性:空间从观察空间的右手坐标系改为裁剪空间的左手坐标系,即在裁剪空间下,z值越大代表离摄像机越远。
屏幕空间(screen space)
经过投影矩阵的变换后,可进行裁剪工作,完成裁剪工作后开始真正意义上的投影工作。即将视锥体投影到屏幕空间,即得到真正的像素位置。这个过程有两个步骤。
步骤一:进行标准齐次除法(homogeneous division)/透视除法(perspective division):用齐次坐标系的w分量去除xyz分量,在OpenGL中,经过这一步可以得到归一化的设备坐标(normalized device coordinates,NDC),通过这一步可以把坐标从齐次裁剪坐标空间转换到NDC中,NDC是个立方体,在OpenGL中,这个立方体的分量处于[-1,1],这也是unity选择的。在DirectX中,z分量处于[0,1]。需要注意的是,对透视投影进行投影矩阵的变换后再做透视除法才有效果,因为正交投影在投影矩阵处理完以后已经是相应的立方体了。
也就是说,经过齐次除法以后,透视投影和正交投影的视锥体都变换到了一个相同的立方体内
步骤二:缩放。根据变换后的xy坐标映射输出窗口的对应的像素坐标。
在unity中,屏幕左下角像素坐标为(0,0),右上角像素坐标为(pixelWidth,pixelHeight),NDC内坐标的xy分量∈[-1,1],这两个步骤合在一起的公式为:
其中分量从[-1,1]缩放到[0,pixel]:先从[-1,1]缩放到[0,1]即x2=(x1+1)/2,再从[0,1]缩放到[0,pixel],x3=x2*pixel,再加上齐次除法,化简后可得下式。
z分量一般用于深度缓冲,在unity中,从裁剪空间到屏幕空间的转换底层来完成,顶点着色器只需要把顶点转换到裁剪空间中即可。
总结
以上就是一个顶点如何从模型空间变换到屏幕空间得过程,顶点着色器最基本的任务就是把顶点坐标从模型空间转换到裁剪空间中,而在片元着色器中,通常可以得到该片元在屏幕空间的像素位置。