Criando recursos de textura (Direct3D 10)

Um recurso de textura é uma coleção estruturada de dados. Normalmente, os valores de cor são armazenados em texturas e acessados durante a renderização pelo pipeline em vários estágios para entrada e saída. Criar texturas e definir como elas serão usadas é uma parte importante da renderização de cenas interessantes no Direct3D 10.

Embora as texturas normalmente contenham informações de cor, criar texturas usando diferentes DXGI_FORMAT permite que elas armazenem diferentes tipos de dados. Esses dados podem ser aproveitados pelo pipeline do Direct3D 10 de maneiras não tradicionais.

Todas as texturas têm limites sobre a quantidade de memória que consomem e quantos texels contêm. Esses limites são especificados por constantes de recursos.

Criar uma textura de um arquivo

Observação

A biblioteca de utilitários D3DX foi preterida para Windows 8 e não tem suporte para aplicativos da Windows Store.

 

Ao criar uma textura no Direct3D 10, você também precisa criar uma exibição. Uma exibição é um objeto que informa ao dispositivo como uma textura deve ser acessada durante a renderização. A maneira mais comum de acessar uma textura é lê-la usando um sombreador. Uma exibição sombreador-recurso informará a um sombreador como ler de uma textura durante a renderização. O tipo de exibição que uma textura usará deve ser especificado ao criá-la.

Criar uma textura e carregar seus dados iniciais pode ser feito de duas maneiras diferentes: criar uma textura e exibição separadamente ou criar a textura e a exibição ao mesmo tempo. A API fornece ambas as técnicas para que você possa escolher qual melhor atende às suas necessidades.

Criar textura e exibição separadamente

A maneira mais fácil de criar uma textura é carregá-la de um arquivo de imagem. Para criar a textura, basta preencher uma estrutura e fornecer o nome da textura para D3DX10CreateTextureFromFile.

ID3D10Device *pDevice = NULL;
// Initialize D3D10 device...

D3DX10_IMAGE_LOAD_INFO loadInfo;
ZeroMemory( &loadInfo, sizeof(D3DX10_IMAGE_LOAD_INFO) );
loadInfo.BindFlags = D3D10_BIND_SHADER_RESOURCE;

ID3D10Resource *pTexture = NULL;
D3DX10CreateTextureFromFile( pDevice, L"sample.bmp", &loadInfo, NULL, &pTexture, NULL );

A função D3DX D3DX10CreateTextureFromFile faz três coisas: primeiro, ela cria um objeto de textura Direct3D 10; segundo, ele lê o arquivo de imagem de entrada; terceiro, ele armazena os dados da imagem no objeto de textura. No exemplo acima, um arquivo BMP é carregado, mas a função pode carregar uma variedade de tipos de arquivo.

O sinalizador de associação indica que a textura será criada como um recurso de sombreador que permite que um estágio de sombreador leia a partir da textura durante a renderização.

O exemplo acima não especifica todos os parâmetros de carregamento. Na verdade, geralmente é benéfico simplesmente zerar os parâmetros de carregamento, pois isso permite que o D3DX escolha valores apropriados com base na imagem de entrada. Se você quiser que a imagem de entrada determine todos os parâmetros com os quais a textura é criada, basta especificar NULL para o parâmetro loadInfo como este:

D3DX10CreateTextureFromFile( pDevice, L"sample.bmp", NULL, NULL, &pTexture, NULL );

Especificar NULL para as informações de carregamento é um atalho simples, mas poderoso.

Agora que uma textura foi criada, você precisa criar uma exibição sombreador-recurso para que a textura possa ser associada como uma entrada a um sombreador. Como D3DX10CreateTextureFromFile retorna um ponteiro para um recurso e não um ponteiro para uma textura, você precisa determinar o tipo exato de recurso que foi carregado e, em seguida, pode criar uma exibição de recurso de sombreador usando CreateShaderResourceView.

D3D10_SHADER_RESOURCE_VIEW_DESC srvDesc;
D3D10_RESOURCE_DIMENSION type;
pTexture->GetType( &type );
switch( type )
{
    case D3D10_RESOURCE_DIMENSION_BUFFER:
    //...
    break;
    case D3D10_RESOURCE_DIMENSION_TEXTURE1D:
    //...
    break;
    case D3D10_RESOURCE_DIMENSION_TEXTURE2D:
    {
        D3D10_TEXTURE2D_DESC desc;
        ID3D10Texture2D *pTexture2D = (ID3D10Texture2D*)pTexture;
        pTexture2D->GetDesc( &desc );
        
        srvDesc.Format = desc.Format;
        srvDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
        srvDesc.Texture2D.MipLevels = desc.MipLevels;
        srvDesc.Texture2D.MostDetailedMip = desc.MipLevels -1;

    }
    break;
    case D3D10_RESOURCE_DIMENSION_TEXTURE3D:
    //...
    break;
    default:
    //...
    break;
}

ID3D10ShaderResourceView *pSRView = NULL;
pDevice->CreateShaderResourceView( pTexture, &srvDesc, &pSRView );

Embora o exemplo acima crie uma exibição de recurso de sombreador 2D, o código para criar outros tipos de exibição de recurso de sombreador é muito semelhante. Qualquer estágio de sombreador agora pode usar essa textura como entrada.

Usar D3DX10CreateTextureFromFile e CreateShaderResourceView para criar uma textura e sua exibição associada é uma maneira de preparar uma textura a ser associada a um estágio de sombreador. Outra maneira de fazer isso é criar a textura e sua exibição ao mesmo tempo, o que é discutido na próxima seção.

Criar textura e exibição simultaneamente

O Direct3D 10 requer uma textura e uma exibição de recurso de sombreador para ler de uma textura durante o runtime. Como a criação de uma textura e uma exibição de sombreador-recurso é uma tarefa tão comum, o D3DX fornece o D3DX10CreateShaderResourceViewFromFile para fazer isso por você.

D3DX10_IMAGE_LOAD_INFO loadInfo;
ZeroMemory( &loadInfo, sizeof(D3DX10_IMAGE_LOAD_INFO) );
loadInfo.BindFlags = D3D10_BIND_SHADER_RESOURCE;
loadInfo.Format = DXGI_FORMAT_BC1_UNORM;

ID3D10ShaderResourceView *pSRView = NULL;
D3DX10CreateShaderResourceViewFromFile( pDevice, L"sample.bmp", &loadInfo, NULL, &pSRView, NULL );

Com uma única chamada D3DX, a textura e a exibição de recurso de sombreador são criadas. A funcionalidade do parâmetro loadInfo é inalterada; você pode usá-la para personalizar como a textura é criada ou derivar os parâmetros necessários do arquivo de entrada especificando NULL para o parâmetro loadInfo .

O objeto ID3D10ShaderResourceView retornado pela função D3DX10CreateShaderResourceViewFromFile poderá ser usado posteriormente para recuperar a interface ID3D10Resource original, se necessário. Isso pode ser feito chamando o método GetResource .

O D3DX fornece uma única função para criar uma exibição de textura e sombreador-recurso como uma convieniência; cabe a você decidir qual método de criação de textura e exibição melhor atende às necessidades do seu aplicativo.

Agora que você sabe como criar uma textura e sua exibição de recurso de sombreador, a próxima seção mostrará como você pode amostrar (leitura) dessa textura usando um sombreador.

Criando texturas vazias

Às vezes, os aplicativos desejam criar uma textura e calcular os dados a serem armazenados na textura ou usar o pipeline de gráficos para renderizar essa textura e, posteriormente, usar os resultados em outro processamento. Essas texturas podem ser atualizadas pelo pipeline de gráficos ou pelo próprio aplicativo, dependendo de que tipo de uso foi especificado para a textura quando ela foi criada.

Renderizando para uma textura

O caso mais comum de criar uma textura vazia a ser preenchida com dados durante o runtime é o caso em que um aplicativo deseja renderizar em uma textura e, em seguida, usar os resultados da operação de renderização em uma passagem subsequente. Texturas criadas com essa finalidade devem especificar o uso padrão.

O exemplo de código a seguir cria uma textura vazia para a qual o pipeline pode renderizar e, posteriormente, usar como entrada para um sombreador.

// Create the render target texture
D3D10_TEXTURE2D_DESC desc;
ZeroMemory( &desc, sizeof(desc) );
desc.Width = 256;
desc.Height = 256;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;

ID3D10Texture2D *pRenderTarget = NULL;
pDevice->CreateTexture2D( &desc, NULL, &pRenderTarget );

Criar a textura requer que o aplicativo especifique algumas informações sobre as propriedades que a textura terá. A largura e a altura da textura em texels são definidas como 256. Para esse destino de renderização, um único nível de mipmap é tudo o que precisamos. Somente um destino de renderização é necessário para que o tamanho da matriz seja definido como 1. Cada texel contém quatro valores de ponto flutuante de 32 bits, que podem ser usados para armazenar informações muito precisas (consulte DXGI_FORMAT). Um exemplo por pixel é tudo o que será necessário. O uso é definido como padrão porque isso permite o posicionamento mais eficiente do destino de renderização na memória. Por fim, o fato de que a textura será associada como um destino de renderização e um recurso de sombreador em diferentes pontos no tempo é especificado.

As texturas não podem ser associadas à renderização para o pipeline diretamente; use uma exibição de destino de renderização, conforme mostrado no exemplo de código a seguir.

D3D10_RENDER_TARGET_VIEW_DESC rtDesc;
rtDesc.Format = desc.Format;
rtDesc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2D;
rtDesc.Texture2D.MipSlice = 0;

ID3D10RenderTargetView *pRenderTargetView = NULL;
pDevice->CreateRenderTargetView( pRenderTarget, &rtDesc, &pRenderTargetView );

O formato da exibição de destino de renderização é simplesmente definido como o formato da textura original. As informações no recurso devem ser interpretadas como uma textura 2D e queremos usar apenas o primeiro nível mipmap do destino de renderização.

Da mesma forma que uma exibição de destino de renderização deve ser criada para que o destino de renderização possa ser associado à saída para o pipeline, uma exibição de sombreador-recurso deve ser criada para que o destino de renderização possa ser associado ao pipeline como uma entrada. O exemplo de código a seguir demonstra isso.

// Create the shader-resource view
D3D10_SHADER_RESOURCE_VIEW_DESC srDesc;
srDesc.Format = desc.Format;
srDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
srDesc.Texture2D.MostDetailedMip = 0;
srDesc.Texture2D.MipLevels = 1;

ID3D10ShaderResourceView *pShaderResView = NULL;
pDevice->CreateShaderResourceView( pRenderTarget, &srDesc, &pShaderResView );

Os parâmetros das descrições de exibição de sombreador-recurso são muito semelhantes aos das descrições de exibição de destino de renderização e foram escolhidos pelos mesmos motivos.

Preenchendo texturas manualmente

Às vezes, os aplicativos gostariam de calcular valores em runtime, colocá-los em uma textura manualmente e, em seguida, fazer com que o pipeline de gráficos use essa textura em operações de renderização posteriores. Para fazer isso, o aplicativo deve criar uma textura vazia de forma a permitir que a CPU acesse a memória subjacente. Isso é feito criando uma textura dinâmica e obtendo acesso à memória subjacente chamando um método específico. O exemplo de código a seguir demonstra como fazer isso.

D3D10_TEXTURE2D_DESC desc;
desc.Width = 256;
desc.Height = 256;
desc.MipLevels = desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DYNAMIC;
desc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
ID3D10Texture2D *pTexture = NULL;
pd3dDevice->CreateTexture2D( &desc, NULL, &pTexture );

Observe que o formato é definido como 32 bits por pixel em que cada componente é definido por 8 bits. O parâmetro de uso é definido como dinâmico enquanto os sinalizadores de associação são definidos para especificar que a textura será acessada por um sombreador. O restante da descrição da textura é semelhante à criação de um destino de renderização.

Chamar Mapa permite que o aplicativo acesse a memória subjacente da textura. O ponteiro recuperado é então usado para preencher a textura com dados. Isso pode ser visto no exemplo de código a seguir.

D3D10_MAPPED_TEXTURE2D mappedTex;
pTexture->Map( D3D10CalcSubresource(0, 0, 1), D3D10_MAP_WRITE_DISCARD, 0, &mappedTex );

UCHAR* pTexels = (UCHAR*)mappedTex.pData;
for( UINT row = 0; row < desc.Height; row++ )
{
    UINT rowStart = row * mappedTex.RowPitch;
    for( UINT col = 0; col < desc.Width; col++ )
    {
        UINT colStart = col * 4;
        pTexels[rowStart + colStart + 0] = 255; // Red
        pTexels[rowStart + colStart + 1] = 128; // Green
        pTexels[rowStart + colStart + 2] = 64;  // Blue
        pTexels[rowStart + colStart + 3] = 32;  // Alpha
    }
}

pTexture->Unmap( D3D10CalcSubresource(0, 0, 1) );

Vários Rendertargets

Até oito exibições de destino de renderização podem ser associadas ao pipeline por vez (com OMSetRenderTargets). Para cada pixel (ou cada exemplo, se o multisampling estiver habilitado), a mesclagem é feita independentemente para cada exibição de destino de renderização. Duas das variáveis de estado de combinação - BlendEnable e RenderTargetWriteMask - são matrizes de oito, cada membro da matriz corresponde a uma exibição de destino de renderização. Ao usar vários destinos de renderização, cada destino de renderização deve ser o mesmo tipo de recurso (buffer, textura 1D, matriz de textura 2D etc.) e deve ter a mesma dimensão (largura, altura, profundidade para texturas 3D e tamanho da matriz para matrizes de textura). Se os destinos de renderização forem de várias amostras, todos eles deverão ter o mesmo número de amostras por pixel.

Só pode haver um buffer de estêncil de profundidade ativo, independentemente de quantos destinos de renderização estejam ativos. Ao usar matrizes de textura como destinos de renderização, todas as dimensões de exibição devem corresponder. Os destinos de renderização não precisam ter o mesmo formato de textura.

Recursos (Direct3D 10)