Sim, um mecanismo de jogo em geral terá uma variedade de shaders diferentes. O padrão típico é:
Ao inicializar o mecanismo e carregar o mundo do jogo, prepare todos os shaders que você usará para renderizar. Por "preparação", quero dizer carregá-los na memória, compilá-los, se necessário, e fazer todas as ID3D11Device::CreatePixelShader
chamadas semelhantes para obter os objetos de sombreador D3D alocados e prontos para uso. Mantenha os objetos em uma matriz ou em alguma outra estrutura de dados.
Normalmente, você terá um relacionamento individual entre os shaders de vértice e os shaders de pixel projetados para trabalhar juntos. Penso neles como um único objeto, que chamo de "sombreador", mesmo que ele realmente contenha um sombreador de vértice e um sombreador de pixel (e talvez sombreadores de geometria / casco / domínio também).
Cada quadro, depois de encontrar a lista de objetos (malhas) a serem renderizados, classifique-os por sombreador. A idéia é minimizar o número de vezes que você alterna shaders em um quadro, desenhando todos os objetos com um determinado shader juntos. Isso ocorre porque a mudança shaders é uma operação um pouco caro (embora certamente você pode fazê-lo várias centenas ou milhares de vezes por quadro, por isso não é realmente que caro).
De fato, você pode dar um passo adiante e classificar as malhas por shader primeiro e material por segundo. Por "material", quero dizer uma combinação de um sombreador e um conjunto de texturas e parâmetros para ele. Os shaders geralmente têm algumas texturas e parâmetros numéricos (armazenados em buffers constantes) que os alimentam; portanto, por exemplo, um material de tijolo e asfalto pode usar o mesmo código de shader, apenas com texturas diferentes.
Para desenhar, faça um loop sobre os shaders, defina cada shader no ID3D11DeviceContext
, defina quaisquer parâmetros (buffers constantes, texturas etc.) e desenhe os objetos. No pseudocódigo, incluindo a distinção shaders / materiais, mencionei:
for each shader:
// Set the device context to use this shader
pContext->VSSetShader(shader.pVertexShader);
pContext->PSSetShader(shader.pPixelShader);
for each material that uses this shader:
// Set the device context to use any constant buffers, textures, samplers,
// etc. needed for this material
pContext->VSSetConstantBuffers(...);
pContext->PSSetConstantBuffers(...);
pContext->PSSetShaderResources(...);
pContext->PSSetSamplers(...);
for each mesh that uses this material:
// Set any constant buffers containing parameters specific to the mesh
// (e.g. world matrix)
pContext->VSSetConstantBuffers(...);
// Set the context to use the vertex & index buffers for this mesh
pContext->IASetInputLayout(mesh.pInputLayout);
pContext->IASetVertexBuffers(...);
pContext->IASetIndexBuffer(...);
pContext->IASetPrimitiveTopology(...)
// Draw it
pContext->DrawIndexed(...)
Muito mais pode ser dito sobre o gerenciamento de objetos, malhas, shaders, layouts de entrada, buffers constantes, etc., mas isso deve ser suficiente para você começar. :)