详细介绍VertexShader结构(转)

详细介绍VertexShader结构(转)[@more@]

  我们将通过下面这个Vertex Shader结构的图形模型来进入Vertex Shader的世界:

  
  2004.12.28.15.1.25.1.gif

  在Vertex Shader中的所有数据都被表现为128-bit的四元数(4 x 32-bit):

  
  2004.12.28.15.1.37.2.gif

  因为使用一个指令但处理一组数据,一个Vertex Shader的硬件可以被看成是一个典型的SMID(单指令多数据)处理器。Vertex Shader使用的这种数据格式非常的有用,因为大多数的转换和光线计算的进行都需要使用4 x 4的矩阵或者 四元组。

  

  Vertex Shader指令非常的简单和容易理解。因为Vertex Shader不允许任何的循环,跳转和条件分支,这意味着它仅仅是线性的执行程序,一个指令接着一个指令。Vertex Shader的程序在Directx 8.1中最长为128个指令。我们可以结合几个Vertex Shader使用, 一个计算转换,一个计算光照。但是在同一时候仅仅只有一个Verthex Shader可以被激活,并且激活的Vertex Shader必须要计算每个顶点所有需要的输出数据。

  

  一个Vertex shader使用16个输入寄存器(v0-v15,每一个寄存器都由128位的四元浮点数构成)来读取输入的数据。通过输入寄存器,Vertex shader可以非常容易的表示一个典型顶点的数据:位置坐标,法线,漫反射颜色和镜面反射颜色,雾坐标和贴图大小信息。

  

  常量寄存器在vertex shader开始执行指定程序之前被CPU加载。常量寄存器是只读的, 一般用于储存例如光源位置、材质、特殊动画所需数据等参数。常量寄存器可以通过地址寄存器a0.x来间接寻址。常量寄存器除了在vertex shader中还可以在程序中被使用,但是在每一条指令中仅仅可以引用一个常量就寄存器。如果一条指令需要引用超过一个的常量寄存器,它只能通过暂存寄存器来引用。一般的常量寄存器为c0-c95,但在ATI RADEOM 8500中是c0-c191。

  

  暂存寄存器由12个寄存器组成,是可读写的,可以用于数据的存储和读取。它们分别是r0-r11。

  

  根据具体的硬件的不同,有至少13个输出寄存器。每个输出寄存器都以o打头。输出寄存器在光栅化时可以被使用。存在输出寄存器中的最终结果是另外的一个顶点,一个转换入“同源剪裁空间”的顶点。下面的表中列出了所有可用的寄存器:

  
  2004.12.28.15.1.48.3.JPG

  Vertex Shader编程概览

  

  因为在同一个时候仅仅有一个Vertex shader可以被激活,为每一个基本的功能块编写一个vertex shader是一个不错的主意。一般来说在不同的vertex shader之间切换的性能消耗要比变换一个贴图的性能消耗都要小。所以如果一个物体需要一种特殊的转换或者灯光,最好就在它的任务中给它一个恰当的vertex shader。让我们看看下面的例子:

  

  你在一个外星球遇难了,身上穿着正规军的盔甲,但仅仅装备着一个锯子。当你在一个烛光照耀着的地下室穿行时,一个怪物出现了,然后你就躲到了一个在任何星球都很常见的箱子后面。在考虑你作为一个使用锯子拯救这个世界的英雄的命运同时,我们开始计算这个场景所需要的vertex shader数目。

  

  首先需要一个vertex shader作为怪物的动画需要,光照渲染和可能存在的环境反射渲染。其他的vertex shader将分配给地板,墙,箱子,视角,烛光和你的锯子。或许地板,墙,箱子和锯子可以使用同一个shader,但是烛光和视角必须要有不同的shader。这依赖于你的设计和特定图形硬件的性能。

  

  每一个vertex shader驱动的程序都必须要有下面的几个步骤:

  

  通过检查D3DCAPS8::VertexShaderVersion来确定 vertex shader有无被支持。

  

  使用D3DVSD_*宏来定义 vertex shader,使 vertex shader 的流映射到输入寄存器

  

  使用SetVertexShaderConstant() 来设定vertex shader常量寄存器

  

  使用D3DXAssembleShader*() 编译刚才所写的vertex shader(或者,你可以预编译vertex shader 使用一个shader编译器)

  

  使用CreateVertexShader() 创建一个vertex shader句柄

  

  使用SetVertexShader()将vertex shader与一个特定的物体相连

  

  使用DeleteVertexShader() 删除vertex shader

  

  

  检查Vertex Shader的支持状况

  检查如果缺少一些特殊功能的支持,程序应该使用默认的行为(例如使用T&L)或者给用户一个提示,使用户做一些使这些特殊功能得以支持的事(就是提示他们可以购买新的显卡了^_^)。下面的代码段检查vertex shader 1.1是否被支持了:

  

  if( pCaps->VertexShaderVersion < D3DVS_VERSION(1,1) )

  return E_FAIL;

  在程序启动阶段,必须通过GetDeviceCaps()函数来得到一个D3DCCAPS8的结构caps。如果你使用Directx 8.1 SDK中提供的Directx框架来搭建你的应用程序,这个会被自动完成。如果检查后发现你的硬件不支持你需要的vertex shader版本,你必须通过设置D3DCREATE_SOFTWARE_VERTEXPROCESSING 属性调用CreateDevice()来切换使用软件vertex shader。这时将由对Intel和AMD不同CPU进行优化过的软件接口来进行vertex shader执行。

  

  下面是不同版本Directx支持的vertex shader的版本:

  
  2004.12.28.15.2.0.4.JPG

  在1.0和1.1之间的唯一区别就是对于a0寄存器的支持。Directx 8.0和Directx 8.1对应的光栅化器(rasterizer)和Inter、AMD为他们各自CPU所写的软件模拟接口都支持1.1版本。在本文写成之时,市面上支持1.1的硬件只有GeForce3/4TI、RADEON 8500,同时要注意的是并没有只支持1.0版本的显卡,支持1.0的也肯定支持1.1,1.0只是一个过渡版本。

  

  Vertex Shader定义

  

  你必须在使用Vertex Shader前定义它。它的定义可以通过一个静态的外部接口来完成。看起来有可能是这个样子:

  

  float c[4] = {0.0f,0.5f,1.0f,2.0f};

  DWORD dwDecl0[] = {

  D3DVSD_STREAM(0),

  D3DVSD_REG(0, D3DVSDT_FLOAT3 ),  // 输入寄存器 v0

  D3DVSD_REG(5, D3DVSDT_D3DCOLOR ), // 输入寄存器 v5

  // 设置几个常量寄存器

  D3DVSD_CONST(0,1),*(DWORD*)&c[0],*(DWORD*)&c[1],*(DWORD*)&c[2],*(DWORD*)&c[3],

  D3DVSD_END()

  };

  上面的Vertex shader定义使用D3DVSD_STREAM(0)来设置它成为0号数据流。在以后SetStreamSource() 将会通过这个声明绑定一个顶点buffer到设备数据流。你可以通过这种方法提供Direct3D渲染引擎不同的数据流。

  

  举个例子,我们可以用第一个数据流表示位置和法线,第二个数据流来表示颜色和贴图坐标。它也可以使在单材质渲染和多材质渲染之间的切换变得非常容易:只要使有第二套材质坐标的的数据流失效就可以了。

  

  对于哪一个顶点属性或者输入的顶点数据被映射到哪一个输入寄存器,你也必须给出定义。D3DVSD_REG 将一个顶点寄存器和一个顶点数据流中的顶点元素(或者属性)加以绑定。在我们上面的例子中, D3DVSDT_FLOAT3 将被放入第一个输入寄存器中而D3DVSDT_D3DCOLOR 将被放入到第6个输入寄存器中。举个另外的例子,通过D3DVSD_REG(0, D3DVSDT_FLOAT3 ) 定义关于位置的数据可以被0号输入寄存器(v0)处理,而通过 D3DVSD_REG(3, D3DVSDT_FLOAT3 )的定义,法线数据可以被3号输入寄存器(v3)处理。

  

  如果一个人想使用N-Patches,开发者如何将输入的顶点属性映射入不同的寄存器比较的重要,因为N-Patch的斑格纹需要它的位置数据放在v0而法线数据放在v3。否则,开发者可以自由的映射到自己看上去适合的寄存器。例如,通过D3DVSD_REG(0, D3DVSDT_FLOAT3 ) 定义使0号输入寄存器(v0)处理关于位置的数据,而通过 D3DVSD_REG(3, D3DVSDT_FLOAT3 )的定义,使3号输入寄存器(v3)处理法线数据。

  

  与此形成对比的是在fixed-function渲染管道中,映射入不同寄存器的数据是固定的。d3d8types.h中有一张关于fixed-function管道渲染输入的数据的预定义。特定的顶点元素例如位置必须被放置在位于顶点输入内存中的特定寄存器。例如,顶点的位置被 D3DVSDE_POSITION 限定放于0号寄存器,漫反射光颜色被 D3DVSDE_DIFFUSE 限定放于3号寄存器。下面是d3d8types.h中的整张列表:

  

  #define D3DVSDE_POSITION   0

  #define D3DVSDE_BLENDWEIGHT  1

  #define D3DVSDE_BLENDINDICES 2

  #define D3DVSDE_NORMAL    3

  #define D3DVSDE_PSIZE     4

  #define D3DVSDE_DIFFUSE    5

  #define D3DVSDE_SPECULAR   6

  #define D3DVSDE_TEXCOORD0   7

  #define D3DVSDE_TEXCOORD1   8

  #define D3DVSDE_TEXCOORD2   9

  #define D3DVSDE_TEXCOORD3   10

  #define D3DVSDE_TEXCOORD4   11

  #define D3DVSDE_TEXCOORD5   12

  #define D3DVSDE_TEXCOORD6   13

  #define D3DVSDE_TEXCOORD7   14

  #define D3DVSDE_POSITION2   15

  #define D3DVSDE_NORMAL2    16

  D3DVSD_REG 中的第二个参数表示了纬度和算法数据类型。下面的是定义在d3d8types.h中的值:

  

  // bit declarations for _Type fields

  #define D3DVSDT_FLOAT1 0x00 // 1D float expanded to (value, 0., 0., 1.)

  #define D3DVSDT_FLOAT2 0x01 // 2D float expanded to (value, value, 0., 1.)

  #define D3DVSDT_FLOAT3 0x02 // 3D float expanded to (value, value, value, 1.)

  #define D3DVSDT_FLOAT4 0x03 // 4D float

  

  // 4D p

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/8225414/viewspace-951768/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/8225414/viewspace-951768/

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值