Framework di rendering II: rendering del giocoRendering framework II: Game rendering

Nota

Questo argomento fa parte della serie di esercitazioni su come creare un semplice piattaforma UWP (Universal Windows Platform) (UWP) con DirectX .This topic is part of the Create a simple Universal Windows Platform (UWP) game with DirectX tutorial series. L'argomento in corrispondenza di tale collegamento imposta il contesto per la serie.The topic at that link sets the context for the series.

Nel Framework di rendering i, abbiamo analizzato il modo in cui prendiamo le informazioni sulla scena e le presenti nella schermata di visualizzazione.In Rendering framework I, we've covered how we take the scene info and present it to the display screen. A questo punto, verrà illustrato come preparare i dati per il rendering.Now, we'll take a step back and learn how to prepare the data for rendering.

Nota

Se non è stato scaricato il codice del gioco più recente per questo esempio, vedere il gioco di esempio Direct3D.If you haven't downloaded the latest game code for this sample, go to Direct3D sample game. Questo esempio fa parte di una vasta raccolta di esempi di funzionalità UWP.This sample is part of a large collection of UWP feature samples. Per istruzioni su come scaricare l'esempio, vedere ottenere gli esempi di UWP da GitHub.For instructions on how to download the sample, see Get the UWP samples from GitHub.

ObiettivoObjective

Riepilogo rapido dell'obiettivo.Quick recap on the objective. È necessario comprendere come configurare un Framework di rendering di base per visualizzare l'output di grafica per un gioco UWP DirectX.It is to understand how to set up a basic rendering framework to display the graphics output for a UWP DirectX game. È possibile raggrupparli liberamente in questi tre passaggi.We can loosely group them into these three steps.

  1. Stabilire una connessione con l'interfaccia graficaEstablish a connection to our graphics interface
  2. Preparazione: creare le risorse necessarie per creare la graficaPreparation: Create the resources we need to draw the graphics
  3. Visualizzare la grafica: eseguire il rendering del frameDisplay the graphics: Render the frame

Framework di rendering I: Introduzione al rendering ha illustrato come viene eseguito il rendering della grafica, coprendo i passaggi 1 e 3.Rendering framework I: Intro to rendering explained how graphics are rendered, covering Steps 1 and 3.

Questo articolo illustra come configurare altre parti di questo Framework e preparare i dati necessari prima che il rendering possa verificarsi, ovvero il passaggio 2 del processo.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.

Progettare il rendererDesign the renderer

Il renderer è responsabile della creazione e della gestione di tutti gli oggetti D3D11 e D2D usati per generare gli oggetti visivi del gioco.The renderer is responsible for creating and maintaining all the D3D11 and D2D objects used to generate the game visuals. La classe GameRenderer è il renderer per questo gioco di esempio ed è progettata per soddisfare le esigenze di rendering del gioco.The GameRenderer class is the renderer for this sample game and is designed to meet the game's rendering needs.

Questi sono alcuni dei concetti che è possibile usare per progettare il renderer per il gioco:These are some concepts you can use to help design the renderer for your game:

  • Poiché le API Direct3D 11 sono definite come API com , è necessario fornire riferimenti ComPtr agli oggetti definiti da queste API.Because Direct3D 11 APIs are defined as COM APIs, you must provide ComPtr references to the objects defined by these APIs. Questi oggetti vengono automaticamente rilasciati quando il loro ultimo riferimento esce dall'ambito al termine dell'app.These objects are automatically freed when their last reference goes out of scope when the app terminates. Per ulteriori informazioni, vedere ComPtr.For more information, see ComPtr. Esempio di questi oggetti: buffer costanti, oggetti shader- vertex shader, pixel shadere oggetti risorsa shader.Example of these objects: constant buffers, shader objects - vertex shader, pixel shader, and shader resource objects.
  • I buffer costanti sono definiti in questa classe per conservare i vari dati necessari per il rendering.Constant buffers are defined in this class to hold various data needed for rendering.
    • Usare più buffer costanti con frequenze diverse per ridurre la quantità di dati che devono essere inviati alla GPU per fotogramma.Use multiple constant buffers with different frequencies to reduce the amount of data that must be sent to the GPU per frame. Questo esempio separa le costanti in buffer diversi in base alla frequenza con cui devono essere aggiornati.This sample separates constants into different buffers based on the frequency that they must be updated. Questa è una procedura consigliata per la programmazione Direct3D.This is a best practice for Direct3D programming.
    • In questo gioco di esempio vengono definiti 4 buffer costanti.In this sample game, 4 constant buffers are defined.
      1. m _ constantBufferNeverChanges contiene i parametri di illuminazione.m_constantBufferNeverChanges contains the lighting parameters. Viene impostato una volta nel metodo FinalizeCreateGameDeviceResources e non viene mai modificato.It's set one time in the FinalizeCreateGameDeviceResources method and never changes again.
      2. m _ constantBufferChangeOnResize contiene la matrice di proiezione.m_constantBufferChangeOnResize contains the projection matrix. che dipende dalle dimensioni e dalle proporzioni della finestra.The projection matrix is dependent on the size and aspect ratio of the window. Viene impostato in CreateWindowSizeDependentResources , quindi viene aggiornato dopo il caricamento delle risorse nel metodo FinalizeCreateGameDeviceResources .It's set in CreateWindowSizeDependentResources and then updated after resources are loaded in the FinalizeCreateGameDeviceResources method. Se il rendering viene eseguito in 3D, viene modificato due volte per fotogramma.If rendering in 3D, it is also changed twice per frame.
      3. m _ constantBufferChangesEveryFrame contiene la matrice di visualizzazione.m_constantBufferChangesEveryFrame contains the view matrix. Questa matrice dipende dalla posizione della fotocamera e dalla direzione dell'aspetto (la normale alla proiezione) e viene modificata una volta per fotogramma nel metodo 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. Questo argomento è stato illustrato in precedenza nel Framework di rendering i: Introduzione al rendering, sotto il metodo GameRenderer:: Render .This was discussed earlier in Rendering framework I: Intro to rendering, under the GameRenderer::Render method.
      4. m _ constantBufferChangesEveryPrim contiene la matrice del modello e le proprietà del materiale di ogni primitiva.m_constantBufferChangesEveryPrim contains the model matrix and material properties of each primitive. La matrice modello converte i vertici dalle coordinate locali alle coordinate globali.The model matrix transforms vertices from local coordinates into world coordinates. Queste costanti sono specifiche delle singole primitive e vengono aggiornate a ogni chiamata a Draw.These constants are specific to each primitive and are updated for every draw call. Questo argomento è stato illustrato in precedenza nel Framework di rendering i: Introduzione al rendering, sotto il rendering primitivo.This was discussed earlier in Rendering framework I: Intro to rendering, under the Primitive rendering.
  • In questa classe sono definiti anche gli oggetti risorsa dello shader che contengono trame per le primitive.Shader resource objects that hold textures for the primitives are also defined in this class.
    • Alcune trame sono predefinite (DDS è un formato di file che può essere usato per archiviare trame compresse e non compresse.Some textures are pre-defined (DDS is a file format that can be used to store compressed and uncompressed textures. Le trame DDS vengono usate per i muri e i piani del mondo, nonché le sfere ammo.DDS textures are used for the walls and floor of the world as well as the ammo spheres.)
    • In questo gioco di esempio gli oggetti risorsa dello shader sono : 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.
  • Gli oggetti shader sono definiti in questa classe per calcolare le primitive e le trame.Shader objects are defined in this class to compute our primitives and textures.
    • In questo gioco di esempio, gli oggetti shader sono m _ vertexshader, __m _ vertexShaderFlat__e m _ PixelShader, m _ pixelShaderFlat.In this sample game, the shader objects are m_vertexShader, m_vertexShaderFlat, and m_pixelShader, m_pixelShaderFlat.
    • Il vertex shader elabora le primitive e l'illuminazione di base, mentre il pixel shader (che a volte viene chiamato fragment shader) elabora le trame e gli eventuali effetti a livello di 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.
    • Esistono due versioni di questi shader (normale e piatto) per il rendering di primitive diverse.There are two versions of these shaders (regular and flat) for rendering different primitives. Il motivo per cui sono disponibili versioni diverse è che le versioni Flat sono molto più semplici e non eseguono evidenziazioni speculari o effetti di illuminazione per 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. Sono usate per le pareti e rendono più rapido il rendering in dispositivi meno potenti.These are used for the walls and make rendering faster on lower powered devices.

GameRenderer.hGameRenderer.h

Esaminiamo ora il codice nell'oggetto classe renderer del gioco di esempio.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;
};

CostruttoreConstructor

Esaminare quindi il costruttore GameRenderer del gioco di esempio e confrontarlo con il costruttore Sample3DSceneRenderer fornito nel modello di app 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();
}

Creare e caricare risorse grafiche DirectXCreate and load DirectX graphic resources

Nel gioco di esempio (e nel modello app DirectX 11 (Windows universale) di Visual Studio), la creazione e il caricamento delle risorse di gioco vengono implementate usando questi due metodi che vengono chiamati dal costruttore 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:

Metodo CreateDeviceDependentResourcesCreateDeviceDependentResources method

Nel modello di app DirectX 11 questo metodo viene usato per caricare i vertici e pixel shader in modo asincrono, creare lo shader e il buffer costante, creare una mesh con vertici che contengono informazioni sulla posizione e sul colore.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.

Nel gioco di esempio, queste operazioni degli oggetti scene sono invece divise tra i metodi CreateGameDeviceResourcesAsync e FinalizeCreateGameDeviceResources .In the sample game, these operations of the scene objects are instead split among the CreateGameDeviceResourcesAsync and FinalizeCreateGameDeviceResources methods.

Per questo gioco di esempio, cosa passa a questo metodo?For this sample game, what goes into this method?

  • È stata creata un'istanza delle variabili (m _ gameResourcesLoaded = false e m _ levelResourcesLoaded = false) che indicano se le risorse sono state caricate prima di procedere con il rendering, dal momento che vengono caricate in modo asincrono.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.
  • Poiché il rendering HUD e overlay si trovano in oggetti classe distinti, chiamare qui i metodi GameHud:: CreateDeviceDependentResources e GameInfoOverlay:: CreateDeviceDependentResources .Since HUD and overlay rendering are in separate class objects, call GameHud::CreateDeviceDependentResources and GameInfoOverlay::CreateDeviceDependentResources methods here.

Ecco il codice per 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();
}

Di seguito è riportato un elenco dei metodi usati per creare e caricare le risorse.Below is a list of the methods that are used to create and load resources.

  • CreateDeviceDependentResourcesCreateDeviceDependentResources
    • CreateGameDeviceResourcesAsync (aggiunto)CreateGameDeviceResourcesAsync (Added)
    • FinalizeCreateGameDeviceResources (aggiunto)FinalizeCreateGameDeviceResources (Added)
  • CreateWindowSizeDependentResourcesCreateWindowSizeDependentResources

Prima di immergersi negli altri metodi usati per creare e caricare le risorse, creare prima il renderer e vedere come si inserisce nel ciclo del gioco.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.

Creare il rendererCreate the renderer

GameRenderer viene creato nel costruttore di GameMain.The GameRenderer is created in the GameMain's constructor. Chiama anche altri due metodi, CreateGameDeviceResourcesAsync e FinalizeCreateGameDeviceResources , che vengono aggiunti per semplificare la creazione e il caricamento delle risorse.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();

    ...
}

Metodo CreateGameDeviceResourcesAsyncCreateGameDeviceResourcesAsync method

CreateGameDeviceResourcesAsync viene chiamato dal metodo del costruttore GameMain nel ciclo di _ attività create poiché le risorse del gioco vengono caricate in modo asincrono.CreateGameDeviceResourcesAsync is called from the GameMain constructor method in the create_task loop since we're loading game resources asynchronously.

CreateDeviceResourcesAsync è un metodo che esegue una serie distinta di attività asincrone per caricare le risorse del gioco.CreateDeviceResourcesAsync is a method that runs as a separate set of async tasks to load the game resources. Poiché è prevista l'esecuzione in un thread separato, può accedere solo ai metodi del dispositivo Direct3D 11 (quelli definiti in ID3D11Device) e non ai metodi di contesto del dispositivo (i metodi definiti in sul ID3D11DeviceContext), quindi non esegue alcun rendering.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.

Il metodo FinalizeCreateGameDeviceResources viene eseguito sul thread principale e ha accesso ai metodi di contesto del dispositivo Direct3D 11.FinalizeCreateGameDeviceResources method runs on the main thread and does have access to the Direct3D 11 device context methods.

In linea di principio:In principle:

  • Usare solo metodi ID3D11Device in CreateGameDeviceResourcesAsync perché sono a thread libero, il che significa che possono essere eseguiti in qualsiasi thread.Use only ID3D11Device methods in CreateGameDeviceResourcesAsync because they are free-threaded, which means that they are able to run on any thread. Si prevede inoltre che non vengano eseguiti sullo stesso thread in cui è stato creato il GameRenderer .It is also expected that they do not run on the same thread as the one GameRenderer was created on.
  • Non usare i metodi in sul ID3D11DeviceContext qui perché devono essere eseguiti in un singolo thread e nello stesso thread di GameRenderer.Do not use methods in ID3D11DeviceContext here because they need to run on a single thread and on the same thread as GameRenderer.
  • Utilizzare questo metodo per creare buffer costanti.Use this method to create constant buffers.
  • Usare questo metodo per caricare le trame (ad esempio i file con estensione DDS) e le informazioni sullo shader (come i file con estensione CSO) negli shader.Use this method to load textures (like the .dds files) and shader info (like the .cso files) into the shaders.

Questo metodo viene usato per:This method is used to:

  • Creare i 4 buffer costanti: m _ constantBufferNeverChanges, m _ constantBufferChangeOnResize, m _ constantBufferChangesEveryFrame, m _ constantBufferChangesEveryPrimCreate the 4 constant buffers: m_constantBufferNeverChanges, m_constantBufferChangeOnResize, m_constantBufferChangesEveryFrame, m_constantBufferChangesEveryPrim
  • Creare un oggetto stato del campionatore che incapsula le informazioni di campionamento per una tramaCreate a sampler-state object that encapsulates sampling information for a texture
  • Creare un gruppo di attività che contenga tutte le attività asincrone create dal metodo.Create a task group that contains all async tasks created by the method. Attende il completamento di tutte queste attività asincrone, quindi chiama FinalizeCreateGameDeviceResources.It waits for the completion of all these async tasks, and then calls FinalizeCreateGameDeviceResources.
  • Creare un caricatore usando il caricatore di base.Create a loader using Basic Loader. Aggiungere le operazioni di caricamento asincrono del caricatore come attività nel gruppo di attività creato in precedenza.Add the loader's async loading operations as tasks into the task group created earlier.
  • I metodi come BasicLoader:: LoadShaderAsync e BasicLoader:: LoadTextureAsync vengono usati per caricare:Methods like BasicLoader::LoadShaderAsync and BasicLoader::LoadTextureAsync are used to load:
    • oggetti shader compilati (VertextShader. CSO, VertexShaderFlat. CSO, PixelShader. cso e PixelShaderFlat. CSO).compiled shader objects (VertextShader.cso, VertexShaderFlat.cso, PixelShader.cso, and PixelShaderFlat.cso). Per altre informazioni, vedere diversi formati di file shader.For more info, go to Various shader file formats.
    • trame specifiche del gioco (assets \ fondo. 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;
    }
}

Metodo FinalizeCreateGameDeviceResourcesFinalizeCreateGameDeviceResources method

Il metodo FinalizeCreateGameDeviceResources viene chiamato dopo il completamento di tutte le attività di caricamento delle risorse presenti nel metodo CreateGameDeviceResourcesAsync .FinalizeCreateGameDeviceResources method is called after all the load resources tasks that are in the CreateGameDeviceResourcesAsync method completes.

  • Inizializzare constantBufferNeverChanges con le posizioni e il colore della luce.Initialize constantBufferNeverChanges with the light positions and color. Carica i dati iniziali nei buffer costanti con una chiamata al metodo del contesto di dispositivo a sul ID3D11DeviceContext:: UpdateSubresource.Loads the initial data into the constant buffers with a device context method call to ID3D11DeviceContext::UpdateSubresource.
  • Dal momento che le risorse caricate in modo asincrono hanno completato il caricamento, è possibile associarle agli oggetti di gioco appropriati.Since asynchronously loaded resources have completed loading, it's time to associate them with the appropriate game objects.
  • Per ogni oggetto gioco, creare la mesh e il materiale usando le trame che sono state caricate.For each game object, create the mesh and the material using the textures that have been loaded. Associare quindi la mesh e il materiale all'oggetto gioco.Then associate the mesh and material to the game object.
  • Per l'oggetto Game targets, la trama composta da anelli colorati concentrici, con un valore numerico nella parte superiore, non viene caricata da un file di trama.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. Viene invece generato in base a procedura tramite il codice in TargetTexture. cpp.Instead, it's procedurally generated using the code in TargetTexture.cpp. La classe TargetTexture crea le risorse necessarie per creare la trama in una risorsa fuori schermata in fase di inizializzazione.The TargetTexture class creates the necessary resources to draw the texture into an off screen resource at initialization time. La trama risultante viene quindi associata agli oggetti del gioco di destinazione appropriati.The resulting texture is then associated with the appropriate target game objects.

FinalizeCreateGameDeviceResources e CreateWindowSizeDependentResources condividono parti simili di codice per le seguenti operazioni:FinalizeCreateGameDeviceResources and CreateWindowSizeDependentResources share similar portions of code for these:

  • Usare SetProjParams per assicurarsi che la fotocamera abbia la matrice di proiezione corretta.Use SetProjParams to ensure that the camera has the right projection matrix. Per altre informazioni, vedere la pagina relativa alla fotocamera e allo spazio delle coordinate.For more info, go to Camera and coordinate space.
  • Gestire la rotazione dello schermo per post moltiplicando la matrice di rotazione 3D con la matrice di proiezione della fotocamera.Handle screen rotation by post multiplying the 3D rotation matrix to the camera's projection matrix. Aggiornare quindi il buffer costante ConstantBufferChangeOnResize con la matrice di proiezione risultante.Then update the ConstantBufferChangeOnResize constant buffer with the resulting projection matrix.
  • Impostare la variabile globale booleana m _ gameResourcesLoaded per indicare che le risorse sono ora caricate nei buffer, pronti per il passaggio successivo.Set the m_gameResourcesLoaded Boolean global variable to indicate that the resources are now loaded in the buffers, ready for the next step. Si ricordi che questa variabile è stata inizializzata come false nel metodo del costruttore di __GameRenderer__tramite il metodo GameRenderer:: CreateDeviceDependentResources .Recall that we first initialized this variable as FALSE in the GameRenderer's constructor method, through the GameRenderer::CreateDeviceDependentResources method.
  • Quando __ _ GameResourcesLoaded__ è true, è possibile eseguire il rendering degli oggetti della scena.When this m_gameResourcesLoaded is TRUE, rendering of the scene objects can take place. Questo è stato trattato nell'articolo relativo al Framework di rendering i: Introduzione a rendering , in metodo 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;
}

Metodo CreateWindowSizeDependentResourceCreateWindowSizeDependentResource method

I metodi CreateWindowSizeDependentResources vengono chiamati ogni volta che le dimensioni della finestra, l'orientamento, il rendering abilitato per lo stereo o la risoluzione cambiano.CreateWindowSizeDependentResources methods are called every time the window size, orientation, stereo-enabled rendering, or resolution changes. Nel gioco di esempio viene aggiornata la matrice di proiezione in ConstantBufferChangeOnResize.In the sample game, it updates the projection matrix in ConstantBufferChangeOnResize.

Le risorse delle dimensioni della finestra vengono aggiornate in questo modo:Window size resources are updated in this manner:

  • Il Framework app ottiene uno dei diversi eventi possibili che indicano una modifica nello stato della finestra.The App framework gets one of several possible events indicating a change in the window state.
  • Il ciclo del gioco principale viene quindi informato sull'evento e chiama CreateWindowSizeDependentResources nell'istanza della classe principale (GameMain), che quindi chiama l'implementazione CreateWindowSizeDependentResources nella classe del renderer del gioco (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.
  • Il compito principale di questo metodo è quello di assicurarsi che gli oggetti visivi non vengano confusi o non siano validi a causa di una modifica nelle proprietà della finestra.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.

Per questo gioco di esempio, una serie di chiamate al metodo è identica a quella del metodo FinalizeCreateGameDeviceResources .For this sample game, a number of method calls are the same as the FinalizeCreateGameDeviceResources method. Per la procedura dettagliata del codice, vedere la sezione precedente.For code walkthrough, go to the previous section.

Le regolazioni di rendering del gioco HUD e sovrapposizione delle dimensioni della finestra sono descritte in aggiungere un'interfaccia utente.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
            );
    }
}

Passaggi successiviNext steps

Questo è il processo di base per l'implementazione del Framework di rendering della grafica di un gioco.This is the basic process for implementing the graphics rendering framework of a game. Maggiore è il gioco, più astrazioni è necessario mettere in atto per gestire gerarchie di tipi di oggetti e comportamenti di animazione.The larger your game, the more abstractions you would have to put in place to handle hierarchies of object types and animation behaviors. È necessario implementare metodi più complessi per il caricamento e la gestione di asset come mesh e trame.You need to implement more complex methods for loading and managing assets such as meshes and textures. Verrà ora illustrato come aggiungere un'interfaccia utente.Next, let's learn how to add a user interface.