Shader学习06【坐标空间】

前言:我们通过前面的学习已经知道了如何使用矩阵来表示基本的变换,如平移,旋转,缩放,而在本节中,我们将关注如何使用这些变换来对坐标空间进行变换

       在前面渲染流水线中就已经接触了坐标空间的变换,例如,在学习顶点着色器流水线阶段时,提到过,顶点着色器最基本的功能就是把模型的顶点坐标从模型空间转换到裁剪坐标空间中。

       渲染游戏的过程可以理解成是把一个个顶点经过层层处理最终转化到屏幕上的过程,那么后续我们就会学习这个转换的过程是如何实现的,更具体来说,顶点是经过了哪些坐标空间后,最后被画在了我们的屏幕上。

1:坐标空间的变换

       先做一下数学铺垫。在渲染流水线中,我们往往需要把一个点或者方向矢量从一个坐标空间转换到另一个坐标空间,这个过程到底怎么实现?

       先把这个问题一般化,要想定义一个坐标空间,必须指明其原点位置和3个坐标轴的方向,而这些数值实际上是相对于另一个坐标空间的(所有的都是相对的,没有相对的坐标空间,也就无法标注原点和坐标轴位置),也就是说,坐标空间会形成一个层次结构——每个坐标空间都是另一个坐标空间的子空间,反过来说,每个空间都有一个父坐标空间,对坐标空间的变换实际上就是在父空间和子空间之间对点和矢量进行变换

       假设,现在有父坐标空间P以及一个子坐标空间C,我们知道在父坐标空间中子坐标空间的原点位置以及3个单位坐标轴,我们一般会有这两种需求:一种需求是把子坐标空间下表示的点或者矢量Ac转换到父坐标空间下的表示Ap,另一个需求是返反过来:即把父坐标空间下表示的点或者矢量Bp转换到子坐标空间下的表示Bc,我们可以使用下面的公式来表示这两种需求:

 

 其中Mc->p表示的是从子坐标空间的变换矩阵,而Mp->c是其逆矩阵(即反向变换)。那么现在的问题就是,如何求解这些变换矩阵?事实上,我们只需要解出两者之一即可,另一个矩阵可以通过求逆矩阵的方式来得到

        

        回顾一个简单的问题:当给定一个坐标空间以及其中一点(a,b,c)时,我们是如何知道该点的位置的呢?我们可以通过4个步骤来确定他的位置:

        @1:从坐标空间的原点开始

        @2:向X轴移动a个单位

        @3:向Y轴移动b个单位

        @4:向Z轴移动c个单位

        需要说明的是,上面的步骤只是我们的想象,这个点实际上并没有发生移动,上面的的步骤看起来再简单不过了,坐标空间的变换就蕴含在上面的4个步骤中,现在,我们已知子坐标空间C的3个坐标轴在父坐标空间P下的表示xc,yc,zc,以及其原点位置Oc,当给定一个子坐标空间中的一点Ac=(a,b,c),我们同样可以依照上面4个步骤来确定其在父坐标空间下的位置Ap:

        @1:从坐标空间的原点开始

        这很简单,我们已经知道了子坐标空间的原点位置Oc

        @2:向X轴方向移动了X个单位:

        仍然简单,我们已经知道了X轴的矢量表示,因此可以得到

        Oc+axc

        @3:向y轴方向移动了Y个单位:

        Oc+axc+byc

        @4:向Z轴方向移动z个单位:

        最后可以得到:Oc+axc+byc+czc

        现在,我们已经求出了Mc->p!  为什么呢? 我们可以来看一下最后得到的式子:

        Ap=Oc+axc+byc+czc

        你可能会问,这个式子里根本就没有矩阵啊,其实我们只要稍微使用一点“魔法”,矩阵就会出现在上面的式子中:

        

 

       其中“|”符号表示按列展开,上面的式子实际上就是使用了我们之前所学的公式而已,但这个最后的表达式还不是很漂亮,因为还存在加法表达式,即平移变换,我们已经知道3*3的矩阵无法表示平移变换,因此为了得到一个更漂亮的结果,我们把上面的式子扩展到齐次坐标空间中,得:

      

 

      那么现在,哈哈,已经看到Mc->p在哪里了吧?没错:

      

 

      一旦求出来Mc->p,Mp->c就可以通过求逆矩阵的方式求出来,因为从坐标空间C变换到坐标空间P与从坐标空间P变换到坐标空间C是互逆的两个过程

     可以看出来,变换矩阵Mc->p实际上可以通过坐标空间C与在坐标空间P中的原点和坐标轴的矢量表示来构建出来:把3个坐标轴依次放入矩阵的前3列,把原点矢量放到最后一列,再用0和1填充最后一行即可

     需要注意的是,这里我们并没有要求3个坐标轴xc,yc,zc是单位矢量,事实上,如果存在缩放的话,这3个矢量很可能不是单位矢量

     不仅如此,我们可以利用反向思维,从这个变换矩阵反推来获取子坐标空间的原点和坐标轴方向!例如,当我们已知从模型空间到世界空间的一个4*4变换矩阵,可以提取它的第一列,第二列,第三列来获得模型空间在世界空间下的X,Y,Z轴,我们可以从另一个角度来理解这个过程,因为矩阵Mc->p可以把一个方向矢量从坐标空间C变换到坐标空间P中,那么,我们只需要用它来变换坐标空间C中的X轴(1,0,0,0),即使用矩阵乘法Mc->p[1,0,0,0]T,得到的结果正是Mc->p的第一列(在父坐标系下子坐标系的坐标轴表示)

     另一个有趣的情况是,对方向矢量的坐标空间变换,我们知道,矢量是没有位置的,因此坐标空间的原点变换时可以忽略的。也就是说,我们仅仅平移坐标系的原点是不会对矢量造成任何影响的。那么,对矢量的坐标空间变换就可以使用3*3的矩阵来表示,因为我们不需要表示非线性的平移的变换,那么变换矩阵就是:

     

 

 

     在Shader中,我们常常会看到截取变换矩阵的前3行前3列来对法线方向,光照方向来进行空间变换,这正是原因所在

     现在再看其逆矩阵Mp->c.我们前面讲到,可以通过Mc->p的逆矩阵的方式求解出来反向变换Mp->c但有一种情况我们不需要求解逆矩阵就可以得到Mp->c这种情况就是Mc->p是一个正交矩阵,如果它是一个正交矩阵的话,Mc->p的逆矩阵就等于它的转置矩阵。这意味着我们不需要进行复杂的求逆操作就可以得到反向变换,也就是说

 

      而现在,我们不仅可以根据变换矩阵Mc->p反推出子坐标空间的坐标轴方向在父坐标空间中的表示XcYcZc还可以反推出父坐标空间的坐标轴方向在子坐标空间中的表示XpYpZp,这些坐标轴对应的就是Mc->p的每一行,也就是说,如果我们知道坐标空间变换矩阵MA->B是一个正交矩阵,那么我们可以提取它的第一列来得到坐标空间A的X轴在坐标空间B下的表示,还可以提取它的第一行来得到坐标空间B的X轴在坐标空间A下的表示,反过来,如果我们知道坐标空间B的X轴,Y轴,Z轴(必须是单位矢量,否则构建出来的就不是正交矩阵了)在坐标空间A下的表示,就可以把它们依次放在矩阵的每一行就可以得到从A到B的变换矩阵了

      看到这里,我相信没有接触过的Shader或者图形学的各位已经一团乱麻了,这个过程很容易造成思维的混乱,但是只有知道了这些原理,遇到疑问时你才知道怎么样去验证结果的正确性

      当你不知道把坐标轴的表示是按行放还是按列放的时,不妨先选择一种摆放方式来得到变换矩阵。例如,现在我们想把一个矢量从坐标空间A变换到坐标空间B,而且我们已经知道坐标空间B的X轴,Y轴,Z轴在空间A下的表示,即XBYBZB那么想得到从A到B的变换矩阵MA->B,我们是把他们按列排放还是按行呢?如果读者实在想不起来正确答案,我们不妨先随便选择一种方式,例如按列摆放,那么:

      

 

      注意,这个矩阵是不对的,我们可以非常快速的来验证它是否是正确的,方法就是,用MA->B来变换XB,在计算前我们先想一下结果,如果我们用变换矩阵来变换B的X轴的话,那么结果应该是(1,0,0)才对,因为当变换到空间B中时,X轴的指向就是(1,0,0),好了,我们可以来进行真正的计算来验证它了:

      

 

      这里,你应该会有困惑,我们并不确定上式的真正计算结果,这当然不是我们的计算方式有问题,是上式的计算结果的确不可知,这种时候你就会发现我们的摆放方式选择错了,现在我们使用正确的摆放方式,即按行来摆放,那么就有:

      

 

     

      至此,一些shader中常用矩阵变换的原理,解释结束

 

     

        

posted @ 2021-06-18 15:10  专杀小三  阅读(159)  评论(0编辑  收藏  举报