Infrastructure de rendu II : rendu de jeuRendering framework II: Game rendering

Notes

Cette rubrique fait partie de la série de didacticiels créer un jeu de plateforme Windows universelle simple (UWP) avec DirectX .This topic is part of the Create a simple Universal Windows Platform (UWP) game with DirectX tutorial series. La rubrique de ce lien définit le contexte de la série.The topic at that link sets the context for the series.

Dans l' infrastructure de rendu I, nous avons abordé la manière dont nous prenons les informations de scène et nous la présentons sur l’écran d’affichage.In Rendering framework I, we've covered how we take the scene info and present it to the display screen. Nous allons maintenant effectuer un pas à pas détaillé et apprendre à préparer les données pour le rendu.Now, we'll take a step back and learn how to prepare the data for rendering.

Notes

Si vous n’avez pas téléchargé le dernier code de jeu pour cet exemple, accédez à l' exemple de jeu Direct3D.If you haven't downloaded the latest game code for this sample, go to Direct3D sample game. Cet exemple fait partie d’une grande collection d’exemples de fonctionnalités UWP.This sample is part of a large collection of UWP feature samples. Pour obtenir des instructions sur le téléchargement de l’exemple, consultez obtenir les exemples UWP à partir de GitHub.For instructions on how to download the sample, see Get the UWP samples from GitHub.

ObjectifObjective

Récapitulatif rapide sur l’objectif.Quick recap on the objective. Il doit comprendre comment configurer une infrastructure de rendu de base pour afficher la sortie graphique pour un jeu DirectX UWP.It is to understand how to set up a basic rendering framework to display the graphics output for a UWP DirectX game. Nous pouvons les regrouper librement en trois étapes.We can loosely group them into these three steps.

  1. Établir une connexion à notre interface graphiqueEstablish a connection to our graphics interface
  2. Préparation : créer les ressources dont nous avons besoin pour dessiner les graphiquesPreparation: Create the resources we need to draw the graphics
  3. Afficher les graphiques : restituer le frameDisplay the graphics: Render the frame

Infrastructure de rendu I : introduction au rendu explication du rendu des graphiques, couvrant les étapes 1 et 3.Rendering framework I: Intro to rendering explained how graphics are rendered, covering Steps 1 and 3.

Cet article explique comment configurer d’autres parties de cette infrastructure et préparer les données requises avant de pouvoir effectuer le rendu, à savoir l’étape 2 du processus.This article explains how to set up other pieces of this framework and prepare the required data before rendering can happen, which is Step 2 of the process.

Concevoir le convertisseurDesign the renderer

Le convertisseur est responsable de la création et de la gestion de tous les objets D3D11 et D2D utilisés pour générer les éléments visuels du jeu.The renderer is responsible for creating and maintaining all the D3D11 and D2D objects used to generate the game visuals. La classe GameRenderer est le convertisseur de cet exemple de jeu et est conçu pour répondre aux besoins de rendu du jeu.The GameRenderer class is the renderer for this sample game and is designed to meet the game's rendering needs.

Voici quelques concepts que vous pouvez utiliser pour vous aider à concevoir le convertisseur pour votre jeu :These are some concepts you can use to help design the renderer for your game:

  • Étant donné que les API Direct3D 11 sont définies en tant qu’API com , vous devez fournir des références ComPtr aux objets définis par ces API.Because Direct3D 11 APIs are defined as COM APIs, you must provide ComPtr references to the objects defined by these APIs. Ces objets sont automatiquement libérés lorsque leur dernière référence sort du contexte une fois l’exécution de l’application terminée.These objects are automatically freed when their last reference goes out of scope when the app terminates. Pour plus d’informations, consultez ComPtr.For more information, see ComPtr. Exemple de ces objets : mémoires tampons constantes, objets nuanceur- nuanceur de sommets, nuanceur de pixelset objets de ressource de nuanceur.Example of these objects: constant buffers, shader objects - vertex shader, pixel shader, and shader resource objects.
  • Les mémoires tampons constantes sont définies dans cette classe pour contenir différentes données nécessaires au rendu.Constant buffers are defined in this class to hold various data needed for rendering.
    • Utilisez plusieurs mémoires tampons constantes avec différentes fréquences pour réduire la quantité de données qui doivent être envoyées au GPU par trame.Use multiple constant buffers with different frequencies to reduce the amount of data that must be sent to the GPU per frame. Cet exemple sépare les constantes dans des mémoires tampons différentes en fonction de la fréquence à laquelle elles doivent être mises à jour.This sample separates constants into different buffers based on the frequency that they must be updated. Il s’agit d’une recommandation pour la programmation Direct3D.This is a best practice for Direct3D programming.
    • Dans cet exemple de jeu, 4 mémoires tampons constantes sont définies.In this sample game, 4 constant buffers are defined.
      1. m _ constantBufferNeverChanges contient les paramètres d’éclairage.m_constantBufferNeverChanges contains the lighting parameters. Elle est définie une fois dans la méthode FinalizeCreateGameDeviceResources et ne change jamais.It's set one time in the FinalizeCreateGameDeviceResources method and never changes again.
      2. m _ constantBufferChangeOnResize contient la matrice de projection.m_constantBufferChangeOnResize contains the projection matrix. La matrice de projection dépend de la taille et des proportions de la fenêtre.The projection matrix is dependent on the size and aspect ratio of the window. Elle est définie dans CreateWindowSizeDependentResources , puis mise à jour après le chargement des ressources dans la méthode FinalizeCreateGameDeviceResources .It's set in CreateWindowSizeDependentResources and then updated after resources are loaded in the FinalizeCreateGameDeviceResources method. Si le rendu est en 3D, il est également modifié deux fois par cadre.If rendering in 3D, it is also changed twice per frame.
      3. m _ constantBufferChangesEveryFrame contient la matrice de vue.m_constantBufferChangesEveryFrame contains the view matrix. Cette matrice dépend de la position de la caméra et de la direction de l’apparence (la normale à la projection) et change une fois par Frame dans la méthode Render .This matrix is dependent on the camera position and look direction (the normal to the projection) and changes one time per frame in the Render method. Cela a été abordé plus haut dans l' infrastructure de rendu I : introduction au rendu, sous la méthode GameRenderer :: Render .This was discussed earlier in Rendering framework I: Intro to rendering, under the GameRenderer::Render method.
      4. m _ constantBufferChangesEveryPrim contient les propriétés de matériau et de matrice de modèle de chaque primitive.m_constantBufferChangesEveryPrim contains the model matrix and material properties of each primitive. La matrice de modèle transforme les vertex des coordonnées locales en coordonnées universelles.The model matrix transforms vertices from local coordinates into world coordinates. Ces constantes sont spécifiques à chaque primitive et sont mises à jour pour chaque appel de dessin.These constants are specific to each primitive and are updated for every draw call. Cela a été abordé plus haut dans l' infrastructure de rendu I : introduction au rendu, sous le rendu primitif.This was discussed earlier in Rendering framework I: Intro to rendering, under the Primitive rendering.
  • Les objets de ressource de nuanceur qui contiennent des textures pour les primitives sont également définis dans cette classe.Shader resource objects that hold textures for the primitives are also defined in this class.
    • Certaines textures sont prédéfinies (DDS est un format de fichier qui peut être utilisé pour stocker des textures compressées et non compressées.Some textures are pre-defined (DDS is a file format that can be used to store compressed and uncompressed textures. Les textures DDS sont utilisées pour les murs et le plancher du monde, ainsi que pour les AMMO.)DDS textures are used for the walls and floor of the world as well as the ammo spheres.)
    • Dans cet exemple de jeu, les objets de ressource de nuanceur sont : m _ sphereTexture, m __ _ cylinderTexture__, m __ _ ceilingTexture__, m _ floorTexture, m _ wallsTexture.In this sample game, shader resource objects are: m_sphereTexture, m_cylinderTexture, m_ceilingTexture, m_floorTexture, m_wallsTexture.
  • Les objets nuanceur sont définis dans cette classe pour calculer nos primitives et leurs textures.Shader objects are defined in this class to compute our primitives and textures.
    • Dans cet exemple de jeu, les objets de nuanceur sont m _ vertexShader, __m _ vertexShaderFlat__et m _ pixelShader, m _ pixelShaderFlat.In this sample game, the shader objects are m_vertexShader, m_vertexShaderFlat, and m_pixelShader, m_pixelShaderFlat.
    • Le nuanceur de vertex traite les primitives et l’éclairage de base, tandis que le nuanceur de pixels (parfois appelé nuanceur de fragments) traite les textures et tous les effets par pixel.The vertex shader processes the primitives and the basic lighting, and the pixel shader (sometimes called a fragment shader) processes the textures and any per-pixel effects.
    • Il existe deux versions de ces nuanceurs (régulier et plat) pour rendre des primitives différentes.There are two versions of these shaders (regular and flat) for rendering different primitives. La raison pour laquelle nous avons des versions différentes est que les versions plates sont bien plus simples et n’effectuent pas de surbrillances spéculaires ou d’effets d’éclairage par pixel.The reason we have different versions is that the flat versions are much simpler and don't do specular highlights or any per pixel lighting effects. Ces nuanceurs sont utilisés pour les murs et accélèrent le rendu sur des périphériques de faible puissance.These are used for the walls and make rendering faster on lower powered devices.

GameRenderer.hGameRenderer.h

Examinons à présent le code de l’objet de classe de convertisseur de l’exemple de jeu.Now let's look at the code in the sample game's renderer class object.

// Class handling the rendering of the game
class GameRenderer : public std::enable_shared_from_this<GameRenderer>
{
public:
    GameRenderer(std::shared_ptr<DX::DeviceResources> const& deviceResources);

    void CreateDeviceDependentResources();
    void CreateWindowSizeDependentResources();
    void ReleaseDeviceDependentResources();
    void Render();
    // --- end of async related methods section

    winrt::Windows::Foundation::IAsyncAction CreateGameDeviceResourcesAsync(_In_ std::shared_ptr<Simple3DGame> game);
    void FinalizeCreateGameDeviceResources();
    winrt::Windows::Foundation::IAsyncAction LoadLevelResourcesAsync();
    void FinalizeLoadLevelResources();

    Simple3DGameDX::IGameUIControl* GameUIControl() { return &m_gameInfoOverlay; };

    DirectX::XMFLOAT2 GameInfoOverlayUpperLeft()
    {
        return DirectX::XMFLOAT2(m_gameInfoOverlayRect.left, m_gameInfoOverlayRect.top);
    };
    DirectX::XMFLOAT2 GameInfoOverlayLowerRight()
    {
        return DirectX::XMFLOAT2(m_gameInfoOverlayRect.right, m_gameInfoOverlayRect.bottom);
    };
    bool GameInfoOverlayVisible() { return m_gameInfoOverlay.Visible(); }
    // --- end of rendering overlay section
...
private:
    // Cached pointer to device resources.
    std::shared_ptr<DX::DeviceResources>        m_deviceResources;

    ...

    // Shader resource objects
    winrt::com_ptr<ID3D11ShaderResourceView>    m_sphereTexture;
    winrt::com_ptr<ID3D11ShaderResourceView>    m_cylinderTexture;
    winrt::com_ptr<ID3D11ShaderResourceView>    m_ceilingTexture;
    winrt::com_ptr<ID3D11ShaderResourceView>    m_floorTexture;
    winrt::com_ptr<ID3D11ShaderResourceView>    m_wallsTexture;

    // Constant buffers
    winrt::com_ptr<ID3D11Buffer>                m_constantBufferNeverChanges;
    winrt::com_ptr<ID3D11Buffer>                m_constantBufferChangeOnResize;
    winrt::com_ptr<ID3D11Buffer>                m_constantBufferChangesEveryFrame;
    winrt::com_ptr<ID3D11Buffer>                m_constantBufferChangesEveryPrim;

    // Texture sampler
    winrt::com_ptr<ID3D11SamplerState>          m_samplerLinear;

    // Shader objects: Vertex shaders and pixel shaders
    winrt::com_ptr<ID3D11VertexShader>          m_vertexShader;
    winrt::com_ptr<ID3D11VertexShader>          m_vertexShaderFlat;
    winrt::com_ptr<ID3D11PixelShader>           m_pixelShader;
    winrt::com_ptr<ID3D11PixelShader>           m_pixelShaderFlat;
    winrt::com_ptr<ID3D11InputLayout>           m_vertexLayout;
};

ConstructeurConstructor

Nous allons ensuite examiner le constructeur GameRenderer du jeu d’exemples et le comparer au constructeur Sample3DSceneRenderer fourni dans le modèle d’application DirectX 11.Next, let's examine the sample game's GameRenderer constructor and compare it with the Sample3DSceneRenderer constructor provided in the DirectX 11 App template.

// Constructor method of the main rendering class object
GameRenderer::GameRenderer(std::shared_ptr<DX::DeviceResources> const& deviceResources) : ...
    m_gameInfoOverlay(deviceResources),
    m_gameHud(deviceResources, L"Windows platform samples", L"DirectX first-person game sample")
{
    // m_gameInfoOverlay is a GameHud object to render text in the top left corner of the screen.
    // m_gameHud is Game info rendered as an overlay on the top-right corner of the screen,
    // for example hits, shots, and time.

    CreateDeviceDependentResources();
    CreateWindowSizeDependentResources();
}

Créer et charger des ressources graphiques DirectXCreate and load DirectX graphic resources

Dans l’exemple de jeu (et dans le modèle d' application DirectX 11 (Windows universel) de Visual Studio), la création et le chargement des ressources de jeu sont implémentées à l’aide de ces deux méthodes qui sont appelées à partir du constructeur GameRenderer :In the sample game (and in Visual Studio's DirectX 11 App (Universal Windows) template), creating and loading game resources is implemented using these two methods that are called from GameRenderer constructor:

Méthode CreateDeviceDependentResourcesCreateDeviceDependentResources method

Dans le modèle d’application DirectX 11, cette méthode est utilisée pour charger le nuanceur vertex et pixel de manière asynchrone, créer le nuanceur et la mémoire tampon constante, créer un maillage avec des sommets qui contiennent des informations sur la position et la couleur.In the DirectX 11 App template, this method is used to load vertex and pixel shader asynchronously, create the shader and constant buffer, create a mesh with vertices that contain position and color info.

Dans l’exemple de jeu, ces opérations des objets de scène sont à la place fractionnées entre les méthodes CreateGameDeviceResourcesAsync et FinalizeCreateGameDeviceResources .In the sample game, these operations of the scene objects are instead split among the CreateGameDeviceResourcesAsync and FinalizeCreateGameDeviceResources methods.

Pour cet exemple de jeu, qu’est-ce qui va dans cette méthode ?For this sample game, what goes into this method?

  • Variables instanciées (m _ gameResourcesLoaded = false et m _ levelResourcesLoaded = false) qui indiquent si les ressources ont été chargées avant le rendu, puisque nous les chargeons de manière asynchrone.Instantiated variables (m_gameResourcesLoaded = false and m_levelResourcesLoaded = false) that indicate whether resources have been loaded before moving forward to render, since we're loading them asynchronously.
  • Étant donné que le rendu HUD et superposition se trouvent dans des objets de classe distincts, appelez ici les méthodes GameHud :: CreateDeviceDependentResources et GameInfoOverlay :: CreateDeviceDependentResources .Since HUD and overlay rendering are in separate class objects, call GameHud::CreateDeviceDependentResources and GameInfoOverlay::CreateDeviceDependentResources methods here.

Voici le code de GameRenderer :: CreateDeviceDependentResources.Here's the code for GameRenderer::CreateDeviceDependentResources.

// This method is called in GameRenderer constructor when it's created in GameMain constructor.
void GameRenderer::CreateDeviceDependentResources()
{
    // instantiate variables that indicate whether resources were loaded.
    m_gameResourcesLoaded = false;
    m_levelResourcesLoaded = false;

    // game HUD and overlay are design as separate class objects.
    m_gameHud.CreateDeviceDependentResources();
    m_gameInfoOverlay.CreateDeviceDependentResources();
}

Vous trouverez ci-dessous une liste des méthodes utilisées pour créer et charger des ressources.Below is a list of the methods that are used to create and load resources.

  • CreateDeviceDependentResourcesCreateDeviceDependentResources
    • CreateGameDeviceResourcesAsync (ajouté)CreateGameDeviceResourcesAsync (Added)
    • FinalizeCreateGameDeviceResources (ajouté)FinalizeCreateGameDeviceResources (Added)
  • CreateWindowSizeDependentResourcesCreateWindowSizeDependentResources

Avant de plonger dans les autres méthodes utilisées pour créer et charger des ressources, commençons par créer le convertisseur et voyons comment il s’intègre à la boucle de jeu.Before diving into the other methods that are used to create and load resources, let's first create the renderer and see how it fits into the game loop.

Créer le convertisseurCreate the renderer

Le GameRenderer est créé dans le constructeur de GameMain.The GameRenderer is created in the GameMain's constructor. Il appelle également les deux autres méthodes, CreateGameDeviceResourcesAsync et FinalizeCreateGameDeviceResources , qui sont ajoutées pour aider à créer et charger des ressources.It also calls the two other methods, CreateGameDeviceResourcesAsync and FinalizeCreateGameDeviceResources that are added to help create and load resources.

GameMain::GameMain(std::shared_ptr<DX::DeviceResources> const& deviceResources) : ...
{
    m_deviceResources->RegisterDeviceNotify(this);

    // Creation of GameRenderer
    m_renderer = std::make_shared<GameRenderer>(m_deviceResources);

    ...

    ConstructInBackground();
}

winrt::fire_and_forget GameMain::ConstructInBackground()
{
    ...

    // Asynchronously initialize the game class and load the renderer device resources.
    // By doing all this asynchronously, the game gets to its main loop more quickly
    // and in parallel all the necessary resources are loaded on other threads.
    m_game->Initialize(m_controller, m_renderer);

    co_await m_renderer->CreateGameDeviceResourcesAsync(m_game);

    // The finalize code needs to run in the same thread context
    // as the m_renderer object was created because the D3D device context
    // can ONLY be accessed on a single thread.
    // co_await of an IAsyncAction resumes in the same thread context.
    m_renderer->FinalizeCreateGameDeviceResources();

    InitializeGameState();

    ...
}

Méthode CreateGameDeviceResourcesAsyncCreateGameDeviceResourcesAsync method

CreateGameDeviceResourcesAsync est appelé à partir de la méthode de constructeur GameMain dans la boucle Create _ Task , car nous chargeons les ressources de jeu de manière asynchrone.CreateGameDeviceResourcesAsync is called from the GameMain constructor method in the create_task loop since we're loading game resources asynchronously.

CreateDeviceResourcesAsync est une méthode qui exécute un ensemble de tâches asynchrones afin de charger les ressources de jeu.CreateDeviceResourcesAsync is a method that runs as a separate set of async tasks to load the game resources. Étant donné qu’il est supposé s’exécuter sur un thread distinct, il a uniquement accès aux méthodes d’appareil Direct3D 11 (celles définies sur ID3D11Device) et non aux méthodes de contexte de périphérique (les méthodes définies sur ID3D11DeviceContext), de sorte qu’il n’effectue aucun rendu.Because it's expected to run on a separate thread, it only has access to the Direct3D 11 device methods (those defined on ID3D11Device) and not the device context methods (the methods defined on ID3D11DeviceContext), so it does not perform any rendering.

La méthode FinalizeCreateGameDeviceResources s’exécute sur le thread principal et a accès aux méthodes de contexte de périphérique Direct3D 11.FinalizeCreateGameDeviceResources method runs on the main thread and does have access to the Direct3D 11 device context methods.

En principe :In principle:

  • Utilisez uniquement les méthodes ID3D11Device dans CreateGameDeviceResourcesAsync , car elles sont libres de thread, ce qui signifie qu’elles peuvent s’exécuter sur n’importe quel thread.Use only ID3D11Device methods in CreateGameDeviceResourcesAsync because they are free-threaded, which means that they are able to run on any thread. Il est également prévu qu’ils ne s’exécutent pas sur le même thread que celui sur lequel le GameRenderer a été créé.It is also expected that they do not run on the same thread as the one GameRenderer was created on.
  • N’utilisez pas de méthodes dans ID3D11DeviceContext ici, car elles doivent s’exécuter sur un thread unique et sur le même thread que GameRenderer.Do not use methods in ID3D11DeviceContext here because they need to run on a single thread and on the same thread as GameRenderer.
  • Utilisez cette méthode pour créer des mémoires tampons constantes.Use this method to create constant buffers.
  • Utilisez cette méthode pour charger des textures (comme les fichiers. DDS) et des informations de nuanceur (comme les fichiers. CSO) dans les nuanceurs.Use this method to load textures (like the .dds files) and shader info (like the .cso files) into the shaders.

Cette méthode permet d’effectuer les opérations suivantes :This method is used to:

  • Créez les 4 mémoires tampons constantes: m _ constantBufferNeverChanges, m __ _ constantBufferChangeOnResize__, m __ _ constantBufferChangesEveryFrame__, m _ constantBufferChangesEveryPrimCreate the 4 constant buffers: m_constantBufferNeverChanges, m_constantBufferChangeOnResize, m_constantBufferChangesEveryFrame, m_constantBufferChangesEveryPrim
  • Créer un objet d' État d’échantillonnage qui encapsule les informations d’échantillonnage pour une textureCreate a sampler-state object that encapsulates sampling information for a texture
  • Créez un groupe de tâches qui contient toutes les tâches asynchrones créées par la méthode.Create a task group that contains all async tasks created by the method. Elle attend la fin de toutes ces tâches Async, puis appelle FinalizeCreateGameDeviceResources.It waits for the completion of all these async tasks, and then calls FinalizeCreateGameDeviceResources.
  • Créez un chargeur à l’aide du chargeur de base.Create a loader using Basic Loader. Ajoutez les opérations de chargement asynchrone du chargeur en tant que tâches dans le groupe de tâches créé précédemment.Add the loader's async loading operations as tasks into the task group created earlier.
  • Des méthodes telles que BasicLoader :: LoadShaderAsync et BasicLoader :: LoadTextureAsync sont utilisées pour charger :Methods like BasicLoader::LoadShaderAsync and BasicLoader::LoadTextureAsync are used to load:
    • objets de nuanceur compilés (VertextShader. CSO, VertexShaderFlat. CSO, PixelShader. CSO et PixelShaderFlat. CSO).compiled shader objects (VertextShader.cso, VertexShaderFlat.cso, PixelShader.cso, and PixelShaderFlat.cso). Pour plus d’informations, accédez à différents formats de fichiers de nuanceur.For more info, go to Various shader file formats.
    • textures spécifiques aux jeux (biens de \ surface. DDS, metal_texture. DDS, cellceiling. DDS, cellfloor. DDS, cellwall. DDS).game specific textures (Assets\seafloor.dds, metal_texture.dds, cellceiling.dds, cellfloor.dds, cellwall.dds).
IAsyncAction GameRenderer::CreateGameDeviceResourcesAsync(_In_ std::shared_ptr<Simple3DGame> game)
{
    auto lifetime = shared_from_this();

    // Create the device dependent game resources.
    // Only the d3dDevice is used in this method. It is expected
    // to not run on the same thread as the GameRenderer was created.
    // Create methods on the d3dDevice are free-threaded and are safe while any methods
    // in the d3dContext should only be used on a single thread and handled
    // in the FinalizeCreateGameDeviceResources method.
    m_game = game;

    auto d3dDevice = m_deviceResources->GetD3DDevice();

    // Define D3D11_BUFFER_DESC. See
    // https://docs.microsoft.com/windows/win32/api/d3d11/ns-d3d11-d3d11_buffer_desc
    D3D11_BUFFER_DESC bd;
    ZeroMemory(&bd, sizeof(bd));

    // Create the constant buffers.
    bd.Usage = D3D11_USAGE_DEFAULT;
    ...

    // Create the constant buffers: m_constantBufferNeverChanges, m_constantBufferChangeOnResize,
    // m_constantBufferChangesEveryFrame, m_constantBufferChangesEveryPrim
    // CreateBuffer is used to create one of these buffers: vertex buffer, index buffer, or 
    // shader-constant buffer. For CreateBuffer API ref info, see
    // https://docs.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11device-createbuffer.
    winrt::check_hresult(
        d3dDevice->CreateBuffer(&bd, nullptr, m_constantBufferNeverChanges.put())
        );

    ...

    // Define D3D11_SAMPLER_DESC. For API ref, see
    // https://docs.microsoft.com/windows/win32/api/d3d11/ns-d3d11-d3d11_sampler_desc.
    D3D11_SAMPLER_DESC sampDesc;

    // ZeroMemory fills a block of memory with zeros. For API ref, see
    // https://docs.microsoft.com/previous-versions/windows/desktop/legacy/aa366920(v=vs.85).
    ZeroMemory(&sampDesc, sizeof(sampDesc));

    sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
    sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
    sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
    ...

    // Create a sampler-state object that encapsulates sampling information for a texture.
    // The sampler-state interface holds a description for sampler state that you can bind to any 
    // shader stage of the pipeline for reference by texture sample operations.
    winrt::check_hresult(
        d3dDevice->CreateSamplerState(&sampDesc, m_samplerLinear.put())
        );

    // Start the async tasks to load the shaders and textures.

    // Load compiled shader objects (VertextShader.cso, VertexShaderFlat.cso, PixelShader.cso, and PixelShaderFlat.cso).
    // The BasicLoader class is used to convert and load common graphics resources, such as meshes, textures, 
    // and various shader objects into the constant buffers. For more info, see
    // https://docs.microsoft.com/windows/uwp/gaming/complete-code-for-basicloader.
    BasicLoader loader{ d3dDevice };

    std::vector<IAsyncAction> tasks;

    uint32_t numElements = ARRAYSIZE(PNTVertexLayout);

    // Load shaders asynchronously with the shader and pixel data using the
    // BasicLoader::LoadShaderAsync method. Push these method calls into a list of tasks.
    tasks.push_back(loader.LoadShaderAsync(L"VertexShader.cso", PNTVertexLayout, numElements, m_vertexShader.put(), m_vertexLayout.put()));
    tasks.push_back(loader.LoadShaderAsync(L"VertexShaderFlat.cso", nullptr, numElements, m_vertexShaderFlat.put(), nullptr));
    tasks.push_back(loader.LoadShaderAsync(L"PixelShader.cso", m_pixelShader.put()));
    tasks.push_back(loader.LoadShaderAsync(L"PixelShaderFlat.cso", m_pixelShaderFlat.put()));

    // Make sure the previous versions if any of the textures are released.
    m_sphereTexture = nullptr;
    ...

    // Load Game specific textures (Assets\\seafloor.dds, metal_texture.dds, cellceiling.dds,
    // cellfloor.dds, cellwall.dds).
    // Push these method calls also into a list of tasks.
    tasks.push_back(loader.LoadTextureAsync(L"Assets\\seafloor.dds", nullptr, m_sphereTexture.put()));
    ...

    // Simulate loading additional resources by introducing a delay.
    tasks.push_back([]() -> IAsyncAction { co_await winrt::resume_after(GameConstants::InitialLoadingDelay); }());

    // Returns when all the async tasks for loading the shader and texture assets have completed.
    for (auto&& task : tasks)
    {
        co_await task;
    }
}

Méthode FinalizeCreateGameDeviceResourcesFinalizeCreateGameDeviceResources method

La méthode FinalizeCreateGameDeviceResources est appelée après l’exécution de toutes les tâches de chargement des ressources qui se trouvent dans la méthode CreateGameDeviceResourcesAsync .FinalizeCreateGameDeviceResources method is called after all the load resources tasks that are in the CreateGameDeviceResourcesAsync method completes.

  • Initialisez constantBufferNeverChanges avec les positions et la couleur de l’éclairage.Initialize constantBufferNeverChanges with the light positions and color. Charge les données initiales dans les mémoires tampons constantes à l’aide d’un appel de méthode de contexte de périphérique à ID3D11DeviceContext :: UpdateSubresource.Loads the initial data into the constant buffers with a device context method call to ID3D11DeviceContext::UpdateSubresource.
  • Étant donné que le chargement des ressources chargées de façon asynchrone est terminé, il est temps de les associer aux objets de jeu appropriés.Since asynchronously loaded resources have completed loading, it's time to associate them with the appropriate game objects.
  • Pour chaque objet de jeu, créez le maillage et le matériau à l’aide des textures qui ont été chargées.For each game object, create the mesh and the material using the textures that have been loaded. Associez ensuite le maillage et le matériau à l’objet de jeu.Then associate the mesh and material to the game object.
  • Pour l’objet de jeu targets, la texture composée d’anneaux colorés concentriques, avec une valeur numérique en haut, n’est pas chargée à partir d’un fichier de texture.For the targets game object, the texture which is composed of concentric colored rings, with a numeric value on the top, is not loaded from a texture file. Au lieu de cela, elle est générée de façon procédurale à l’aide du code dans TargetTexture. cpp.Instead, it's procedurally generated using the code in TargetTexture.cpp. La classe TargetTexture crée les ressources nécessaires pour dessiner la texture dans une ressource hors écran au moment de l’initialisation.The TargetTexture class creates the necessary resources to draw the texture into an off screen resource at initialization time. La texture qui en résulte est ensuite associée aux objets de jeu cibles appropriés.The resulting texture is then associated with the appropriate target game objects.

FinalizeCreateGameDeviceResources et CreateWindowSizeDependentResources partagent des portions similaires de code pour les éléments suivants :FinalizeCreateGameDeviceResources and CreateWindowSizeDependentResources share similar portions of code for these:

  • Utilisez SetProjParams pour vous assurer que la caméra possède la matrice de projection appropriée.Use SetProjParams to ensure that the camera has the right projection matrix. Pour plus d’informations, accédez à la caméra età l’espace de coordonnées.For more info, go to Camera and coordinate space.
  • Gérer la rotation d’écran en multipliant la matrice de rotation 3D à la matrice de projection de la caméra.Handle screen rotation by post multiplying the 3D rotation matrix to the camera's projection matrix. Ensuite, mettez à jour la mémoire tampon constante ConstantBufferChangeOnResize avec la matrice de projection résultante.Then update the ConstantBufferChangeOnResize constant buffer with the resulting projection matrix.
  • Définissez la variable globale booléenne m _ gameResourcesLoaded pour indiquer que les ressources sont maintenant chargées dans les tampons, prêt pour l’étape suivante.Set the m_gameResourcesLoaded Boolean global variable to indicate that the resources are now loaded in the buffers, ready for the next step. Rappelez-vous que nous avons d’abord initialisé cette variable avec la valeur false dans la méthode du constructeur de GameRenderer, par le biais de la méthode GameRenderer :: CreateDeviceDependentResources .Recall that we first initialized this variable as FALSE in the GameRenderer's constructor method, through the GameRenderer::CreateDeviceDependentResources method.
  • Lorsque ce __ _ GameResourcesLoaded__ est true, le rendu des objets de scène peut avoir lieu.When this m_gameResourcesLoaded is TRUE, rendering of the scene objects can take place. Cela a été abordé dans l' infrastructure de rendu I : introduction au rendu de l’article, sous la méthode GameRenderer :: Render.This was covered in the Rendering framework I: Intro to rendering article, under GameRenderer::Render method.
// This method is called from the GameMain constructor.
// Make sure that 2D rendering is occurring on the same thread as the main rendering.
void GameRenderer::FinalizeCreateGameDeviceResources()
{
    // All asynchronously loaded resources have completed loading.
    // Now associate all the resources with the appropriate game objects.
    // This method is expected to run in the same thread as the GameRenderer
    // was created. All work will happen behind the "Loading ..." screen after the
    // main loop has been entered.

    // Initialize the Constant buffer with the light positions
    // These are handled here to ensure that the d3dContext is only
    // used in one thread.

    auto d3dDevice = m_deviceResources->GetD3DDevice();

    ConstantBufferNeverChanges constantBufferNeverChanges;
    constantBufferNeverChanges.lightPosition[0] = XMFLOAT4(3.5f, 2.5f, 5.5f, 1.0f);
    ...
    constantBufferNeverChanges.lightColor = XMFLOAT4(0.25f, 0.25f, 0.25f, 1.0f);

    // CPU copies data from memory (constantBufferNeverChanges) to a subresource 
    // created in non-mappable memory (m_constantBufferNeverChanges) which was created in the earlier 
    // CreateGameDeviceResourcesAsync method. For UpdateSubresource API ref info, 
    // go to: https://msdn.microsoft.com/library/windows/desktop/ff476486.aspx
    // To learn more about what a subresource is, go to:
    // https://msdn.microsoft.com/library/windows/desktop/ff476901.aspx

    m_deviceResources->GetD3DDeviceContext()->UpdateSubresource(
        m_constantBufferNeverChanges.get(),
        0,
        nullptr,
        &constantBufferNeverChanges,
        0,
        0
        );

    // For the objects that function as targets, they have two unique generated textures.
    // One version is used to show that they have never been hit and the other is 
    // used to show that they have been hit.
    // TargetTexture is a helper class to procedurally generate textures for game
    // targets. The class creates the necessary resources to draw the texture into 
    // an off screen resource at initialization time.

    TargetTexture textureGenerator(
        d3dDevice,
        m_deviceResources->GetD2DFactory(),
        m_deviceResources->GetDWriteFactory(),
        m_deviceResources->GetD2DDeviceContext()
        );

    // CylinderMesh is a class derived from MeshObject and creates a ID3D11Buffer of
    // vertices and indices to represent a canonical cylinder (capped at
    // both ends) that is positioned at the origin with a radius of 1.0,
    // a height of 1.0 and with its axis in the +Z direction.
    // In the game sample, there are various types of mesh types:
    // CylinderMesh (vertical rods), SphereMesh (balls that the player shoots), 
    // FaceMesh (target objects), and WorldMesh (Floors and ceilings that define the enclosed area)

    auto cylinderMesh = std::make_shared<CylinderMesh>(d3dDevice, (uint16_t)26);
    ...

    // The Material class maintains the properties that represent how an object will
    // look when it is rendered.  This includes the color of the object, the
    // texture used to render the object, and the vertex and pixel shader that
    // should be used for rendering.

    auto cylinderMaterial = std::make_shared<Material>(
        XMFLOAT4(0.8f, 0.8f, 0.8f, .5f),
        XMFLOAT4(0.8f, 0.8f, 0.8f, .5f),
        XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
        15.0f,
        m_cylinderTexture.get(),
        m_vertexShader.get(),
        m_pixelShader.get()
        );

    ...

    // Attach the textures to the appropriate game objects.
    // We'll loop through all the objects that need to be rendered.
    for (auto&& object : m_game->RenderObjects())
    {
        if (object->TargetId() == GameConstants::WorldFloorId)
        {
            // Assign a normal material for the floor object.
            // This normal material uses the floor texture (cellfloor.dds) that was loaded asynchronously from
            // the Assets folder using BasicLoader::LoadTextureAsync method in the earlier 
            // CreateGameDeviceResourcesAsync loop

            object->NormalMaterial(
                std::make_shared<Material>(
                    XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f),
                    XMFLOAT4(0.8f, 0.8f, 0.8f, 1.0f),
                    XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f),
                    150.0f,
                    m_floorTexture.get(),
                    m_vertexShaderFlat.get(),
                    m_pixelShaderFlat.get()
                    )
                );
            // Creates a mesh object called WorldFloorMesh and assign it to the floor object.
            object->Mesh(std::make_shared<WorldFloorMesh>(d3dDevice));
        }
        ...
        else if (auto cylinder = dynamic_cast<Cylinder*>(object.get()))
        {
            cylinder->Mesh(cylinderMesh);
            cylinder->NormalMaterial(cylinderMaterial);
        }
        else if (auto target = dynamic_cast<Face*>(object.get()))
        {
            const int bufferLength = 16;
            wchar_t str[bufferLength];
            int len = swprintf_s(str, bufferLength, L"%d", target->TargetId());
            auto string{ winrt::hstring(str, len) };

            winrt::com_ptr<ID3D11ShaderResourceView> texture;
            textureGenerator.CreateTextureResourceView(string, texture.put());
            target->NormalMaterial(
                std::make_shared<Material>(
                    XMFLOAT4(0.8f, 0.8f, 0.8f, 0.5f),
                    XMFLOAT4(0.8f, 0.8f, 0.8f, 0.5f),
                    XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f),
                    5.0f,
                    texture.get(),
                    m_vertexShader.get(),
                    m_pixelShader.get()
                    )
                );

            texture = nullptr;
            textureGenerator.CreateHitTextureResourceView(string, texture.put());
            target->HitMaterial(
                std::make_shared<Material>(
                    XMFLOAT4(0.8f, 0.8f, 0.8f, 0.5f),
                    XMFLOAT4(0.8f, 0.8f, 0.8f, 0.5f),
                    XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f),
                    5.0f,
                    texture.get(),
                    m_vertexShader.get(),
                    m_pixelShader.get()
                    )
                );

            target->Mesh(targetMesh);
        }
        ...
    }

    // The SetProjParams method calculates the projection matrix based on input params and
    // ensures that the camera has been initialized with the right projection
    // matrix.  
    // The camera is not created at the time the first window resize event occurs.

    auto renderTargetSize = m_deviceResources->GetRenderTargetSize();
    m_game->GameCamera().SetProjParams(
        XM_PI / 2,
        renderTargetSize.Width / renderTargetSize.Height,
        0.01f,
        100.0f
        );

    // Make sure that the correct projection matrix is set in the ConstantBufferChangeOnResize buffer.

    // Get the 3D rotation transform matrix. We are handling screen rotations directly to eliminate an unaligned 
    // fullscreen copy. So it is necessary to post multiply the 3D rotation matrix to the camera's projection matrix
    // to get the projection matrix that we need.

    auto orientation = m_deviceResources->GetOrientationTransform3D();

    ConstantBufferChangeOnResize changesOnResize;

    // The matrices are transposed due to the shader code expecting the matrices in the opposite
    // row/column order from the DirectX math library.

    // XMStoreFloat4x4 takes a matrix and writes the components out to sixteen single-precision floating-point values at the given address. 
    // The most significant component of the first row vector is written to the first four bytes of the address, 
    // followed by the second most significant component of the first row, and so on. The second row is then written out in a 
    // like manner to memory beginning at byte 16, followed by the third row to memory beginning at byte 32, and finally 
    // the fourth row to memory beginning at byte 48. For more API ref info, go to: 
    // https://msdn.microsoft.com/library/windows/desktop/microsoft.directx_sdk.storing.xmstorefloat4x4.aspx

    XMStoreFloat4x4(
        &changesOnResize.projection,
        XMMatrixMultiply(
            XMMatrixTranspose(m_game->GameCamera().Projection()),
            XMMatrixTranspose(XMLoadFloat4x4(&orientation))
            )
        );

    // UpdateSubresource method instructs CPU to copy data from memory (changesOnResize) to a subresource 
    // created in non-mappable memory (m_constantBufferChangeOnResize ) which was created in the earlier 
    // CreateGameDeviceResourcesAsync method.

    m_deviceResources->GetD3DDeviceContext()->UpdateSubresource(
        m_constantBufferChangeOnResize.get(),
        0,
        nullptr,
        &changesOnResize,
        0,
        0
        );

    // Finally we set the m_gameResourcesLoaded as TRUE, so we can start rendering.
    m_gameResourcesLoaded = true;
}

Méthode CreateWindowSizeDependentResourceCreateWindowSizeDependentResource method

Les méthodes CreateWindowSizeDependentResources sont appelées chaque fois que la taille de la fenêtre, l’orientation, le rendu stéréo ou la résolution change.CreateWindowSizeDependentResources methods are called every time the window size, orientation, stereo-enabled rendering, or resolution changes. Dans l’exemple de jeu, elle met à jour la matrice de projection dans ConstantBufferChangeOnResize.In the sample game, it updates the projection matrix in ConstantBufferChangeOnResize.

Les ressources de taille de fenêtre sont mises à jour de cette manière :Window size resources are updated in this manner:

  • L’infrastructure d’application obtient l’un des différents événements possibles indiquant une modification de l’état de la fenêtre.The App framework gets one of several possible events indicating a change in the window state.
  • Votre principale boucle de jeu est ensuite informée de l’événement et appelle CreateWindowSizeDependentResources sur l’instance de classe principale (GameMain), qui appelle ensuite l’implémentation CreateWindowSizeDependentResources dans la classe de convertisseur de jeu (GameRenderer).Your main game loop is then informed about the event and calls CreateWindowSizeDependentResources on the main class (GameMain) instance, which then calls the CreateWindowSizeDependentResources implementation in the game renderer (GameRenderer) class.
  • Le travail principal de cette méthode consiste à s’assurer que les éléments visuels ne sont pas confondus ou non valides en raison d’une modification des propriétés de la fenêtre.The primary job of this method is to make sure the visuals don't become confused or invalid because of a change in window properties.

Pour cet exemple de jeu, un certain nombre d’appels de méthode sont les mêmes que la méthode FinalizeCreateGameDeviceResources .For this sample game, a number of method calls are the same as the FinalizeCreateGameDeviceResources method. Pour la procédure pas à pas de code, consultez la section précédente.For code walkthrough, go to the previous section.

Les réglages de la taille de la fenêtre superposition et de la fenêtre superposée sont traités dans Ajouter une interface utilisateur.The game HUD and overlay window size rendering adjustments is covered under Add a user interface.

// Initializes view parameters when the window size changes.
void GameRenderer::CreateWindowSizeDependentResources()
{
    // Game HUD and overlay window size rendering adjustments are done here
    // but they'll be covered in the UI section instead.

    m_gameHud.CreateWindowSizeDependentResources();

    ...

    auto d3dContext = m_deviceResources->GetD3DDeviceContext();
    // In Sample3DSceneRenderer::CreateWindowSizeDependentResources, we had:
    // Size outputSize = m_deviceResources->GetOutputSize();

    auto renderTargetSize = m_deviceResources->GetRenderTargetSize();

    ...

    m_gameInfoOverlay.CreateWindowSizeDependentResources(m_gameInfoOverlaySize);

    if (m_game != nullptr)
    {
        // Similar operations as the last section of FinalizeCreateGameDeviceResources method
        m_game->GameCamera().SetProjParams(
            XM_PI / 2, renderTargetSize.Width / renderTargetSize.Height,
            0.01f,
            100.0f
            );

        XMFLOAT4X4 orientation = m_deviceResources->GetOrientationTransform3D();

        ConstantBufferChangeOnResize changesOnResize;
        XMStoreFloat4x4(
            &changesOnResize.projection,
            XMMatrixMultiply(
                XMMatrixTranspose(m_game->GameCamera().Projection()),
                XMMatrixTranspose(XMLoadFloat4x4(&orientation))
                )
            );

        d3dContext->UpdateSubresource(
            m_constantBufferChangeOnResize.get(),
            0,
            nullptr,
            &changesOnResize,
            0,
            0
            );
    }
}

Étapes suivantesNext steps

Il s’agit du processus de base pour implémenter l’infrastructure de rendu graphique d’un jeu.This is the basic process for implementing the graphics rendering framework of a game. Plus votre jeu est grand, plus vous devez mettre en place des abstractions pour gérer les hiérarchies de types d’objets et les comportements d’animation.The larger your game, the more abstractions you would have to put in place to handle hierarchies of object types and animation behaviors. Vous devez implémenter des méthodes plus complexes pour le chargement et la gestion des ressources telles que les maillages et les textures.You need to implement more complex methods for loading and managing assets such as meshes and textures. Voyons ensuite comment Ajouter une interface utilisateur.Next, let's learn how to add a user interface.