Tuesday, July 10, 2018

c++ - Different shaders for different objects DirectX 11


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:




  1. 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).




  2. 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.




  3. 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

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...