1,Vertex Shader
struct VertexIn
{
float3 pos : POSITION;
float4 color : COLOR;
};
上图的float3和float4是HLSL内置类型。
级着色器语言(High Level Shader Language,简称HLSL),由微软拥有及开发的一种语言,HLSL 独立的工作在 Windows 平台上,只能供微软的Direct3D使用。 HLSL是微软抗衡GLSL的产品,同时不能与OpenGL标准兼容。他跟Nvidia的Cg非常相似。 HLSL的主要作用为将一些复杂的图像处理,快速而又有效率地在显示卡上完成,与组合式或低阶Shader Language相比,能降低在编写复杂特殊效果时所发生编程错误的机会。 HLSL已经整合到了 DirectX 9中。
HLSL我会在后面专门写一篇笔记学习
POSITION和COLOR分别对应顶点的坐标以及颜色向量,这两个声明是在C++语言中设置输入布局InputLayout内容设定的。
struct VertexOut
{
float4 posH : SV_POSITION;
float4 color : COLOR;
};
Shader计算顶点的函数模型如下:
VertexOut VS(VertexIn in)
{
VertexOut out;
out.pos=mul(float4(in.pos,1),modelMatrix);
out.pos=mul(out.pos,worldMatrix);
out.pos=mul(out.pos,viewMatrix);
out.color=in.color;
return out;
}
2.Pixel Shader
根据管线渲染步骤,顶点Shader计算是先于像素Shader计算的,在光栅化阶段,一个三角形在其所覆盖的每个像素处使用插值来计算相应顶点的各个属性,然后把插值后的顶点传递给像素着色器。在简单的没有Geometry Shader和Tessellator时,顶点着色器的输出就是像素着色器的输入
float4 PS(VertexOut pin):SV_TARGET
{
return pin.color;
}
SV_TARGET是系统值,表示该函数返回的是用于下一个阶段OutPut Merger的颜色值,这里只简单的做了颜色值得传递。
3.常量缓冲区
在HLSL中,设置常量缓冲区声明为cbuffer,在着色器Shader代码里,我们经常把刷新频率相似的常量放置在相同的常量缓存中。如世界矩阵以及其他转换矩阵是针对当个物体设计,放在同一个常量缓存里,而场景灯光的位置,朝向,颜色等这下针对整个场合都要每帧渲染的的数据,以相同格式放在一个cbuffer里
4.Effect框架利用
Effect框架是微软提供给我们的一个开源的代码库,用来把shader代码和相应的渲染状态合理的组织到一起,来实现一个针对性的特效。比如渲染水面、云彩、阴影等不同特效时,需要用到不同的渲染状态,这样每个特效都可以写成单独的Effect。
Effect组成为:
technique11:一个特效的一种渲染方法即为technique11,一个特效可以有多种方法。
pass:一个technique11必须包含至少一个pass,指的是物体的实际渲染过程,一个technique11可以包含多个pass以实现混合效果
下面为一个特效的简单effect
cbuffer PerFrame
{
float4x4 g_worldViewProj;
};
struct VertexIn
{
float3 pos : POSITION;
float4 color : COLOR;
};
struct VertexOut
{
float4 posH : SV_POSITION;
float4 color : COLOR;
};
VertexOut VS(VertexIn vin)
{
VertexOut vout;
vout.posH = mul(float4(vin.pos,1.f),g_worldViewProj);
vout.color = vin.color;
return vout;
}
float4 PS(VertexOut pin):SV_TARGET
{
return pin.color;
}
technique11 BasicDraw
{
Pass p0
{
SetVertexShader(CompileShader(vs_5_0,VS()));
SetPixelShader(CompileShader(ps_5_0,PS()));
}
}
既然我们使用了Effect来封装一整个渲染的过程,那在C++里面为其编辑就要用到effect专有的接口了
有ID3DX11Effect,technique11为ID3D11EffectTechnique。
改变shader里的数据需要通过相应的接口
ID3DX11EffectMatixVariable 对应 Float4x4接口
ID3DX11EffectVetorVariable 对应 Float4接口
ID3DX11EffectVariable 对已无类型接口
这些接口通过如下方式获得:
这里的“g_worldViewProj”即shader中的全局变量名字。之后就可以在C++程序中通过更改g_fxWorldViewProj接口来更新shader中相应的全局变量了:
4输入布局(input layout)
设置输入布局主要是为了指定GPU按特定格式读取顶点缓存中的Void*数据,在未定义输入布局之前,顶点缓存中的所有数据都为Void*类型,即为GPU对其是陌生的。
在顶点结构中,针对每个成员,我们要定义一个相应的D3D11_INPUT_ELEMENT_DESC。这个结构定义了该成员相应的有关信息,定义如下:
typedef struct D3D11_INPUT_ELEMENT_DESC {
LPCSTR SemanticName;
UINT SemanticIndex;
DXGI_FORMAT Format;
UINT InputSlot;
UINT AlignedByteOffset;
D3D11_INPUT_CLASSIFICATION InputSlotClass;
UINT InstanceDataStepRate;
} D3D11_INPUT_ELEMENT_DESC;
具体信息见
Direct11参数列表中的条目1;
接下来我们就对每一个顶点的参数成员定义一个D3DD_INPUT_ELEMENT_DESC,并组成数组
D3D11_INPUT_ELEMENT_DESC inputDesc[2] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
接下来创建这个input layout
HRESULT ID3D11Device::CreateInputLayout(
[in] const D3D11_INPUT_ELEMENT_DESC *pInputElementDescs,
[in] UINT NumElements,
[in] const void *pShaderBytecodeWithInputSignature,
[in] SIZE_T BytecodeLength,
[out] ID3D11InputLayout **ppInputLayout
);
对于顶点,我们需要为其设定一个缓存,即buffer。当然,指定顶点索引的数组也需要为其设定一个索引缓存。
建立缓存前,我们需要设计一个缓存描述符来描述缓存格式,所有的缓存都可以用D3D11_BUFFER_DESC
typedef struct D3D11_BUFFER_DESC {
UINT ByteWidth;
D3D11_USAGE Usage;
UINT BindFlags;
UINT CPUAccessFlags;
UINT MiscFlags;
UINT StructureByteStride;
} D3D11_BUFFER_DESC;
接下来,我们用到D3D11_SUBRESOURCE_DATA来之定缓存的数据源
typedef struct D3D11_SUBRESOURCE_DATA {
const void *pSysMem;
UINT SysMemPitch;
UINT SysMemSlicePitch;
} D3D11_SUBRESOURCE_DATA;
这样,物体顶点数据的准备工作就全部完成了,接下来就是相关渲染的准备
5.渲染阶段
一般为下几个步骤:
1,清屏
2,指定inputlayout,顶点缓存,索引缓存,图元拓扑类型
3,宣传technique11,逐步进行pass渲染
g_deviceContext->IASetInputLayout(g_inputLayout);
//指定顶点缓存
UINT stride = sizeof(Vertex);
UINT offset = 0;
g_deviceContext->IASetVertexBuffers(0,1,&g_VB,&stride,&offset);
//指定索引缓存
g_deviceContext->IASetIndexBuffer(g_IB,DXGI_FORMAT_R32_UINT,0);
//指定图元拓扑类型
g_deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3DX11EffectTechnique *tech = g_effect->GetTechniqueByName("BasicDraw");
D3DX11_TECHNIQUE_DESC techDesc;
tech->GetDesc(&techDesc);
for(UINT i=0; iGetPassByIndex(i)->Apply(0,g_deviceContext);
g_deviceContext->DrawIndexed(36,0,0);
}