D3D1渲染技術之取樣
在前面的部落格,我們看到除了紋理資料之外,還有兩個與使用紋理相關的關鍵概念:紋理過濾和定址模式。 取樣紋理資源時使用的過濾器和定址模式由取樣器物件定義, 應用程式通常需要多個取樣器物件以不同方式對紋理進行取樣。
建立取樣器
正如我們將在下面看到的,取樣器用於著色器, 為了將取樣器繫結到著色器以供使用,我們需要將描述符繫結到取樣器物件。 以下程式碼顯示了示例根簽名,以便第二個槽採用繫結到取樣器暫存器槽0的一個取樣器描述符的表。
CD3DX12_DESCRIPTOR_RANGE descRange[3]; descRange[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); descRange[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0); descRange[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0); CD3DX12_ROOT_PARAMETER rootParameters[3]; rootParameters[0].InitAsDescriptorTable(1, &descRange[0], D3D12_SHADER_VISIBILITY_PIXEL); rootParameters[1].InitAsDescriptorTable(1, &descRange[1], D3D12_SHADER_VISIBILITY_PIXEL); rootParameters[2].InitAsDescriptorTable(1, &descRange[2], D3D12_SHADER_VISIBILITY_ALL); CD3DX12_ROOT_SIGNATURE_DESC descRootSignature; descRootSignature.Init(3, rootParameters, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
如果我們要設定sampler描述符,我們需要一個取樣器堆, 通過填寫D3D12_DESCRIPTOR_HEAP_DESC例項並指定堆型別D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER來建立取樣器堆:
D3D12_DESCRIPTOR_HEAP_DESC descHeapSampler = {}; descHeapSampler.NumDescriptors = 1; descHeapSampler.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; descHeapSampler.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; ComPtr<ID3D12DescriptorHeap> mSamplerDescriptorHeap; ThrowIfFailed(mDevice->CreateDescriptorHeap(&descHeapSampler, __uuidof(ID3D12DescriptorHeap), (void**)&mSamplerDescriptorHeap));
一旦我們有一個取樣器堆,我們就可以建立取樣器描述符。 在這裡我們通過填寫D3D12_SAMPLER_DESC物件來指定定址模式和過濾器型別以及其他引數:
typedef struct D3D12_SAMPLER_DESC { D3D12_FILTER Filter; D3D12_TEXTURE_ADDRESS_MODE AddressU; D3D12_TEXTURE_ADDRESS_MODE AddressV; D3D12_TEXTURE_ADDRESS_MODE AddressW; FLOAT MipLODBias; UINT MaxAnisotropy; D3D12_COMPARISON_FUNC ComparisonFunc; FLOAT BorderColor[ 4 ]; FLOAT MinLOD; FLOAT MaxLOD; } D3D12_SAMPLER_DESC;
我們可以在SDK文件中查詢D3D12_FILTER列舉型別,以下示例說明如何為堆中的取樣器建立描述符,該取樣器使用線性過濾,換行定址模式和其他引數的典型預設值:
D3D12_SAMPLER_DESC samplerDesc = {};
samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
samplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
samplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
samplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
samplerDesc.MinLOD = 0;
samplerDesc.MaxLOD = D3D12_FLOAT32_MAX;
samplerDesc.MipLODBias = 0.0f;
samplerDesc.MaxAnisotropy = 1;
samplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS;
md3dDevice->CreateSampler(&samplerDesc,
mSamplerDescriptorHeap->GetCPUDescriptorHandleForHeapStart());
以下程式碼顯示如何將取樣器描述符繫結到根簽名引數槽以供著色器程式使用:
commandList->SetGraphicsRootDescriptorTable(1,
samplerDescriptorHeap->GetGPUDescriptorHandleForHeapStart());
靜態取樣
事實證明,圖形應用程式通常只使用少數取樣器, 因此,Direct3D提供了一個特殊的快捷方式來定義一個取樣器陣列並設定它們,而無需經歷建立取樣器堆的過程。 CD3DX12_ROOT_SIGNATURE_DESC類的Init函式有兩個引數,允許我們定義應用程式可以使用的所謂靜態取樣器陣列。 靜態取樣器由D3D12_STATIC_SAMPLER_DESC結構描述, 此結構與D3D12_SAMPLER_DESC非常相似,但有以下例外:
1、border color 可能有一些限制,具體來說,靜態取樣器的border color必須是以下成員:
enum D3D12_STATIC_BORDER_COLOR
{
D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK = 0,
D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK = (
D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK + 1 ) ,
D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE = (
D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK + 1 )
}D3D12_STATIC_BORDER_COLOR;
2、它包含用於指定著色器暫存器,暫存器空間和著色器可見性的其他欄位,通常將其指定為取樣器堆的一部分。
此外,只能定義2032個靜態取樣器,這對於大多數應用來說已經足夠了。
但是,如果確實需要更多,則可以使用非靜態取樣器並通過取樣器堆。
我們在演示中使用靜態取樣器, 以下程式碼顯示了我們如何定義靜態取樣器。 請注意,我們在演示中不需要所有這些靜態取樣器,但我們仍然定義它們,以便在需要它們時它們就在那裡。 無論如何,它只是少數,定義一些可能使用或不使用的額外取樣器並沒有什麼壞處。
std::array<const CD3DX12_STATIC_SAMPLER_DESC, 6> TexColumnsApp::GetStaticSamplers()
{
// Applications usually only need a handful of samplers. So just define them
// all up front and keep them available as part of the root signature.
const CD3DX12_STATIC_SAMPLER_DESC pointWrap(
0, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_POINT, // filter
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_WRAP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC pointClamp(
1, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_POINT, // filter
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_CLAMP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC linearWrap(
2, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_LINEAR, // filter
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_WRAP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC linearClamp(
3, // shaderRegister
D3D12_FILTER_MIN_MAG_MIP_LINEAR, // filter
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_CLAMP); // addressW
const CD3DX12_STATIC_SAMPLER_DESC anisotropicWrap(
4, // shaderRegister
D3D12_FILTER_ANISOTROPIC, // filter
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_WRAP, // addressW
0.0f, // mipLODBias
8); // maxAnisotropy
const CD3DX12_STATIC_SAMPLER_DESC anisotropicClamp(
5, // shaderRegister
D3D12_FILTER_ANISOTROPIC, // filter
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressU
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressV
D3D12_TEXTURE_ADDRESS_MODE_CLAMP, // addressW
0.0f, // mipLODBias
8); // maxAnisotropy
return {
pointWrap, pointClamp,
linearWrap, linearClamp,
anisotropicWrap, anisotropicClamp };
}
void TexColumnsApp::BuildRootSignature()
{
CD3DX12_DESCRIPTOR_RANGE texTable;
texTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0);
// Root parameter can be a table, root descriptor or root constants.
CD3DX12_ROOT_PARAMETER slotRootParameter[4];
slotRootParameter[0].InitAsDescriptorTable(1,
&texTable, D3D12_SHADER_VISIBILITY_PIXEL);
slotRootParameter[1].InitAsConstantBufferView(0);
slotRootParameter[2].InitAsConstantBufferView(1);
slotRootParameter[3].InitAsConstantBufferView(2);
auto staticSamplers = GetStaticSamplers();
// A root signature is an array of root parameters.
CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(4, slotRootParameter,
(UINT)staticSamplers.size(), staticSamplers.data(),
D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
// create a root signature with a single slot which points to a
// descriptor range consisting of a single constant buffer
ComPtr<ID3DBlob> serializedRootSig = nullptr;
ComPtr<ID3DBlob> errorBlob = nullptr;
HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1,
serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());
if(errorBlob != nullptr)
{
::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
}
ThrowIfFailed(hr);
ThrowIfFailed(md3dDevice->CreateRootSignature(
0,
serializedRootSig->GetBufferPointer(),
serializedRootSig->GetBufferSize(),
IID_PPV_ARGS(mRootSignature.GetAddressOf())));
}