将顶点着色器与顶点声明分离

来源:互联网 发布:学历证书制作软件 编辑:程序博客网 时间:2024/06/15 12:14

在Microsoft DirectX 9.0中,顶点着色器和顶点声明不再是在创建顶点着色器(CreateVertexShader)的时候绑定在一起。对着色器的验证已经被分成两部分,一部分在顶点着色器创建时执行,另一部分在绘制(DrawPrimitive)时执行。

  • 把DirectX 8.x的顶点声明映射到DirectX 9.0的顶点声明
  • 把FVF码映射到DirectX 9.0的顶点声明
  • 把DirectX 9.0的顶点声明映射到DirectX 8.x的顶点声明
  • 把DirectX 9.0的顶点声明映射到FVF码

顶点着色器和顶点声明都由相应的对象表示。为了使之能与DirectX 8.x驱动程序一起工作,Direct3D进行了一些高速缓存。在“绘制的时候”,运行库会检查是否存在一个组合着色器对象,该对象封装了当前声明和着色器。如果存在,那么运行库会将它送给驱动程序,反之,运行库会为当前着色器和声明的组合创建一个新的组合着色器对象。另外,为了解决API的可用性问题,9.0版增加了一个与SetVertexDeclaration调用等价的SetFVF调用。这是一个有用的函数,当调用这个函数时,新的FVF会取代当前的顶点声明,反之亦然。如果驱动程序是DirectX 8.0之前的版本(NumStream为0),那么对于那些不能被转换成弹性顶点格式(FVF)的顶点声明,SetVertexDeclaration可能会失败并返回错误码。

  • SetVertexDeclaraion和SetVertexShader调用会被状态块记录。设置或取得着色器或声明会导致该对象的引用计数的增加。
  • FVF码既可以用于固定功能流水线,又能用于可编程顶点流水线。FVF要根据转换表,被转换为顶点着色器声明。在写顶点着色器函数中的DCL命令时,应该紧记这一点。
  • 软件顶点处理支持DirectX 9.0级别的特性,也支持tessellation,因此在用软件顶点处理进行绘制时,可以使用更多的声明。
  • 对可编程顶点流水线而言:在绘制的时候,Microsoft Direct3D会在当前的顶点声明和当前的顶点着色器函数中查找相同的“用途 – 用途索引”组合。如果找到了这样的组合,那么着色器函数中DCL声明的寄存器会被用作顶点元素的目标寄存器。
  • 如果FVF包含了XYZRHW位置类型,那么顶点被认为是经过变换和经过光照处理的,在用于可编程顶点流水线时,不会应用顶点着色器函数。
  • 如果当前顶点声明中的一个顶点元素的用途无法在当前顶点着色器中找到,那么该顶点元素就被忽略。

在DirectX 8.x驱动程序(numstrems不为零,但不支持数据流偏移量)上允许使用声明的一个子集,只能创建那些可被转换为 DirectX 8.x风格的声明。由于这个原因,如果声明不能被转换,那么CreateVertexDeclaration调用可能会失败。对混合模式设备来说,此类失败会发生在Drawxxx的时候,因为只有这时候才能知道着色器是被用于硬件还是软件顶点处理。表中概括了这类转换。

在DirectX 8.0之前的驱动程序(numstreams为零)上允许使用一个更小的子集,只能使用那些可被转换为FVF的声明。如果无法进行转换,那么CreateVertexDeclaration可能会失败。对混合模式设备来说,此类失败会发生在Drawxxx的时候,因为只有这时候才能知道着色器是被用于硬件还是软件顶点处理。表中概括了这类转换。只能使用数据流0(显然可以从MaxStreams设备能力中看出)。

顶点元素的顺序应该和FVF码相对应,D3DDECLUSAGE_POSITION和D3DDECLUSAGE_NORMAL的用途索引应该为零。使用混合顶点处理的设备时,如果要切换顶点处理模式,那么无需重置所有的输入顶点(和索引)数据流,顶点声明和顶点函数。不能用NULL作为SetVertexDeclaration的输入。在使用软件顶点处理时,可编程顶点流水线的着色器代码中用到的用途也应该存在于在绘制的时候与之绑定的声明(或FVF)中。

符合以下规则的声明可以用来在固定功能流水线(假设不需要tessellation)中进行渲染。

  • 只使用D3DDECLMETHOD_DEFAULT。
  • 顶点元素之间不能有间隔(SKIP在DirectX 8.x声明中是不允许的)
  • 指定POSITION用途。
  • 为POSITION用途指定D3DDECLTYPE_FLOAT3数据类型。
  • 下表中没有描述的顶点元素在所有DirectX 8.0之前的驱动程序上都无法被转换为有效的FVF码,因此无法用于固定功能顶点处理。

把DirectX 8.x的顶点声明映射到DirectX 9.0的顶点声明

DirectX 8.x

DirectX 9.0 用途

DirectX 9.0 用途索引

D3DVSDE_POSITION

D3DDECLUSAGE_POSITION

0

D3DVSDE_POSITION2

D3DDECLUSAGE_POSITION

1

D3DVSDE_NORMAL

D3DDECLUSAGE_NORMAL

0

D3DVSDE_NORMAL2

D3DDECLUSAGE_NORMAL

1

D3DVSDE_BLENDWEIGHT

D3DDECLUSAGE_BLENDWEIGHT

0

D3DVSDE_BLENDINDICES

D3DDECLUSAGE_BLENDINDICES

0

D3DVSDE_PSIZE

D3DDECLUSAGE_PSIZE

0

D3DVSDE_DIFFUSE

D3DDECLUSAGE_COLOR

0

D3DVSDE_SPECULAR

D3DDECLUSAGE_COLOR

1

D3DVSDE_TEXCOORDn

D3DDECLUSAGE_TEXCOORD

n

把FVF码转换为DirectX 9.0的顶点声明

FVF

数据类型

用途

用途索引

D3DFVF_XYZ

D3DDECLTYPE_FLOAT3

D3DDECLUSAGE_POSITION

0

D3DFVF_XYZRHW

D3DDECLTYPE_FLOAT4

D3DDECLUSAGE_POSITIONT

0

D3DFVF_XYZW

D3DDECLTYPE_FLOAT4

D3DDECLUSAGE_POSITIONT

0

D3DFVF_XYZB5 and D3DFVF_LASTBETA_UBYTE4

D3DVSDT_FLOAT3, D3DVSDT_FLOAT4, D3DVSDT_UBYTE4

D3DDECLUSAGE_POSITION, D3DDECLUSAGE_BLENDWEIGHT, D3DDECLUSAGE_BLENDINDICES

0

D3DFVF_XYZB5

D3DDECLTYPE_FLOAT3, D3DDECLTYPE_FLOAT4, D3DDECLTYPE_FLOAT1

D3DDECLUSAGE_POSITION, D3DDECLUSAGE_BLENDWEIGHT, D3DDECLUSAGE_BLENDINDICES

0

D3DFVF_XYZBn (n=1..4)

D3DDECLTYPE_FLOAT3 D3DDECLTYPE_FLOATn

D3DDECLUSAGE_POSITION, D3DDECLUSAGE_BLENDWEIGHT

0

D3DFVF_XYZBn (n=1..4) and D3DFVF_LASTBETA_UBYTE4

D3DDECLTYPE_FLOAT3 D3DDECLTYPE_FLOAT(n-1) D3DDECLTYPE_UBYTE4

D3DDECLUSAGE_POSITION, D3DDECLUSAGE_BLENDWEIGHT, D3DDECLUSAGE_BLENDINDICES

0

D3DFVF_NORMAL

D3DDECLTYPE_FLOAT3

D3DDECLUSAGE_NORMAL

0

D3DFVF_PSIZE

D3DDECLTYPE_FLOAT1

D3DDECLUSAGE_PSIZE

0

D3DFVF_DIFFUSE

D3DDECLTYPE_D3DCOLOR

D3DDECLUSAGE_COLOR

0

D3DFVF_SPECULAR

D3DDECLTYPE_D3DCOLOR

D3DDECLUSAGE_COLOR

1

D3DFVF_TEXCOORDSIZEm(n)

D3DDECLTYPE_FLOATm

D3DDECLUSAGE_TEXCOORD

n

把DirectX 9.0的顶点声明转换为DirectX 8.x的顶点声明

用途

用途索引

DirectX decl

D3DDECLUSAGE_POSITION

0

D3DVSDE_POSITION

D3DDECLUSAGE_POSITION

1

D3DVSDE_POSITION2

D3DDECLUSAGE_BLENDWEIGHT

0

D3DVSDE_BLENDWEIGHT

D3DDECLUSAGE_BLENDINDICES

0

D3DVSDE_BLENDINDICES

D3DDECLUSAGE_NORMAL

0

D3DVSDE_NORMAL

D3DDECLUSAGE_NORMAL

1

D3DVSDE_NORMAL2

D3DDECLUSAGE_PSIZE

0

D3DVSDE_PSIZE

D3DDECLUSAGE_COLOR

0

D3DVSDE_DIFFUSE

D3DDECLUSAGE_COLOR

1

D3DVSDE_SPECULAR

D3DDECLUSAGE_TEXCOORD

n

D3DVSDE_TEXTUREn, n <= 7

把DirectX 9.0的顶点声明转换为FVF码

在DirectX 8.0及以后的驱动程序上,更多的类型可以成功地被转换为有效的声明,因此可以被用于固定功能顶点处理。

  • 如果使用了DirectX 8.x驱动程序,那么顶点声明会根据以下规则被转换为DirectX 8.x的声明。如果使用其它的用途组合,那么对Direct3D的调用会失败。
  • 对于D3DVSD_STREAM中每个新的数据流,会插入一个标记(token)。
  • 顶点元素不能共享数据流中相同的偏移量,并且不能重叠。
  • 数据类型必须小于或等于D3DDECLTYPE_SHORT4。
  • 对于每个使用了D3DDECLMETHOD_DEFAULT方法的顶点元素,会插入D3DVSD_REG标记。
  • Usage和UsageIndex会根据下表被转换为寄存器值。如果使用DrawRectPatch(RT-patches),那么tessellator的输出应该符合前面的规则。

数据类型

用途

用途索引

FVF

D3DDECLTYPE_FLOAT3

D3DDECLUSAGE_POSITION

0

D3DFVF_XYZ

D3DDECLTYPE_FLOATn

D3DDECLUSAGE_BLENDWEIGHT

0

D3DFVF_XYZBn

D3DDECLTYPE_UBYTE4

D3DDECLUSAGE_BLENDINDICES

0

D3DFVF_XYZB (nWeights+1)

D3DDECLTYPE_FLOAT3

D3DDECLUSAGE_NORMAL

0

D3DFVF_NORMAL

D3DDECLTYPE_FLOAT1

D3DDECLUSAGE_PSIZE

0

D3DFVF_PSIZE

D3DDECLTYPE_D3DCOLOR

D3DDECLUSAGE_COLOR

0

D3DFVF_DIFFUSE

D3DDECLTYPE_D3DCOLOR

D3DDECLUSAGE_COLOR

1

D3DFVF_SPECULAR

D3DDECLTYPE_FLOATm

D3DDECLUSAGE_TEXCOORD

n

D3DFVF_TEXCOORDSIZEm(n)

D3DDECLTYPE_FLOAT3

D3DDECLUSAGE_POSITION

1

N/A

D3DDECLTYPE_FLOAT3

D3DDECLUSAGE_NORMAL

1

N/A


 

- 像素着色器 (Pixel Shaders) (alpha version)

像素着色器


在Microsoft DirectX 8.0之前,Microsoft Direct3D使用固定功能流水线把三维几何体转换为屏幕上的像素。用户通过设置流水线的属性来控制Direct3D进行变换、光照和渲染像素的方式。固定功能顶点格式在编译的时候定义并决定输入顶点的格式,一旦定义,用户在运行的时候就几乎无法控制流水线的改变。

通过允许对顶点的变换、光照和对每个像素的着色等功能进行编程,着色器把图形流水线引入了一个新的高度。像素着色器是一些小程序,在对三角形进行光栅化操作时运行。这在渲染像素的方法上给了用户更高一级的灵活性。

像素着色器包含由ASCII文本组成的像素着色器指令。算术指令可以用来进行漫反射和/或镜面反射光照计算。纹理寻址指令提供了多种读取和应用纹理数据的操作。着色器具有这样的功能,可以给颜色分量设置掩码以及交换颜色分量。着色器的正文看起来有点像汇编语言,它用Direct3D扩展(D3DX)进行汇编,输入可以是文本字符串或是文件。汇编器的输出是一系列操作码,应用程序可以通过IDirect3DDevice9::CreatePixelShader方法把这些操作码提供给Direct3D。像素着色器有几个版本,请参阅着色器参考手册

  • 创建像素着色器 – 包含了示例代码,用像素着色器对物体的漫反射色进行高洛德插值。本示例包含了对所使用方法的详细描述。
  • 确认对像素着色器的支持 – 提供了更为详细的说明,解释如何检测硬件对像素着色器的支持。
  • 纹理操作的转换 – 提供了一些把纹理操作转换为像素着色器指令的例子。
  • 对纹理的一些考虑 – 详细说明了在像素着色器中被忽略的纹理层状态。
  • 像素着色器示例 – 显示了更多的示例代码,添加纹理并把顶点颜色和纹理进行混合。
  • 调试 – 提供了有关调试的信息。

指令集的变化非常快。为避免在使用指令时出现问题,请查阅硬件开发商的网站。或者,也可以使用高级着色器语言,这样就可以得到由Direct3D扩展(D3DX)编译得到的着色器指令。


创建像素着色器


本示例用像素着色器对一个四边形的漫反射色进行高洛德插值。示例显示了着色器文件的内容以及应用程序中所需的代码。

以下是创建像素着色器所需的步骤:

  • 第1步:检查对像素着色器的支持。
  • 第2步:声明顶点数据。
  • 第3步:设计像素着色器。
  • 第4步:创建像素着色器。
  • 第5步:渲染输出像素。

如果读者已经知道如何构建并运行Direct3D示例,那么可以从本示例中复制代码并粘贴到已有的应用程序中。

第1步

要检查对像素着色器的支持,应该使用以下代码。这个例子检查1.1版本的像素着色器。

D3DCAPS9 caps;

m_pd3dDevice->GetDeviceCaps(&caps);        // 使用m_pd3dDevice前要进行初始化

if( caps.PixelShaderVersion < D3DPS_VERSION(1,1) )

         return E_FAIL;

caps结构会返回硬件可用的能力。要用D3DPS_VERSION宏检查当前硬件支持的所有着色器版本。如果caps返回的版本小于1.1,那么这个调用会失败。反之,对所有大于或等于1.1的版本,调用会成功。如果硬件不支持被测试的着色器版本,那么应用程序将不得不退而使用别的渲染方法(也许可以使用一个较低版本的着色器)。

第2步

这个示例使用了一个四边形,由两个三角形组成。每个顶点的数据结构包含了位置和漫反射色数据。D3DFVF_CUSTOMVERTEX宏定义了与顶点数据相匹配的数据结构。实际的顶点数据在全局数组g_Vertices中声明。四个顶点以原点为中心,每个顶点具有不同的漫反射色。

// 声明顶点数据结构。

struct CUSTOMVERTEX

{

    FLOAT x, y, z;

    DWORD diffuseColor;

};

 

// 声明自定义FVF宏。

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)

 

// 声明顶点位置和漫反射色数据。

CUSTOMVERTEX g_Vertices[]=

{

//      x      y      z   漫反射色

    { -1.0f, -1.0f, 0.0f, 0xffff0000 },  // 红 – 左下

    { +1.0f, -1.0f, 0.0f, 0xff00ff00 },  // 绿 – 右下

    { +1.0f, +1.0f, 0.0f, 0xff0000ff },  // 蓝 – 右上

    { -1.0f, +1.0f, 0.0f, 0xffffffff },  // 白 – 左上

};

第3步

这个着色器把经过高洛德插值的漫反射色数据复制到输出像素。着色器文件PixelShader.txt如下所示:

ps_1_1        // 版本指令

mov r0,v0     // 把顶点的漫反射色复制到输出寄存器。

像素着色器文件的第一条指令声明了像素着色器的版本,此处为1.1。

第二条指令把颜色寄存器(v0)的内容复制到输出寄存器(r0)。因为在第1步中声明的顶点数据已经包含了经过插值的漫反射色,所以颜色寄存器包含了顶点的漫反射色。输出寄存器决定渲染目标使用的像素颜色(因为本例中没有更多的处理,如雾,所以输出寄存器就是最终的像素颜色)。

第4步

像素着色器由像素着色器指令创建。本例中,指令被包含在一个单独的文件中。指令也可以被包含在一个文本字符串中。

LPD3DXBUFFER pCode;                  // 存放经过汇编的着色器代码的缓存

LPD3DXBUFFER pErrorMsgs;             // 存放错误信息的缓存

TCHAR        strPixelShaderPath[512];// 用来定位着色器文件

DXUtil_FindMediaFileCb( strPixelShaderPath, sizeof(strPixelShaderPath),

                        _T("PixelShader.txt") );

这个函数是示例框架使用的一个辅助函数,许多示例都以它为基础。

LPDIRECT3DPIXELSHADER9 m_pPixelShader;

D3DXAssembleShaderFromFile( strPixelShaderPath, NULL, NULL, 0, 

                            &pCode, &pErrorMsgs, NULL );  

m_pd3dDevice->CreatePixelShader( (DWORD*)pCode->GetBufferPointer(),

                                  &m_pPixelShader );

着色器创建完成后,指针m_pPixelShader用来对它进行引用。

第5步

除了用像素着色器句柄来设置着色器外,渲染输出像素的过程和使用固定功能流水线类似。

// 在本例中关闭关照。它不会对最终像素的颜色产生影响。

// 像素颜色完全由经过插值的顶点颜色决定。

m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

 

m_pd3dDevice->SetStreamSource( 0, m_pQuadVB, sizeof(CUSTOMVERTEX) );

m_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );

m_pd3dDevice->SetPixelShader( m_pPixelShader );

m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 2 );

源顶点数据由SetStreamSource设置。本例中,SetFVF使用在声明顶点数据时定义的FVF码告诉Direct3D进行固定功能顶点处理。顶点着色器和像素着色器既可以一起使用,也可以分开使用。可以用固定功能流水线代替这两者。SetPixelShader设置像素着色器,而DrawPrimitive则绘制四边形。