cbuffer和tbuffer,Shader Model 4支持的新特性,通过打包数据可以获得更好的性能。
关于Shader与应用程序间的数据传递。要传递的数据主要有 constant buffer,SamplerState,Texture2D(resource)。
这里要先提一下DirectX10中新引入的constant buffer。在DX10中,constant存放于常量缓冲区中,每个常量缓冲区由 4096个常量寄存器组成,共有16个常量缓冲区。
constant buffer会为 两种:cbuffer和tbuffer。注意tbuffer是并不是用来存储纹理的,而是指可以像纹理那样来访问其中的数据,对于索引类数据有更好的性能。
为什么使用cbuffer?
cbuffer通过允许将着色常量组合在一起并同时提交,而不是单独调用以提交每个常量,从而减少更新着色常量所需的带宽。
有效使用常量缓冲区的最佳方法是把更新比较频繁的着色器变量组织为cbuffer。这允许应用程序最小化更新着色器常量所需的带宽。
HLSL打包规则
HLSL打包规则类似于使用Visual Studio执行pragma pack 4,它将数据打包成4字节的边界。此外,HLSL打包数据,使其不跨越16字节的边界。变量被压缩到一个给定的四分量向量中,直到变量跨越一个四分量边界;下一个变量将被放到下一个四分量向量。更详细的讨论:https://blog.csdn.net/X_Jun96/article/details/81283268
Direct3D 9与Direct3D 10和11之间的差异:
与Direct3D 9中常量的自动分配(不执行打包,而是将每个变量分配给一组float4寄存器)不同,HLSL常量变量遵循Direct3D 10和11中的打包规则。
来看实例:
在shader中有如下定义
cbuffer VSConstantBuffer : register(b0)
{
matrix g_World;
matrix g_View;
matrix g_Proj;
matrix g_WorldInvTranspose;
}
register(bN):b表示constant buffer,N为input slot (0-15),能使用的为0-13,预留2个插槽供内部使用 。
即表示Mybuffer存放于b3中。
在VS_3D函数中使用cbuffer中的常量:
// 顶点着色器(3D)
VertexPosHWNormalTex VS_3D(VertexPosNormalTex vIn)
{
VertexPosHWNormalTex vOut;
matrix viewProj = mul(g_View, g_Proj);
float4 posW = mul(float4(vIn.PosL, 1.0f), g_World);
vOut.PosH = mul(posW, viewProj);
vOut.PosW = posW.xyz;
vOut.NormalW = mul(vIn.NormalL, (float3x3) g_WorldInvTranspose);
vOut.Tex = vIn.Tex;
return vOut;
}
在应用程序C++代码中:
// 设置常量缓冲区描述
D3D11_BUFFER_DESC cbd;
ZeroMemory(&cbd, sizeof(cbd));
cbd.Usage = D3D11_USAGE_DYNAMIC;
cbd.ByteWidth = sizeof(VSConstantBuffer);
cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
// 新建用于VS和PS的常量缓冲区
m_pd3dDevice->CreateBuffer(&cbd, nullptr, m_pConstantBuffers[0].GetAddressOf());
cbd.ByteWidth = sizeof(PSConstantBuffer);
m_pd3dDevice->CreateBuffer(&cbd, nullptr, m_pConstantBuffers[1].GetAddressOf());
// 初始化用于VS的常量缓冲区的值
m_VSConstantBuffer.world = XMMatrixIdentity();
m_VSConstantBuffer.view = XMMatrixTranspose(XMMatrixLookAtLH(
XMVectorSet(0.0f, 0.0f, -5.0f, 0.0f),
XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f),
XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f)
));
m_VSConstantBuffer.proj = XMMatrixTranspose(XMMatrixPerspectiveFovLH(XM_PIDIV2, AspectRatio(), 1.0f, 1000.0f));
m_VSConstantBuffer.worldInvTranspose = XMMatrixIdentity();
// VS常量缓冲区对应HLSL寄存于b0的常量缓冲区
m_pd3dImmediateContext->VSSetConstantBuffers(0, 1, m_pConstantBuffers[0].GetAddressOf());
第一个参数即为要传递的buffer放置的slot起点。类似的函数PSSetConstantBuffers,GSSetConstantBuffers。通过VSSetConstantBuffers方法,为cbuffer的常量赋值。
// 更新PS常量缓冲区资源
D3D11_MAPPED_SUBRESOURCE mappedData;
m_pd3dImmediateContext->Map(m_pConstantBuffers[1].Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData);
memcpy_s(mappedData.pData, sizeof(PSConstantBuffer), &m_PSConstantBuffer, sizeof(PSConstantBuffer));
m_pd3dImmediateContext->Unmap(m_pConstantBuffers[1].Get(), 0);
然后,用Map方法更新频繁更改的常量。
像纹理Texture那样来访问其中的数据,语法为register(tN), t 表示纹理,N 为input slot (0-127) 。
纹理并不能直接绑定到着色器中,需要为纹理创建对应的着色器资源视图才能够给着色器使用
例,PS中主要代码:
Texture2D g_Tex : register(t0);
SamplerState g_SamLinear : register(s0);
// 像素着色器(2D)
float4 PS_2D(VertexPosHTex pIn) : SV_Target
{
return g_Tex.Sample(g_SamLinear, pIn.Tex);
}
应用程序C++代码中:
// 创建Texture
CreateDDSTextureFromFile(m_pd3dDevice.Get(), L"Texture\\WoodCrate.dds", nullptr, m_pWoodCrate.GetAddressOf());
// 设置着色器资源
m_pd3dImmediateContext->PSSetShaderResources(0, 1, m_pWoodCrate.GetAddressOf());
通过 PSSetShaderResources 为HLSL中的g_Tex赋值,然后,调用Texture的采样方法,进行纹理采样。
语法为register(sN), s 表示采样器,s 为input slot (0-127) 。
例,PS中主要代码:
g_Tex.Sample函数是采样方法, 采样就是根据纹理坐标取出纹理中对应位置最接近的像素,返回一个float4的向量。
Texture2D g_Tex : register(t0);
SamplerState g_SamLinear : register(s0);
// 像素着色器(2D)
float4 PS_2D(VertexPosHTex pIn) : SV_Target
{
return g_Tex.Sample(g_SamLinear, pIn.Tex);
}
应用程序C++代码中:
// 初始化采样器状态描述
D3D11_SAMPLER_DESC sampDesc;
ZeroMemory(&sampDesc, sizeof(sampDesc));
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;//过滤器
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;//寻址模式
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
sampDesc.MinLOD = 0;
sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
// 创建采样器状态
m_pd3dDevice->CreateSamplerState(&sampDesc, m_pSamplerState.GetAddressOf());
// 像素着色阶段设置好采样器
m_pd3dImmediateContext->PSSetSamplers(0, 1, m_pSamplerState.GetAddressOf());
PSSetSamplers第一个参数就是对应register(s0),采样寄存器的第一个插槽位置。也就是为对应HLSL的PS阶段的shader代码中的g_SamLinear变量赋值,然后在PS中进行采样。
附录:DirectX11--深入理解与使用2D纹理资源
https://blog.csdn.net/X_Jun96/article/details/81010611