I am learning Direct3D 11
, and in all basic tutorials I found on shader writing, Vertex and Pixel shaders are written so they transform whole scene same way. Tutorials like render cube with texture...
But I wonder, how do you differentiate objects? What if you want, for example, to simulate mirror surface on some object and use different shaders to render rest of the scene? I think that most games must use many vertex and pixel shaders to achieve various look and transformations.
Thank you.
Answer
Yes, a game engine will in general have a variety of different shaders. The typical pattern is:
While initializing the engine and loading the game world, prepare all the shaders you will use for rendering. By "prepare" I mean load them into memory, compile them if necessary, and do all the
ID3D11Device::CreatePixelShader
and similar calls to get the D3D shader objects allocated and ready to go. Keep the objects in an array or some other data structure.Usually you will have a one-to-one relationship between vertex shaders and pixel shaders that are designed to work together. I think of them as a single object, which I just call a "shader", even though it really contains a vertex shader and a pixel shader (and maybe geometry/hull/domain shaders as well).
Each frame, once you have found the list of objects (meshes) to render, sort them by shader. The idea is to minimize the number of times you switch shaders in a frame, by drawing all the objects with a given shader together. This is because switching shaders is a somewhat expensive operation (although you can certainly do it several hundred or a thousand times per frame, so it's not really that expensive).
In fact, you can go one step further and sort meshes by shader first and material second. By "material", I mean a combination of a shader and a set of textures and parameters for it. Shaders usually have some textures and numeric parameters (stored in constant buffers) that feed into them, so for instance a brick material and asphalt material might use the same shader code, just with different textures.
To draw, just loop over the shaders, set each shader in the
ID3D11DeviceContext
, set any parameters (constant buffers, textures, etc.), then draw the objects. In pseudocode, including the shaders/materials distinction I mentioned: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(...)
There's a lot more that could be said about managing objects, meshes, shaders, input layouts, constant buffers, etc. but this should be enough to get you started. :)
No comments:
Post a Comment