标题：像素着色器
第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则绘制四边形。
确认对像素着色器的支持
应用程序可以查询D3DCAPS9的成员以确定硬件对像素着色器所涉及的操作的支持程度。下表列出了与可编程像素处理有关的设备能力。
设备能力描述PixelShader1xMaxValue寄存器中可存储的值的范围为[-PixelShader1xMaxValue, PixelShader1xMaxValue]。这个值只对版本1.1到1.4有效。MaxSimultaneousTextures用于固定功能流水线，纹理取样器的数量为MaxTextureBlendStages除以MaxSimultaneousTextures。用于像素着色器的纹理取样器的数量在接下来的表中显示。PixelShaderVersion硬件支持的像素着色器的版本。版本号小于或等于该值的像素着色器被支持。
可用于像素着色器的纹理取样器的数量取决于像素着色器的版本。
像素着色器版本纹理取样器的数量ps_1_1 - ps_1_34个纹理取样器ps_1_46个纹理取样器ps_2_0 - ps_3_016个纹理取样器Fixed function pixel shaderMaxTextureBlendStages/MaxSimultaneousTextures个纹理取样器PixelShaderVersion的第一个字节包含次版本号，第二个字节包含主版本号。经过汇编的着色器的第一个标记就是像素着色器的版本。每种硬件实现都会设置该版本号，表示它能完全支持的像素着色器的最高版本。
纹理操作的转换
像素着色器在以下几个方面扩展并一般化了Microsoft DirectX® 6.0和7.0的多重纹理能力。
加入了一组通用读/写寄存器，这样就允许更为灵活的表达式。用D3DTA_CURRENT的连续级联需要为每层指定一个单独的结果寄存器参数。 D3DTOP_MODULATE2X和D3DTOP_MODULATE4X纹理操作被分成了单独的修饰符，可用于任何指令。这样就无需单独的D3DTOP_MODULATE和D3DTOP_MODULATE2X操作了。 乘加操作加入了可选的第三个参数，因此程序化的像素着色器可以执行arg1 ×arg2 + arg0的操作。这样就不再需要D3DTOP_MODULATEALPHA_ADDCOLOR和D3DTOP_MODULATECOLOR_ADDALPHA 纹理操作了。 混合操作加入了可选的第三个参数，因此程序化的像素着色器可以用arg0作为arg1和arg2之间的混合比。这样就不再需要D3DTOP_BLENDDIFFUSEALPHA, D3DTOP_BLENDTEXTUREALPHA, D3DTOP_BLENDFACTORALPHA, D3DTOP_BLENDTEXTUREALPHAPM, 和D3DTOP_BLENDCURRENTALPHA纹理操作了。 对纹理寻址的修改操作，如D3DTOP_BUMPENVMAP，被从颜色和阿尔法操作中分离出来并作为第三种操作类型，专门用于纹理寻址操作。 为了有效地支持这种新增的灵活性，API的语法从DWORD对改成了ASCII汇编代码语法。这样就暴露了程序化的像素着色器所提供的功能。
注意在使用像素着色器时，镜面反射加法不专门由一个渲染状态控制，如果需要，这可能由像素着色器实现。但是，雾混合仍然由固定功能流水线执行。
对纹理的一些考虑
像素着色器完全取代了由Microsoft DirectX® 6.0和7.0的多重纹理API提供的像素混合功能，尤其是那些由D3DTSS_COLOROP, D3DTSS_COLORARG1, D3DTSS_COLORARG2, D3DTSS_ALPHAOP, D3DTSS_ALPHAARG1和D3DTSS_ALPHAARG2纹理层状态、相关的参数和修饰符定义的操作。如果设置了程序化的像素着色器，那么这些状态会被忽略。
纹理层和取样器状态
如果像素着色器正在运行，那么以下纹理层状态仍然会被使用。对这些状态的使用取决于像素着色器的版本，如下表所示。
纹理层状态版本D3DTSS_BUMPENVMAT001_1 - 1_4D3DTSS_BUMPENVMAT011_1 - 1_4D3DTSS_BUMPENVMAT101_1 - 1_4D3DTSS_BUMPENVMAT111_1 - 1_4D3DTSS_BUMPENVLSCALE1_1 - 1_3D3DTSS_BUMPENVLOFFSET1_1 - 1_3D3DTSS_TEXCOORDINDEX1_1 - 1_3. 仅对固定功能顶点处理有效。D3DTSS_TEXTURETRANSFORMFLAGS1_1 - 1_3. 仅对固定功能顶点处理有效。如果像素着色器正在运行，那么下面的取样器状态仍然会被使用。
取样器状态版本D3DSAMP_ADDRESSU所有版本D3DSAMP_ADDRESSV所有版本D3DSAMP_ADDRESSW所有版本D3DSAMP_BORDERCOLOR所有版本D3DSAMP_MAGFILTER所有版本D3DSAMP_MINFILTER所有版本D3DSAMP_MIPFILTER所有版本D3DSAMP_MIPMAPLODBIAS所有版本D3DSAMP_MAXMIPLEVEL所有版本D3DSAMP_MAXANISOTROPY所有版本D3DSAMP_SRGBTEXTURE所有版本D3DSAMP_ELEMENTINDEX所有版本D3DSAMP_DMAPOFFSET仅顶点着色器（位移贴图）因为纹理层状态不是像素着色器的一部分，在编译着色器时它们是不可用的，所以驱动程序无法对它们做任何假定。例如，驱动程序不能在那时区分出双线性过滤和三线性过滤。应用程序可以自由地改变这些状态而无需重新生成当前绑定的着色器。
像素着色器和纹理取样
纹理取样和过滤操作由标准纹理层状态中的放大、缩小、mip过滤、以及环绕寻址模式控制。更多信息，请参阅纹理层状态。因为驱动程序在编译时也无法得到这些信息，所以着色器必须能够在这些状态改变后继续运行。应用程序负责设置像素着色器所需的正确类型的纹理（二维贴图，立方体贴图，立体贴图等）。如果设置不正确的纹理类型，那么将会产生不可预料的结果。
在像素着色器之后的处理
其它一些像素操作——如雾混合、模板操作、以及渲染目标混合——在着色器执行以后发生。为了支持本主题描述的新特性，渲染目标混合的语法已经做了更新。像素着色器的输入对像素着色器版本1.1到2.0来说，漫反射色和镜面反射色在给着色器使用之前被截取到范围0到1之间，因为这是着色器的有效输入范围。
输入到像素着色器的颜色值被认为是经过透视校正的，但并非所有硬件都能保证这一点。寻址处理器根据纹理坐标产生的颜色总是以透视校正的方式进行迭代。但是，在迭代过程中，它们也会被截取到范围0到1之间。像素着色器输出对像素着色器版本1.1到1.4来说，像素着色器产生的输出是寄存器r0的内容。该寄存器的值会在着色器处理结束后被送往雾处理阶段和渲染目标混合器。
对像素着色器版本2.0以上来说，输出的颜色值为oC0到oC4。
像素着色器示例
本节包含三个像素着色器示例。每个示例都建立在前一个示例的基础上，并增加一些功能。
应用纹理贴图 把顶点漫反射色和纹理进行混合 用颜色值把两张纹理进行混合
应用纹理贴图
本示例把一张纹理贴图应用于一个四边形。本例和前例的区别在于：
顶点数据结构和FVF码包含了纹理坐标。顶点数据包含了u, v数据。因为像素的颜色将从纹理贴图得到，所以顶点数据不再需要漫反射色。 用IDirect3DDevice9::SetTexture把纹理连接到纹理层0。 着色器用t0纹理寄存器取代v0漫反射色寄存器。 示例代码如下：
// 定义顶点数据结构。
struct CUSTOMVERTEX
{
FLOAT x, y, z;
FLOAT u1, v1;
};
// 定义相应的FVF码。
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_TEX|D3DFVF_TEXCOORDSIZE2(0))
// 创建包含位置和纹理坐标的顶点数据。
static CUSTOMVERTEX g_Vertices[]=
{
//  x      y     z     u1    v1
{ -1.0f, -1.0f, 0.0f,  0, 1, }, 
{  1.0f, -1.0f, 0.0f,  1, 1, }, 
{  1.0f,  1.0f, 0.0f,  1, 0, }, 
{ -1.0f,  1.0f, 0.0f,  0, 0, }, 
// 为了和Windows从上到下的约定一致，v1被颠倒了。
// 左上角的纹理坐标为(0,0)。
// 右下角的纹理坐标为(1,1)。
};
// 创建纹理。这个文件包含在供下载的DirectX 9.0 SDK的媒体文件中。
TCHAR        strTexturePath[512];
DXUtil_FindMediaFileCb( strTexturePath, sizeof(strTexturePath), 
_T("DX5_Logo.bmp") );
LPDIRECT3DTEXTURE9      m_pTexture0;
D3DUtil_CreateTexture( m_pd3dDevice, strTexturePath, 
&m_pTexture0, D3DFMT_R5G6B5 );
// Create the pixel shader.
TCHAR        strShaderPath[512];
DXUtil_FindMediaFileCb( strShaderPath, sizeof(strShaderPath), 
_T("PixelShader2.txt") );
这个函数是示例框架使用的一个辅助函数，许多示例都以它为基础。
LPD3DXBUFFER pCode;                  // 用来存放经过汇编的着色器代码的缓存
LPD3DXBUFFER pErrorMsgs;             // 用来存放错误信息的缓存
LPDIRECT3DPIXELSHADER9 m_pPixelShader;
D3DXAssembleShaderFromFile( strShaderPath, 0, NULL, &pCode, NULL );
m_pd3dDevice->CreatePixelShader( (DWORD*)pCode->GetBufferPointer(),
&m_pPixelShader );
m_pd3dDevice->SetPixelShader( m_pPixelShader );
// 载入纹理并渲染输出像素。
m_pd3dDevice->SetTexture( 0, m_pTexture0 );        
m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 2 );
// 文件"PixelShader2.txt"的内容
// 把纹理贴图应用于物体的顶点。
ps_1_1      // 文件的第一条必须是版本指令。
tex t0      // 声明纹理寄存器t0，从纹理层0载入。
mov r0, t0  // 把纹理寄存器数据(t0)复制到输出寄存器(r0)。
得到的图像如下面所示。
把顶点漫反射色和纹理进行混合
本例把纹理贴图中的颜色与顶点的颜色进行混合。本例与前例的区别如下：
顶点的数据结构，FVF码和顶点数据包含了漫反射色。 着色器文件用乘法(mul)指令把纹理的颜色和顶点的漫反射色进行混合。 创建纹理和载入纹理的代码是相同的，放在这里是为了保持代码的完整性。
struct CUSTOMVERTEX
{
FLOAT x, y, z;
DWORD color1;
FLOAT tu1, tv1;
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1|
D3DFVF_TEXCOORDSIZE2(0))
static CUSTOMVERTEX g_Vertices[]=
{
//  x      y     z     diffuse     u1    v1
{ -1.0f, -1.0f, 0.0f, 0xffff0000, 0, 1, }, // 红
{  1.0f, -1.0f, 0.0f, 0xff00ff00, 1, 1, }, // 绿
{  1.0f,  1.0f, 0.0f, 0xff0000ff, 1, 0, }, // 蓝
{ -1.0f,  1.0f, 0.0f, 0xffffffff, 0, 0, }, // 白
// 为了和Windows从上到下的约定一致，v1被颠倒了。
// 左上角的纹理坐标为(0,0)。
// 右下角的纹理坐标为(1,1)。
};
// 创建纹理。这个文件包含在供下载的DirectX 9.0 SDK的媒体文件中。
TCHAR        strTexturePath[512];
DXUtil_FindMediaFileCb( strTexturePath, sizeof(strTexturePath), 
_T("DX5_Logo.bmp") );
LPDIRECT3DTEXTURE9      m_pTexture0;
D3DUtil_CreateTexture( m_pd3dDevice, strTexturePath, 
&m_pTexture0, D3DFMT_R5G6B5 );
// 创建像素着色器。
TCHAR        strShaderPath[512];
DXUtil_FindMediaFileCb( strShaderPath, sizeof(strShaderPath), 
_T("PixelShader3.txt") );
LPD3DXBUFFER pCode;                  // 用来存放经过汇编的着色器代码的缓存
LPD3DXBUFFER pErrorMsgs;             // 用来存放错误信息的缓存
LPDIRECT3DPIXELSHADER9 m_pPixelShader;
D3DXAssembleShaderFromFile( strShaderPath, 0, NULL, &pCode, NULL );
m_pd3dDevice->CreatePixelShader( (DWORD*)pCode->GetBufferPointer(),
&m_pPixelShader );
m_pd3dDevice->SetPixelShader( m_pPixelShader );
// 载入纹理并渲染输出像素。
m_pd3dDevice->SetTexture( 0, m_pTexture0 );
m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 2 );
// 文件"PixelShader3.txt"的内容
ps_1_1          // 版本指令
tex t0          // 声明纹理寄存器t0, 从纹理层0载入
mul r0, v0, t0  // v0*t0, 然后复制到r0
着色器的输入如下面所示。第一幅图为顶点颜色，第二幅图为纹理贴图。
得到的图像如下面所示，也就是顶点的颜色和纹理贴图混合的结果。
用颜色值把两张纹理进行混合
本例把两张纹理贴图进行混合，顶点的颜色用来决定每张纹理贴图的颜色所占的比例。本例和前例的区别如下：
因为使用了两张纹理，所以顶点的数据结构，FVF码，以及顶点数据包含了第二组纹理坐标。另外IDirect3DDevice9::SetTexture也调用了两次，并设置两个纹理层的状态。 着色器文件声明了两个纹理寄存器并使用线性插值(lrp)指令把两张纹理进行混合。漫反射色的值用来决定两张纹理在输出的颜色中所占的比例。 下面是示例代码。
struct CUSTOMVERTEX
{
FLOAT x, y, z;
DWORD color;
FLOAT tu1, tv1;
FLOAT tu2, tv2;    // 第二组纹理坐标
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3D_FVF_DIFFUSE|D3DFVF_TEX2|
D3DFVF_TEXCOORDSIZE4(0))
static CUSTOMVERTEX g_Vertices[]=
{
//  x      y     z     color       u1    v1    u2    v2
{ -1.0f, -1.0f, 0.0f, 0xff0000ff, 1.0f, 1.0f, 1.0f, 1.0f },
{ +1.0f, -1.0f, 0.0f, 0xffff0000, 0.0f, 1.0f, 0.0f, 1.0f },
{ +1.0f, +1.0f, 0.0f, 0xffffff00, 0.0f, 0.0f, 0.0f, 0.0f },
{ -1.0f, +1.0f, 0.0f, 0xffffffff, 1.0f, 0.0f, 1.0f, 0.0f },
};
// 创建纹理。这个文件包含在供下载的DirectX 9.0 SDK的媒体文件中。
TCHAR        strTexturePath[512];
LPDIRECT3DTEXTURE9      m_pTexture0, m_pTexture1;
DXUtil_FindMediaFileCb( strTexturePath, sizeof(strTexturePath), 
_T("DX5_Logo.bmp") );
D3DUtil_CreateTexture( m_pd3dDevice, strTexturePath, 
&m_pTexture0, D3DFMT_R5G6B5 );
DXUtil_FindMediaFileCb( strTexturePath, sizeof(strTexturePath), 
_T("snow2.jpg") );
D3DUtil_CreateTexture( m_pd3dDevice, strTexturePath, 
&m_pTexture0, D3DFMT_R5G6B5 );
// 创建像素着色器。
TCHAR        strShaderPath[512];
DXUtil_FindMediaFileCb( strShaderPath, sizeof(strShaderPath), 
_T("PixelShader4.txt") );
LPD3DXBUFFER pCode;                  // 用来存放经过汇编的着色器代码的缓存
LPD3DXBUFFER pErrorMsgs;             // 用来存放错误信息的缓存
LPDIRECT3DPIXELSHADER9 m_pPixelShader;
D3DXAssembleShaderFromFile( strShaderPath, 0, NULL, &pCode, NULL );
m_pd3dDevice->CreatePixelShader( (DWORD*)pCode->GetBufferPointer(),
&m_pPixelShader );
m_pd3dDevice->SetPixelShader( m_pPixelShader );
// Load the textures stages.
m_pd3dDevice->SetTexture( 0, m_pTexture0 );
m_pd3dDevice->SetTexture( 1, m_pTexture1 );  // 使用第二层纹理
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 );
// 文件"PixelShader4.txt"的内容
ps_1_1              // 像素着色器版本。
tex t0              // 纹理寄存器t0从第0层载入。
tex t1              // 纹理寄存器t1从第1层载入。
mov r1, t1          // 把纹理t1复制到输出寄存器r1。
lrp r0, v0, t0, r1  // 用v0中指定的比例在t0和r1间进行线性插值。
得到的图像如下：
调试
用Visual Studio进行调试
Microsoft Visual Studio 存在一个扩展以支持对某些类型的顶点着色器进行调试。更多信息请参阅着色器调试器。
其它着色器调试器
一些图形芯片厂商在他们的网站提供着色器调试工具。可以通过查找互联网或阅读下面的文章找到这些工具。读者可以在程序运行的过程中把调试器连上去，并用调试器单步运行着色器。通过设置断点，读者可以一次执行一行着色器代码并观察寄存器状态的变化。有关顶点着色器和调试技巧的更多信息。
对纹理混合进行调试
另一个示例程序MFC Tex Sample是SDK安装的组成部分。这个MFC程序是学习如何在固定功能流水线中进行多重纹理混合操作的一个不错的方法。