(注:【D3D11游戏编程】学习笔记系列由优快云作者BonChoix所写,转载请注明出处:http://blog.youkuaiyun.com/BonChoix,谢谢~)
历代的D3D教程中,介绍基本绘图时都会拿立方体作为例子,这次也不例外~ 立方体虽然简单,但正所谓麻雀虽小,五脏俱全。绘制立方体的过程其实已经包含了所有D3D渲染程序中最基本的、必不可少的步骤。因此,从绘制立方体开始学习D3D绘图,既简单易懂,又可以掌握绘图程序的核心步骤,效率自然会很高。
在学习D3D11绘图前,有几个重要的概念尤其需要深刻理解,它们是所有D3D11程序的基础。在没有掌握这些基本概念的情况下是不可能学会D3D11编程的。下面对这些概念逐一进行介绍。
一、 Vertex Shader和Pixel Shader
这两个是写D3D11程序时必不可少的最基本的Shader。最名字上也可以直接看出,Vertex Shader(顶点着色器)是针对每个顶点进行操作的,它的输入是一个顶点,包含用户指定的顶点的基本信息,输出是该顶点转换后的信息。最常见的顶点着色器即对顶点的位置坐标、纹理坐标、法线等信息进行变换,并返回新的顶点,传递给下一个阶段。一般针对顶点着色器的输入与输出,程序员各指定相应的结构来存储相应的顶点信息,如:
struct VertexIn
{
float3 pos : POSITION;
float4 color : COLOR;
};
就是一个简单的输入结构,float3和float4是HLSL内置类型,即用来存放3个float和4个float,此外还有其他很多类型诸如float4x4(4x4矩阵)等,详细可参考HLSL相关资料。pos和color即相应的成员变量名,"POSITION”和"COLOR",同来对相应的成员变量进行语义说明,"POSITION"很显然指位置坐标,"COLOR"即顶点的颜色。这些语义说明在C++程序中创建输入布局(InputLayout)时会用来,并且必须要一致,这个在本文稍后介绍InputLayout时还会解释。
struct VertexOut
{
float4 posH : SV_POSITION;
float4 color : COLOR;
};
这个结构即顶点着色器的输出结构,与输入基本类似,但pos对应地变成了float4类型,这个是齐次坐标下的位置,即经过投影变换后的坐标,这时的顶点坐标必须是float4类型,还有一点重要的是,它对应的语义说明SV_POSITION,也是固定的,”SV“即"System Value",系统值,在像素着色器阶段会需要这个值来进行裁剪操作。除了系统值必须固定外,其他的语义值都是可以任意指定的(虽然可以任意指定,但一般情况下显然是以变量真实作用为依据嘛)。
有了该两个结构,顶点着色器函数如下:
VertexOut VS(VertexIn vin)
{
VertexOut vout;
vout.posH = mul(float4(vin.pos,1.f),g_worldViewProj);
vout.color = vin.color;
return vout;
}
该函数名是可以任意指定的,其参数为输入顶点结构,输出为相应的输出顶点结构。在这个函数里面,它实现的是一个最简单的功能,即坐标变换和颜色的简单传递。这里用到了几个HLSL内置函数:mul用来实现向量、矩阵的相乘,其维数是可度的,比如float3和float3x3相乘,float4和float4x4相乘,也可以进行相同维数的矩阵的相乘。由于输出坐标要求为float4类型,因些相乘时需要float4和float4x4。这里float4通过复制输入顶点后接第4个参数1.f作为构造函数生成,g_worldViewProj为一个全局变量,类型为float4x4,用于进行世界、视角、投影变换,三个变量合成到一个矩阵中实现。
以上就是一个最简单的顶点着色器,下面来看像素着色器。显然,像素着色器是针对逐个像素进行的。在上一篇文章介绍3D渲染管线时提到过,在光栅化阶段,一个三角形在其所覆盖的每个像素处使用插值来计算相应顶点的各个属性,然后把插值后的顶点传递给像素着色器。在简单的没有Geometry Shader和Tessellator时,顶点着色器的输出就是像素着色器的输入,像素着色器最终的输出是该像素处对应片段的颜色值,即float4类型。如下即是一个最简单的像素着色器函数:
float4 PS(VertexOut pin):SV_TARGET
{
return pin.color;
}
该函数参顶点着色器输出结构作为参数,输出为float4类型,注意函数名后面的语义说明:SV_TARGET,它是函数返回值的说明,显然这也是一个系统值,不可更改,它作为相应片段的颜色值传递给下一个阶段(Output Merger)。函数功能相当简单,即简单的颜色值的传递。
常量缓冲区
常量缓冲区在HLSL在用来存放全局变量,供着色器使用,在HLSL中即”cbuffer"。如上面例子当中g_worldViewProj就是一个常量缓冲区中的全局变量。定义如下:
cbuffer PerFrame
{
float4x4 g_worldViewProj;
};
在着色器中,不同的全局变量有着不同的更新频率。比如g_worldViewProj,每个物体都有其相应的变换矩阵,因此在绘制每个物体前都要更新其对应的矩阵,即这个变量为针对单个物体的;此外,比如光源的属性:位置、方向、颜色等,这些信息在绘制每一帧场景过程中是保持不变的,即这些变量更新频率为“每帧”。在写着色器程序时,把相同更新频率的全局变量放在一起,定义在同一个常量缓