本文的代码,来自D3D11微软自带的project-7 demo。
先贴出一段HLSL代码,本文将以这段HLSL为例,说明如何在C++中更新HLSL的常量:
Texture2D txDiffuse : register(t0);
SamplerState samLinear : register(s0);
cbuffer cbNeverChanges : register(b0)
{
matrix View;
};
cbuffer cbChangeOnResize : register(b1)
{
matrix Projection;
};
cbuffer cbChangesEveryFrame : register(b2)
{
matrix World;
float4 vMeshColor;
};
//-----------------------------------------------
struct VS_INPUT
{
float4 Pos : POSITION;
float2 Tex : TEXCOORD0;
};
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD0;
};
//-----------------------------------------------
PS_INPUT VS(VS_INPUT input)
{
PS_INPUT output = (PS_INPUT)0;
output.Pos = mul(input.Pos, World);
output.Pos = mul(output.Pos, View);
output.Pos = mul(output.Pos, Projection);
output.Tex = input.Tex;
return output;
}
float4 PS(PS_INPUT input) : SV_Target
{
return txDiffuse.Sample(samLinear, input.Tex) * vMeshColor;
}
一般在定义常量时,根据数据被修改的频率,将修改频率不同的数据定义在不同的cbuffer中,这样可以提升效率。如上所示的HLSL代码,定义了以下几个常量:
matrix View; // 几乎没有修改
matrix Projection; // 修改频繁很小
matrix World; // 修改频繁较大
float4 vMeshColor; // 修改频繁较大
以下是C++中更新这几个变量的逻辑。主要有三个函数:初始化、更新、使用
ID3D11Buffer* g_pCBNeverChanges = nullptr;
ID3D11Buffer* g_pCBChangeOnResize = nullptr;
ID3D11Buffer* g_pCBChangesEveryFrame = nullptr;
struct CBNeverChanges
{
XMMATRIX mView;
};
struct CBChangeOnResize
{
XMMATRIX mProjection;
};
struct CBChangesEveryFrame
{
XMMATRIX mWorld;
XMFLOAT4 vMeshColor;
};
void InitConstantBuffer()
{
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bd.ByteWidth = sizeof(CBNeverChanges);
g_pd3dDevice->CreateBuffer(&bd, nullptr, &g_pCBNeverChanges);
bd.ByteWidth = sizeof(CBChangeOnResize);
g_pd3dDevice->CreateBuffer(&bd, nullptr, &g_pCBChangeOnResize);
bd.ByteWidth = sizeof(CBChangesEveryFrame);
g_pd3dDevice->CreateBuffer(&bd, nullptr, &g_pCBChangesEveryFrame);
}
void UpdateConstantBuffer()
{
CBNeverChanges cbNeverChanges;
cbNeverChanges.mView = ...; // 赋值
g_pD3DDeviceContext->UpdateSubresource(g_pCBNeverChanges, 0, nullptr, &cbNeverChanges, 0, 0);
CBChangeOnResize cbChangesOnResize;
cbChangesOnResize.mProjection = ...; // 赋值
g_pD3DDeviceContext->UpdateSubresource(g_pCBChangeOnResize, 0, nullptr, &cbChangesOnResize, 0, 0);
CBChangesEveryFrame cb;
cb.mWorld = ...; // 赋值
cb.vMeshColor = ...; // 赋值
g_pD3DDeviceContext->UpdateSubresource(g_pCBChangesEveryFrame, 0, nullptr, &cb, 0, 0);
}
void Render()
{
// clear
g_pD3DDeviceContext->ClearRenderTargetView(g_pRenderTargetView, Colors::MidnightBlue);
g_pD3DDeviceContext->ClearDepthStencilView(g_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
// render
g_pD3DDeviceContext->VSSetShader(g_pVertexShader, nullptr, 0);
g_pD3DDeviceContext->VSSetConstantBuffers(0, 1, &g_pCBNeverChanges);
g_pD3DDeviceContext->VSSetConstantBuffers(1, 1, &g_pCBChangeOnResize);
g_pD3DDeviceContext->VSSetConstantBuffers(2, 1, &g_pCBChangesEveryFrame); // 顶点着色器用到了全部三个常量区,所以需要设置3个常量区
g_pD3DDeviceContext->PSSetShader(g_pPixelShader, nullptr, 0);
g_pD3DDeviceContext->PSSetConstantBuffers(2, 1, &g_pCBChangesEveryFrame); // 像素着色器只用到了第三个常量区,所以只需要设置索引为2的常量区
g_pD3DDeviceContext->PSSetShaderResources(0, 1, &g_pTextureRV); // 对应HLSL的 Texture2D txDiffuse
g_pD3DDeviceContext->PSSetSamplers(0, 1, &g_pSamplerLinear); // 对应HLSL的 SamplerState samLinear
g_pD3DDeviceContext->DrawIndexed(36, 0, 0);
g_pSwapChain->Present(0, 0);
}