Vertex Transformation(顶点变换)

  在前面的章节里面我们已经知道定义一个model需要顶点buffer,索引buffer和primitives,也知道可以使用多路流来实现动态顶点components.如果我们想要移动一个model,我们可以通过锁住顶点buffer,然后编辑它某些组件来达到这样的效果。Direct3D提供了顶点处理流水线来执行这样的转换,它包括平移,旋转和缩放。

  顶点处理是指发生在被渲染的每个primitive的每个顶点上的所有计算。顶点处理的每一个stage都会对它的一个或者多个components产生作用。Direct3D允许应用程序选择顶点的处理方式(硬件处理,软件处理或者两者结合)。也可以只是对顶点进行处理,而不进行渲染。

   顶点的变换矩阵一般是一个4*4的浮点矩阵。这个矩阵一般只执行平移,旋转或者缩放。

   当使用多个转换矩阵的时候,变换矩阵的顺序一般也会改变产生的结果,这是因为矩阵乘法不支持交换律。

   世界变换是顶点处理的第一步,它可以把模型空间的顶点转换到世界空间。转换矩阵可以通过方法GetTransform,SetTransform和MultiplyTransform来操作。

   也可以用矩阵变换来实现场景的层次结构。场景的层次结构可以用于绘制那些被连接起来的人物结构或者有相对定位的模型,例如一个机器人的手臂。

   一个顶点可以使用多个世界变换,这些变换结果通过加权结合在一起产生想要的结果,这样的变换方式叫做顶点blending. Direct3D提供了很多blending操作选项设置。最简单的顶点transform是Tweening,就是使用一个浮点值作为通过两个世界变换矩阵变换后顶点的权重值。顶点的权重值在顶点components里面被给出。

    通常的顶点blending一般会提供2到3个权重值以及相应的3个或者4个变换矩阵。被索引的顶点blending可以允许多大4个不同矩阵用于变换。对于未被索引顶点的blending,一个primitive的所有的顶点变换矩阵必须是一样的,只是他们的权重值可以不一样。对于索引顶点blending,每个顶点的索引可以变化,这样顶点的变换矩阵可以不一样。

    在这一章里面,我们将讲述雾化,面拣选,用户自定义剪裁面,视截体,同次除法和view port的应用。

 

顶点处理

   在以前的章节里面,设备支持软件,硬件以及软硬结合的顶点处理方式。当设备使用软硬结合的处理方式的时候,GetSoftwareVertexProcessing和SetSoftwareVertexProcessing用来控制是软件处理还是硬件处理。当这个render state为true时,就意味着软处理被选择,否则则硬处理被选择。当这个render state改变时,当前流,当前索引以及当前顶点shader需要重新还原到默认值。顶点处理包括:世界变换,纹理座标产生,纹理变换,视觉变换(View Transform),顶点雾化,面拣选,用户剪裁面处理,视截体剪裁,同次除法和视口映射。顶点处理的结果被称为“transformed and lit”顶点,它携带一个屏幕空间的位置,diffuse和specular颜色,最多8组纹理座标。这些信息都是需要传递给光栅器。如果顶点位置格式是D3DFVF_XYZRHW,就会忽略所有的顶点处理,直接传递给光栅器。

 

变换矩阵

   在数学上,座标变换是从一个座标系映射到另外一个座标系。在三维座标系,我们需要能够旋转,缩放,平移一个物体。我们可以列出一个跟一维类似的方程:

P'  =  PM +  b  = [x , y z ] M + [b1 b2 b3]

   这里M是一个3*3的矩阵, 它实现了旋转和缩放,而b实现了平移。但是如果使用同次座标,我们则只需要一个矩阵就能实现这三个功能。当P的笛卡尔座标扩展到同次座标时,4*4的矩阵可以同时对三维的点做平移,旋转和缩放。P'= PM',M'是由M和b组成的,如:[x',y',z',1]= [x,y,z,1]M'。这个同次转换矩阵也可以利用它最右边的一栏用于透视图的距离缩短处理。

   当一个顶点包含一个面法线时,面法线将和顶点在同一座标系。当顶点被转换到新的座标系是,面法线也应该要转换。但是法线不应该被扭曲。所以,如果顶点用M做转换,面法线的变换矩阵应该是M的逆矩阵的转置。

 

世界变换

   在第五章里面我们已经知道怎么在模型空间建模。世界变换是顶点处理的第一步,将模型从模型空间转换到世界空间。

   如果顶点组件里面也包含面法线,则面法线也需要被转换。不过位置组件是一个点,但是法线是一个向量。为了保持表面法线的方向,Direct3D使用一个新矩阵来转换它。这个新的矩阵就是顶点变换矩阵的逆矩阵的转置。不过这个矩阵仍然包含缩放,可以改变法向量的长度。

  RS Vertex Blend 可以用来控制世界变换的类型,它的值定义在D3DVERTEXBLENDFLAGS里面。当RS Vertex Blend为D3DVBF_DISABLE时,顶点将只有一个变换矩阵。

typedef enum _D3DVERTEXBLENDFLAGS

{

     D3DVBF_DISABLE = 0,

     D3DVBF_0WEIGHTS = 256,

     D3DVBF_1WEIGHT = 1,

     D3DVBF_2WEIGHT = 2,

     D3DVBF_3WEIGHT= 3,

   D3DVBF_TWEENING = 255

}

   GetTransform和SetTransform方法管理设备的变换矩阵的属性。D3DTRANFORMSTATETYPE给出了不同类型变换的类型,当vertex blending 被disable的时候使用D3DTS_WORLD。

typedef enum _D3DTRANSFORMSTATETYPE

{

     D3DTS_WORLD= 256,

     D3DTS_WORLD1 = 257,

     D3DTS_WORLD2 = 258,

     D3DTS_WORLD3 = 259,

     D3DTS_VIEW   = 2,

     D3DTS_PROJECTION= 3,

   D3DTS_TEXTURE0 = 16,

     D3DTS_TEXTURE1 = 17,

     D3DTS_TEXTURE2 = 18,

     D3DTS_TEXTURE3 = 19,

     D3DTS_TEXTURE4 = 20,

     D3DTS_TEXTURE5  = 21,

     D3DTS_TEXTURE   = 22,

     D3DTS_TEXTURE   = 23

} D3DTRANSFORMSTATETYPE;

 

 

变换层次

   除了使用SetTransform来设置设备的变换属性外,你也可以在现有的矩阵上乘以一个新矩阵。如果一个模型由很多个相互关联的部分组成,MultiplyTransform可以用于每个部门的相对转换。

 

顶点Blending

  连接起来的linkage对于处理那些生硬的机器人还可以,但是其他物体如布,植物或者动物,这些都需要灵活的而需要精确描述的动作,用层次变化就行了。模拟动物行走的一种方法是把一个模型变换多次,每次都使用不同的矩阵,然后使用一组权重值把把这些结果组合起来产生一个最终的输出。最简单的例子,就是只使用一个权重值,两个变换矩阵,方程式:P' = bP1 + (1-b)P2= bPM1 + (1-b)PM2。

  这个权重值b,定义了每个顶点的转换比率,它通常是被一些3D建模工具。

  • 基本顶点blending

  Direct3D 通过RS Vertex Blend的D3DVERTEXBLENDFLAGS来设定固定功能处理。当这个值是D3DVBF_1WEIGHTS, D3DVBF_2WEIGHTS, D3DVBF_3WEIGHTS分别表示每个顶点有1,2,3个权重值。如果指定D3DVBF_0WEIGHTS,那只有一个矩阵,它跟D3DVBF_DISABLE一样。D3DCAPS9::MaxVertexBlenMatices指定可以使用的最多变换矩阵的数目。当使用FVF定义顶点的格式时,D3DFVF_XYZBn 只是在位置组件里面存放了权重的数目。D3DFVF_XYZB1,D3DFVF_XYZB2,D3DFVF_XYZB3最后一个权重值是隐式的被计算,而D3DFVF_XYZB4则直接给出b0,b1,b2,b3,不需要额外计算最后一个权重值。D3DFVF_XYZB5给出了b0,b1,b2,b3,b4则是blend被索引顶点blending的矩阵索引。

  当使用定点shader声明来定义顶点时,权重值需要映射到blend weights usage. blend 矩阵索引映射到blend weights matrix indices. 权重b1,b2,b3,b4分别对应D3DTS_WORLD, D3DTS_WORLD1,D3DTS_WORLD2,D3DTS_WORLD3. 在Direct3D里面,每个定点blend矩阵通常包含变形矩阵和世界矩阵。当变形矩阵在世界空间中时,或者当在模型变形后在使用世界变换,两个矩阵可以很容易被组合起来。前者使用WD,后者使用DW.

  • 被索引的顶点blending

   使用定点blending,能够实现很多模型的变形。但是在某种程度上它对于很多复杂的有很多连接点的模型来说,也很难描述的很好,例如人。并且顶点blending最多也只有四种变形,并且在每个DrawPrimitive的调用过程中这几种变形必须被所有的顶点使用。

   使用被索引顶点blending ,也称作矩阵palette蒙皮,矩的索引被保存在每个顶点,它可以从palette选择一个矩阵使用。携带N个权重值的将使用N+1个矩阵和N+1个矩阵索引。这允许每个顶点最多四个矩阵,每个三角形最多12个矩阵。被索引的顶点blending是通过RS Indexed Vertex Blend Enable控制,而顶点blending则是通过D3DRS_VERTEXBLEND. 矩阵palette的最多数目则是通过D3DCAPS9::MaxVertexBlendMatrixIndex.每个顶点支持的最大矩阵数与顶点blending一样。对于FVF顶点buffer,增加一个额外的“权重”和包含D3DFVF_LASTBYTE_UBYTE来指定矩阵索引值。D3DFVF_LASTBYTE_UBYTE指顶点的最后一个权重值为矩阵的索引。这个权重值的每个字节代表一个0到255的索引值。如果使用固定功能的顶点shader,矩阵索引流就被映射到它对应的blend indices usage. 如果可编程的shader,这些值可以用于任何寄存器。在这种情况,矩阵索引被声明称D3DDECLTYPE_D3DCOLOR,他们将被缩小到【0,1】范围,如果矩阵索引被声明为D3DDECLTYPE_SHORT2或者D3DECLTYPE_SHORT4,则不进行缩放。

  • 顶点Tweening

   一些顶点blending效果并不能通过矩阵的顶点混合取得,但可以通过Tweening 方法实现。Tweening 这个名字是来源于电影动画,使用一个animator,绘制起始和结束姿势绘制角色,另外一个animator绘制中间的frame。固定功能的流水线提供了一种类似电影的顶点tweening。当D3DCAPS::VertexProcessingCaps 的D3DVTXPCAPS_TWEENING被设置,则设备就支持Tweening。

   每个顶点定义两个位置(P1, P2),两个法线是可选。在进行世界矩阵之前,P1和P2通过Tweening参数f结合起来。

   P' = ((1-f)P1 + fP2)M

  当RS Vertex Blend为D3DVBF_TWEENING,Tweening被激活。固定功能的顶点Tweening必须使用顶点shader声明。Tweening也需要上次出现在顶点里面的第二个位置组件和法线。顶点声明把P1和P2映射到顶点位置usage,usage索引是0和1.使用顶点Tweening,每个顶点的处理都是独立的,互不关联。但是Tweening不能引入和删除顶点。

 

顶点雾化

   雾化也称作depth-cueuing,它是根据物体距离照相机的距离来改变物体颜色的一种效果,通过雾化参数将物的颜色混合到顶点的颜色里面。

   C' = fC + (1-f)C

   f是根据物体距离照相机的距离计算出来的。雾化只是改变物体的颜色,但是它不会改变物体的透明度。因此,Alpha在雾化的过程不会改变。

   Direct3D 提供了两种雾化应用,顶点雾化和像素雾化。一次只能使用他们其中的一种,可编程的顶点shader只支持顶点雾化。使用顶点雾化,顶点处理的时候会计算出每个雾化系数,并且这些雾化系数也会被光栅器插值来计算每个像素的雾化系数。使用像素雾化,也称作table fog,光栅器计算出每个像素的雾化系数,根据距其depth来查询一张雾化系数表。Direct3D也允许应用程序计算雾化系数。一旦雾化系数被计算出来,雾化blend过程将会在像素处理的最后一个阶段进行。

   RS Fog Enable控制是否开启雾化处理。雾的颜色通过RS Fog Color指定,只有RGB channel。  RS Fog Vertex mode和RS Fog Table Mode从D3DFOGMODE中取值,并且分别选择顶点和像素雾化。如果D3DCAPS9::RasterCaps的D3DPRASTERCAPS_FOGVERTEX或者D3DPRASTERCAPS_FOGTABLE被设定,则设备支持顶点雾化或者像素雾化。如果D3DCAPS9::LineCaps的D3DLINECAPS_FOG被设置,则雾化只支持点和线primitive.

typedef enum _D3DFOGMOD

{

   D3DFOG_NONE= 0,

  D3DFOG_LINEAR  = 3,

   D3DFOG_EXP = 1,

   D3DFOG_EXP2 = 2

} D3DFOGMODE;

   当深度值在Zs(RS Fog start),Ze(RS Fog end)范围内,线性雾化是一个物体本来颜色到雾颜色线性转化的过程。Exponential 雾化提供能更加平滑的转化。Exponential有一个雾density系数。

   计算雾化距离是计算照相机离深度z的平面的距离,这种点到image中心的距离,其实不是真正的照相机到点的距离。Range-based的雾计算出点到照相机的真实距离。如果D3DCAPS9::RasterCaps的D3DPRASTERCAPS_FOGRANGE,设备支持基于range-based的雾。RS Range Fog Enable控制Range-based的雾化过程。

   使用在雾化计算过程的深度值的座标空间可能变化。在顶点雾化过程中,雾化距离是在camera空间计算的。深度值的范围[Zn,Zf]他们是近平面和远平面的的位置。在像素雾化过程中,雾化距离是是使用Z buffering在【0,1】范围内,或者在camera空间使用w buffering。如果D3DCAPS9::RasterCaps的D3DPRASTERCAPS_ZFOG或者D3DPRASTERCAPS_WFOG被设置,设备将分别支持z buffer 雾化和w buffer雾化。

   使用固定功能顶点处理,雾化系数存储在specular component的Alpha channel。使用可编程顶点处理,雾化系数可以存储在数据流的任意位置,或者在shader里面通过其他数据计算出来。

 

Face Culling

   大部分模型都只有大约一般的三角形是可视的。当三角形已经转化到了camera空间,如果三角形可见,它的法向量是指向camera的,则那些背离camera朝向的三角形,它就不可见。我们可以使用这种方法做face culling. 这样就避免不可见的三角形做光栅处理。

   Face culling 使用三角形的面法线。但是Direct3D并不会使用顶点组件的法线来用来做Face culling. Face culling是三角形的属性,并不是三角形模拟的光滑表面的属性。法线是通过三角形的顶点计算的,任何面法线向量都是面里面两个向量的叉集。

   RS Cull mode指定Face culling的模式,它包括D3DCULL_NONE, D3DCULL_CW和D3DCULL_CCW.  D3DCULL_NONE禁止了face culling. D3DCULL_CW cull 那些被转换后的顶点以顺时针出现的三角形,D3DCLL_CCW 则是cull 被转换后的顶点以逆时针出现的三角形。

   D3DCAPS9的PrimitiveMiscCaps的三个bit来指定设备的支持能力。

 

Clipping

   当primitive在某个边界之外,则这个顶点就会被删除。这个边界可能是render target的边界,也可能是3维空间的任意的面。当一个primitive被删除的时候,边界以内的部分就确定了,就可以渲染这个部分了。剪裁面和模型的相交,可以产生新的顶点。新的顶点就是剪裁面和物体模型几何体相交的点。当线和三角形被剪裁掉,新的顶点产生。面的法线,纹理座标,diffuse , specular可以通过插值取得。

   Direct3D 提供了集中面剪裁的方式,一种是用户自定义的面,一种是通过视截头体。视截头体提供的是一个照相机显示的空间。Guard band剪裁则类时与视截体空间剪裁,但是它在某些设备能提供更好的性能优化。还有一些frame buffer的操作,如Z test, alpha test, stencil test和一些像素shader,也可以被认为是剪裁操作。但是,这些操作的对象是像素,而不是集合体。

  • 用户clip 面

  用户自定义的clip面最多提供6个面,每个面有一个组系数定义。它可以用于固定功能流水线和可编程顶点处理。clip面的序号是0到5。 可以通过GetClipPlane和SetClipPlane. RS Clip plane enable 控制是否使用这个clipping.

  • 视截体clipping

  如果RS Clipping为真,则视截体clipping被使用。当视截体clipping或者用户定义面被clipping,设备返回clip状态的被剪裁的primitives的信息。当primitive被处理,并且它与剪裁面相交,它将会在clip状态里面设置一个位。如果一个primitive 完全在视截体的外面,它将被culled. Clip status使用GetClipStatus和SetClipStatus管理。这个状态本身是保存在D3DCLIPSTATUS9里面的。

typedef struct _D3DCLIPSTATUS9

{

   DWORD ClipUnion;

   DWORD ClipIntersection;

} D3DCLIPSTATUS9

  • Guard Band clipping

  它类似与视截体clipping,它是基于像素操作,不是基于模型集合。Guard band是一个覆盖当前视口的区域。任何在当前视口以外但是guard band以内的primitive都会被删除。

  如果能保证所有的几何体都在guard band内部,昂贵的几何clipping和插值将会被忽略,如果不能保证,视截体clipping必须要使用。Guard band 的范围 GuardBandLeft, GuardBandRight, GuardBandBotttom,GuardBandTop,它们的值都在屏幕空间。

 

 Screen Space and ViewPort

    齐次除法和视口应用位于顶点和除法之间。在world ,view , projection转换后,顶点已经位于标准视图空间的齐次点(x,y,z,w)。在这个空间把所有的点除以w将会得到一个笛卡尔座标系的标准空间。

    对于已经处于正交的笛卡尔座标系统里面的顶点,他们已经准备映射到screen空间了。这种映射是被viewport定义的。它能把场景只渲染在render target的一个子区域,或者深度buffer一段小区域,或者两则结合。视口是屏幕空间的一个矩形,他的深度区间范围是在【0,1】。默认的视口占据整个render target区域,整个深度区域。

viewport 映射可以用下面的方程式表示:

M = S(w/2, h/2,Zf - Zn) T(Xs,Ys,Zn)

这个转换把x 从【0,1】映射到[Xs,Xs+w],y 从【0,1】映射到【Ys,Ys+h】,z 从【0,1】到[Zn, Zf]。只有位置组件受它的影响。

typedef struct _D3DVIEWPORT9

{

   DWORD X;// XY是左上的座标

   DWORD Y;

   DOWRD Width;

   DWORD Height;

   float MinZ;

   float MaxZ;
}D3DVIEWPORT9;

Z的范围是让应用程序使用depth buffer执行一些可视效果检测。

一旦视口处理结束,顶点处理就结束了。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章