Да, игровой движок, как правило, будет иметь различные шейдеры. Типичная картина:
При инициализации движка и загрузке игрового мира подготовьте все шейдеры, которые вы будете использовать для рендеринга. Под «подготовкой» я подразумеваю загружать их в память, компилировать их при необходимости и выполнять все ID3D11Device::CreatePixelShader
и подобные вызовы, чтобы выделить объекты шейдера D3D и быть готовыми к работе. Храните объекты в массиве или какой-либо другой структуре данных.
Обычно у вас будут взаимно-однозначные отношения между вершинными и пиксельными шейдерами, которые предназначены для совместной работы. Я думаю о них как об одном объекте, который я просто называю «шейдером», даже если он действительно содержит вершинный шейдер и пиксельный шейдер (и, возможно, геометрические / шейдерные / доменные шейдеры).
Каждый кадр, как только вы нашли список объектов (сеток) для рендеринга, сортируйте их по шейдеру. Идея состоит в том, чтобы минимизировать количество переключений шейдеров в кадре, объединяя все объекты с данным шейдером. Это происходит потому , что переключение шейдеры является несколько дорогостоящей операцией (хотя можно , конечно , сделать это несколько сотен или тысяч раз за кадр, так что это на самом деле не что дорого).
На самом деле, вы можете сделать еще один шаг и отсортировать сетки по шейдерам и материалам. Под «материалом» я подразумеваю комбинацию шейдера и набора текстур и параметров для него. Шейдеры обычно имеют некоторые текстуры и числовые параметры (хранящиеся в постоянных буферах), которые вводятся в них, поэтому, например, материал кирпича и асфальтовый материал могут использовать один и тот же код шейдера, только с разными текстурами.
Для рисования просто зациклите шейдеры, установите каждый шейдер в ID3D11DeviceContext
, установите любые параметры (постоянные буферы, текстуры и т. Д.), Затем нарисуйте объекты. В псевдокоде, включая различие между шейдерами и материалами, я упомянул:
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(...)
Можно еще многое сказать об управлении объектами, сетками, шейдерами, макетами ввода, постоянными буферами и т. Д., Но этого должно быть достаточно для начала работы. :)