Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O exemplo de D3D12DynamicIndexing demonstra alguns dos novos recursos HLSL disponíveis no Shader Model 5.1 - particularmente indexação dinâmica e matrizes não limitadas - para renderizar a mesma malha várias vezes, cada vez renderizando-a com um material selecionado dinamicamente. Com a indexação dinâmica, os sombreadores agora podem indexar em uma matriz sem saber o valor do índice em tempo de compilação. Quando combinadas com arrays sem limites, isto adiciona outro nível de indireção e flexibilidade para autores de shaders e fluxos de trabalho de arte.
- Configurar o sombreador de pixel
- Configurar a assinatura raiz
- Crie as texturas
- Carregue os dados de textura
- Carregue a textura difusa
- Criar um amostrador
- Alterar dinamicamente o índice de parâmetros raiz
- Execute o exemplo
- Tópicos relacionados
Configurar o sombreador de pixel
Vamos primeiro olhar para o sombreador em si, que para este exemplo é um sombreador de pixel.
Texture2D g_txDiffuse : register(t0);
Texture2D g_txMats[] : register(t1);
SamplerState g_sampler : register(s0);
struct PSSceneIn
{
float4 pos : SV_Position;
float2 tex : TEXCOORD0;
};
struct MaterialConstants
{
uint matIndex; // Dynamically set index for looking up from g_txMats[].
};
ConstantBuffer<MaterialConstants> materialConstants : register(b0, space0);
float4 PSSceneMain(PSSceneIn input) : SV_Target
{
float3 diffuse = g_txDiffuse.Sample(g_sampler, input.tex).rgb;
float3 mat = g_txMats[materialConstants.matIndex].Sample(g_sampler, input.tex).rgb;
return float4(diffuse * mat, 1.0f);
}
O recurso de matriz não limitada é ilustrado pela matriz g_txMats[], pois não especifica um tamanho de matriz. A indexação dinâmica é usada para indexar em g_txMats[] com matIndex, que é definida como uma constante-base. O sombreador não tem conhecimento do tamanho, da matriz ou do valor do índice em tempo de compilação. Ambos os atributos são definidos na assinatura raiz do objeto de estado do pipeline usado com o sombreador.
Para aproveitar os recursos de indexação dinâmica no HLSL, é necessário que o sombreador seja compilado com o SM 5.1. Além disso, para fazer uso de matrizes não limitadas, o sinalizador /enable_unbounded_descriptor_tables também deve ser usado. As seguintes opções de linha de comando são usadas para compilar esse sombreador com o Effect-Compiler Tool (FXC):
fxc /Zi /E"PSSceneMain" /Od /Fo"dynamic_indexing_pixel.cso" /ps"_5_1" /nologo /enable_unbounded_descriptor_tables
Configurar a assinatura raiz
Agora, vamos examinar a definição de assinatura raiz, particularmente, como definimos o tamanho da matriz ilimitada e vinculamos uma constante de raiz a matIndex. Para o sombreador de pixel, definimos três coisas: uma tabela de descritor para SRVs (nosso Texture2Ds), uma tabela de descritor para Samplers e uma única constante de raiz. A tabela de descritores para os nossos SRVs contém CityMaterialCount + 1 entradas.
CityMaterialCount é uma constante que define o comprimento de g_txMats[] e o + 1 é para g_txDiffuse. A tabela de descritores para nossos Samplers contém apenas uma entrada e definimos apenas um valor constante de raiz de 32 bits por meio InitAsConstants(...)., no método LoadAssets.
// Create the root signature.
{
CD3DX12_DESCRIPTOR_RANGE ranges[3];
ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1 + CityMaterialCount, 0); // Diffuse texture + array of materials.
ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 1, 0);
ranges[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0);
CD3DX12_ROOT_PARAMETER rootParameters[4];
rootParameters[0].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL);
rootParameters[1].InitAsDescriptorTable(1, &ranges[1], D3D12_SHADER_VISIBILITY_PIXEL);
rootParameters[2].InitAsDescriptorTable(1, &ranges[2], D3D12_SHADER_VISIBILITY_VERTEX);
rootParameters[3].InitAsConstants(1, 0, 0, D3D12_SHADER_VISIBILITY_PIXEL);
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(_countof(rootParameters), rootParameters, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
ComPtr<ID3DBlob> signature;
ComPtr<ID3DBlob> error;
ThrowIfFailed(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)));
}
| Fluxo de chamadas | Parâmetros |
|---|---|
| CD3DX12_DESCRIPTOR_RANGE | D3D12_DESCRIPTOR_RANGE_TYPE |
| CD3DX12_ROOT_PARAMETER | D3D12_SHADER_VISIBILITY |
| CD3DX12_ROOT_SIGNATURE_DESC | D3D12_ROOT_SIGNATURE_FLAGS |
| ID3DBlob | |
| D3D12SerializeRootSignature | D3D_ROOT_SIGNATURE_VERSION |
| CreateRootSignature |
Crie as texturas
O conteúdo do g_txMats[] são texturas geradas proceduralmente criadas em LoadAssets. Cada cidade renderizada na cena compartilha a mesma textura difusa, mas cada uma também tem sua própria textura gerada proceduralmente. A matriz de texturas abrange o espectro do arco-íris para visualizar facilmente a técnica de indexação.
// Create the textures and sampler.
{
// Procedurally generate an array of textures to use as city materials.
{
// All of these materials use the same texture desc.
D3D12_RESOURCE_DESC textureDesc = {};
textureDesc.MipLevels = 1;
textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
textureDesc.Width = CityMaterialTextureWidth;
textureDesc.Height = CityMaterialTextureHeight;
textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
textureDesc.DepthOrArraySize = 1;
textureDesc.SampleDesc.Count = 1;
textureDesc.SampleDesc.Quality = 0;
textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
// The textures evenly span the color rainbow so that each city gets
// a different material.
float materialGradStep = (1.0f / static_cast<float>(CityMaterialCount));
// Generate texture data.
vector<vector<unsigned char>> cityTextureData;
cityTextureData.resize(CityMaterialCount);
for (int i = 0; i < CityMaterialCount; ++i)
{
CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_DEFAULT);
ThrowIfFailed(m_device->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&textureDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_cityMaterialTextures[i])));
// Fill the texture.
float t = i * materialGradStep;
cityTextureData[i].resize(CityMaterialTextureWidth * CityMaterialTextureHeight * CityMaterialTextureChannelCount);
for (int x = 0; x < CityMaterialTextureWidth; ++x)
{
for (int y = 0; y < CityMaterialTextureHeight; ++y)
{
// Compute the appropriate index into the buffer based on the x/y coordinates.
int pixelIndex = (y * CityMaterialTextureChannelCount * CityMaterialTextureWidth) + (x * CityMaterialTextureChannelCount);
// Determine this row's position along the rainbow gradient.
float tPrime = t + ((static_cast<float>(y) / static_cast<float>(CityMaterialTextureHeight)) * materialGradStep);
// Compute the RGB value for this position along the rainbow
// and pack the pixel value.
XMVECTOR hsl = XMVectorSet(tPrime, 0.5f, 0.5f, 1.0f);
XMVECTOR rgb = XMColorHSLToRGB(hsl);
cityTextureData[i][pixelIndex + 0] = static_cast<unsigned char>((255 * XMVectorGetX(rgb)));
cityTextureData[i][pixelIndex + 1] = static_cast<unsigned char>((255 * XMVectorGetY(rgb)));
cityTextureData[i][pixelIndex + 2] = static_cast<unsigned char>((255 * XMVectorGetZ(rgb)));
cityTextureData[i][pixelIndex + 3] = 255;
}
}
}
}
| Fluxo de chamadas | Parâmetros |
|---|---|
| D3D12_RESOURCE_DESC |
D3D12_RESOURCE_FLAGS [D3D12_RESOURCE_DIMENSION](/windows/desktop/api/d3d12/ne-d3d12-d3d12_resource_dimension) |
| CriarRecursoComprometido |
D3D12_HEAP_TYPE [D3D12_HEAP_FLAG](/windows/desktop/api/d3d12/ne-d3d12-d3d12_heap_flags) CD3DX12_RESOURCE_DESC [D3D12_RESOURCE_STATES](/windows/desktop/api/d3d12/ne-d3d12-d3d12_resource_states) |
| XMVECTOR |
[XMColorHSLToRGB](/windows/desktop/api/directxmath/nf-directxmath-xmcolorhsltorgb) |
Carregue os dados de textura
Os dados de textura são carregados para a GPU através de uma pilha de carregamento, e os SRVs são criados para cada um e armazenados numa pilha de descritores SRV.
// Upload texture data to the default heap resources.
{
const UINT subresourceCount = textureDesc.DepthOrArraySize * textureDesc.MipLevels;
const UINT64 uploadBufferStep = GetRequiredIntermediateSize(m_cityMaterialTextures[0].Get(), 0, subresourceCount); // All of our textures are the same size in this case.
const UINT64 uploadBufferSize = uploadBufferStep * CityMaterialCount;
CD3DX12_HEAP_PROPERTIES uploadHeap(D3D12_HEAP_TYPE_UPLOAD);
auto uploadDesc = CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize);
ThrowIfFailed(m_device->CreateCommittedResource(
&uploadHeap,
D3D12_HEAP_FLAG_NONE,
&uploadDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&materialsUploadHeap)));
for (int i = 0; i < CityMaterialCount; ++i)
{
// Copy data to the intermediate upload heap and then schedule
// a copy from the upload heap to the appropriate texture.
D3D12_SUBRESOURCE_DATA textureData = {};
textureData.pData = &cityTextureData[i][0];
textureData.RowPitch = static_cast<LONG_PTR>((CityMaterialTextureChannelCount * textureDesc.Width));
textureData.SlicePitch = textureData.RowPitch * textureDesc.Height;
UpdateSubresources(m_commandList.Get(), m_cityMaterialTextures[i].Get(), materialsUploadHeap.Get(), i * uploadBufferStep, 0, subresourceCount, &textureData);
auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_cityMaterialTextures[i].Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
m_commandList->ResourceBarrier(1, &barrier);
}
}
| Fluxo de chamadas | Parâmetros |
|---|---|
| GetRequiredIntermediateSize | |
| CriarRecursoComprometido | |
| D3D12_SUBRESOURCE_DATA | |
| UpdateSubresources | |
| BarreiraDeRecursos |
Carregue a textura difusa
A textura difusa, g_txDiffuse, é carregada de maneira semelhante e também recebe seu próprio SRV, mas os dados de textura já estão definidos em occcity.bin.
// Load the occcity diffuse texture with baked-in ambient lighting.
// This texture will be blended with a texture from the materials
// array in the pixel shader.
{
D3D12_RESOURCE_DESC textureDesc = {};
textureDesc.MipLevels = SampleAssets::Textures[0].MipLevels;
textureDesc.Format = SampleAssets::Textures[0].Format;
textureDesc.Width = SampleAssets::Textures[0].Width;
textureDesc.Height = SampleAssets::Textures[0].Height;
textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
textureDesc.DepthOrArraySize = 1;
textureDesc.SampleDesc.Count = 1;
textureDesc.SampleDesc.Quality = 0;
textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_DEFAULT);
ThrowIfFailed(m_device->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&textureDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&m_cityDiffuseTexture)));
const UINT subresourceCount = textureDesc.DepthOrArraySize * textureDesc.MipLevels;
const UINT64 uploadBufferSize = GetRequiredIntermediateSize(m_cityDiffuseTexture.Get(), 0, subresourceCount);
CD3DX12_HEAP_PROPERTIES uploadHeap(D3D12_HEAP_TYPE_UPLOAD);
auto uploadDesc = CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize);
ThrowIfFailed(m_device->CreateCommittedResource(
&uploadHeap,
D3D12_HEAP_FLAG_NONE,
&uploadDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&textureUploadHeap)));
// Copy data to the intermediate upload heap and then schedule
// a copy from the upload heap to the diffuse texture.
D3D12_SUBRESOURCE_DATA textureData = {};
textureData.pData = pMeshData + SampleAssets::Textures[0].Data[0].Offset;
textureData.RowPitch = SampleAssets::Textures[0].Data[0].Pitch;
textureData.SlicePitch = SampleAssets::Textures[0].Data[0].Size;
UpdateSubresources(m_commandList.Get(), m_cityDiffuseTexture.Get(), textureUploadHeap.Get(), 0, 0, subresourceCount, &textureData);
auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(m_cityDiffuseTexture.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
m_commandList->ResourceBarrier(1, &barrier);
}
| Fluxo de chamadas | Parâmetros |
|---|---|
| D3D12_RESOURCE_DESC | |
| CriarRecursoComprometido | |
| ObterTamanhoIntermediárioRequerido | |
| CriarRecursoComprometido | |
| D3D12_SUBRESOURCE_DATA | |
| ResourceBarrier |
Criar um amostrador
Finalmente, para LoadAssets, um único amostrador é criado para amostrar a textura difusa ou a matriz de textura.
// Describe and create a sampler.
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;
m_device->CreateSampler(&samplerDesc, m_samplerHeap->GetCPUDescriptorHandleForHeapStart());
// Create SRV for the city's diffuse texture.
CD3DX12_CPU_DESCRIPTOR_HANDLE srvHandle(m_cbvSrvHeap->GetCPUDescriptorHandleForHeapStart(), 0, m_cbvSrvDescriptorSize);
D3D12_SHADER_RESOURCE_VIEW_DESC diffuseSrvDesc = {};
diffuseSrvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
diffuseSrvDesc.Format = SampleAssets::Textures->Format;
diffuseSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
diffuseSrvDesc.Texture2D.MipLevels = 1;
m_device->CreateShaderResourceView(m_cityDiffuseTexture.Get(), &diffuseSrvDesc, srvHandle);
srvHandle.Offset(m_cbvSrvDescriptorSize);
// Create SRVs for each city material.
for (int i = 0; i < CityMaterialCount; ++i)
{
D3D12_SHADER_RESOURCE_VIEW_DESC materialSrvDesc = {};
materialSrvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
materialSrvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
materialSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
materialSrvDesc.Texture2D.MipLevels = 1;
m_device->CreateShaderResourceView(m_cityMaterialTextures[i].Get(), &materialSrvDesc, srvHandle);
srvHandle.Offset(m_cbvSrvDescriptorSize);
}
| Fluxo de chamadas | Parâmetros |
|---|---|
| D3D12_SAMPLER_DESC |
D3D12_FLOAT32_MAX (Constantes) D3D12_COMPARISON_FUNC |
| CreateSampler | |
| CD3DX12_CPU_DESCRIPTOR_HANDLE | GetCPUDescriptorHandleForHeapStart |
| D3D12_SHADER_RESOURCE_VIEW_DESC | |
| CreateShaderResourceView | |
| D3D12_SHADER_RESOURCE_VIEW_DESC | |
| CreateShaderResourceView |
Alterar dinamicamente o índice de parâmetros raiz
Se fôssemos renderizar a cena agora, todas as cidades pareceriam iguais, porque não definimos o valor de nossa raiz constante, matIndex. Cada sombreador de pixel seria indexado no 0º slot de g_txMats e a cena ficaria assim:
O valor da constante raiz é definido em FrameResource::PopulateCommandLists. No duplo para loop onde um comando draw é gravado para cada cidade, gravamos uma chamada para SetGraphicsRoot32BitConstants especificando nosso índice de parâmetros raiz em relação à assinatura raiz – neste caso 3 – o valor do índice dinâmico e um offset – neste caso 0. Uma vez que o comprimento do g_txMats é igual ao número de cidades que renderizamos, o valor do índice é incrementado para cada cidade.
for (UINT i = 0; i < m_cityRowCount; i++)
{
for (UINT j = 0; j < m_cityColumnCount; j++)
{
pCommandList->SetPipelineState(pPso);
// Set the city's root constant for dynamically indexing into the material array.
pCommandList->SetGraphicsRoot32BitConstant(3, (i * m_cityColumnCount) + j, 0);
// Set this city's CBV table and move to the next descriptor.
pCommandList->SetGraphicsRootDescriptorTable(2, cbvSrvHandle);
cbvSrvHandle.Offset(cbvSrvDescriptorSize);
pCommandList->DrawIndexedInstanced(numIndices, 1, 0, 0, 0);
}
}
| Fluxo de chamadas | Parâmetros |
|---|---|
| SetPipelineState | |
| SetGraphicsRoot32BitConstant | |
| SetGraphicsRootDescriptorTable | |
| DesenharIndexadoInstanciado |
Executar o exemplo
Agora, quando renderizamos a cena, cada cidade terá um valor diferente para matIndex e, portanto, procurará uma textura diferente de g_txMats[], resultando em a cena ficar assim: