Sí, un motor de juego en general tendrá una variedad de sombreadores diferentes. El patrón típico es:
Mientras inicializa el motor y carga el mundo del juego, prepare todos los sombreadores que usará para renderizar. Por "preparar" me refiero a cargarlos en la memoria, compilarlos si es necesario y hacer todas las ID3D11Device::CreatePixelShader
llamadas similares para obtener los objetos de sombreador D3D asignados y listos para funcionar. Mantenga los objetos en una matriz o alguna otra estructura de datos.
Por lo general, tendrá una relación uno a uno entre los sombreadores de vértices y los sombreadores de píxeles diseñados para trabajar juntos. Pienso en ellos como un solo objeto, al que simplemente llamo un "sombreador", a pesar de que realmente contiene un sombreador de vértices y un sombreador de píxeles (y tal vez sombreadores de geometría / casco / dominio también).
Cada cuadro, una vez que haya encontrado la lista de objetos (mallas) para renderizar, ordénelos por sombreador. La idea es minimizar la cantidad de veces que cambia los sombreadores en un marco, dibujando todos los objetos con un sombreador dado. Esto se debe a que el cambio de sombreadores es una operación algo costosa (aunque ciertamente puede hacerlo varios cientos o mil veces por cuadro, por lo que no es realmente tan costoso).
De hecho, puede ir un paso más allá y ordenar las mallas por sombreador primero y material segundo. Por "material", me refiero a una combinación de un sombreador y un conjunto de texturas y parámetros para él. Los sombreadores generalmente tienen algunas texturas y parámetros numéricos (almacenados en búferes constantes) que los alimentan, por lo que, por ejemplo, un material de ladrillo y material de asfalto podría usar el mismo código de sombreador, solo que con diferentes texturas.
Para dibujar, simplemente recorra los sombreadores, configure cada sombreador en el ID3D11DeviceContext
, configure cualquier parámetro (búferes constantes, texturas, etc.), luego dibuje los objetos. En pseudocódigo, incluida la distinción de sombreadores / materiales que mencioné:
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(...)
Se puede decir mucho más sobre la administración de objetos, mallas, sombreadores, diseños de entrada, buffers constantes, etc., pero esto debería ser suficiente para comenzar. :)