Estrutura de renderização II: introdução ao jogoRendering framework II: Game rendering

Observação

Este tópico faz parte do jogo criar um simples plataforma universal do Windows (UWP) com a série de tutoriais do DirectX .This topic is part of the Create a simple Universal Windows Platform (UWP) game with DirectX tutorial series. O tópico nesse link define o contexto para a série.The topic at that link sets the context for the series.

Em Estrutura de renderização I, falamos sobre como podemos capturar as informações da cena e apresentá-las na tela.In Rendering framework I, we've covered how we take the scene info and present it to the display screen. Agora, vamos voltar um pouco e aprender a preparar os dados para renderização.Now, we'll take a step back and learn how to prepare the data for rendering.

Observação

Se você não tiver baixado o código de jogo mais recente para este exemplo, vá para jogo de exemplo do Direct3D.If you haven't downloaded the latest game code for this sample, go to Direct3D sample game. Este exemplo faz parte de uma grande coleção de exemplos de recursos UWP.This sample is part of a large collection of UWP feature samples. Para obter instruções sobre como baixar o exemplo, consulte Obtenha os exemplos da Plataforma Universal do Windows (UWP) do GitHub.For instructions on how to download the sample, see Get the UWP samples from GitHub.

ObjetivoObjective

Recapitulação rápida do objetivo.Quick recap on the objective. Entender como configurar uma estrutura de renderização básica para exibir a saída gráfica de um jogo UWP em DirectX.It is to understand how to set up a basic rendering framework to display the graphics output for a UWP DirectX game. Podemos agrupá-las facilmente nestas três etapas.We can loosely group them into these three steps.

  1. Estabelecer uma conexão com a interface gráficaEstablish a connection to our graphics interface
  2. Preparação: criar os recursos necessários para desenhar os elementos gráficosPreparation: Create the resources we need to draw the graphics
  3. Exibir os elementos gráficos: renderizar o quadroDisplay the graphics: Render the frame

A Estrutura de renderização I: introdução à renderização descreve como os elementos gráficos são renderizados, abordagem nas etapas 1 e 3.Rendering framework I: Intro to rendering explained how graphics are rendered, covering Steps 1 and 3.

Este artigo explica como configurar as outras partes dessa estrutura e preparar os dados necessários para que a renderização possa ser feita, que é a etapa 2 do 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.

Projetar o renderizadorDesign the renderer

O renderizador é responsável por criar e manter todos os objetos D3D11 e D2D usados para gerar os elementos visuais do jogo.The renderer is responsible for creating and maintaining all the D3D11 and D2D objects used to generate the game visuals. A classe GameRenderer é o renderizador deste jogo de exemplo e é projetada para atender às necessidades de renderização do jogo.The GameRenderer class is the renderer for this sample game and is designed to meet the game's rendering needs.

Estes são alguns conceitos que você pode usar projetar o renderizador do seu jogo:These are some concepts you can use to help design the renderer for your game:

  • Como as APIs do Direct3D 11 são definidas como APIS COM, você deve fornecer referências ComPtr aos objetos definidos por essas APIs.Because Direct3D 11 APIs are defined as COM APIs, you must provide ComPtr references to the objects defined by these APIs. Esses objetos são liberados automaticamente quando sua última referência fica fora do escopo após o encerramento do app.These objects are automatically freed when their last reference goes out of scope when the app terminates. Para obter mais informações, consulte ComPtr.For more information, see ComPtr. Exemplo desses objetos: buffers de constantes, objetos sombreadores - sombreador de vértice, sombreador de pixel e objetos de recurso de sombreador.Example of these objects: constant buffers, shader objects - vertex shader, pixel shader, and shader resource objects.
  • Os buffers de constantes são definidos nessa classe para armazenar vários dados necessários à renderização.Constant buffers are defined in this class to hold various data needed for rendering.
    • Use vários buffers de constantes com diferentes frequências para reduzir a quantidade de dados que deve ser enviada à GPU por quadro.Use multiple constant buffers with different frequencies to reduce the amount of data that must be sent to the GPU per frame. Este exemplo separa as constantes em diferentes buffers com base na frequência em que eles devem ser atualizados.This sample separates constants into different buffers based on the frequency that they must be updated. Esta é a prática recomendada para programação do Direct3D.This is a best practice for Direct3D programming.
    • Neste jogo de exemplo, 4 buffers de constantes são definidos.In this sample game, 4 constant buffers are defined.
      1. m _ constantBufferNeverChanges contém os parâmetros de iluminação.m_constantBufferNeverChanges contains the lighting parameters. Ele é definido uma vez no método FinalizeCreateGameDeviceResources e nunca mais é alterado.It's set one time in the FinalizeCreateGameDeviceResources method and never changes again.
      2. m _ constantBufferChangeOnResize contém a matriz de projeção.m_constantBufferChangeOnResize contains the projection matrix. A matriz de projeção depende do tamanho e da taxa de proporção da janela.The projection matrix is dependent on the size and aspect ratio of the window. Ele é definido em CreateWindowSizeDependentResources e, em seguida, atualizado depois que os recursos são carregados no método FinalizeCreateGameDeviceResources.It's set in CreateWindowSizeDependentResources and then updated after resources are loaded in the FinalizeCreateGameDeviceResources method. Se a renderização for em 3D, ele também será alterado duas vezes por quadro.If rendering in 3D, it is also changed twice per frame.
      3. m _ constantBufferChangesEveryFrame contém a matriz de exibição.m_constantBufferChangesEveryFrame contains the view matrix. Essa matriz depende da posição da câmera e da direção do olhar (o normal para a projeção) e muda somente uma vez por quadro no método 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. Isso foi abordado anteriormente em Estrutura de renderização I: introdução à renderização, no método GameRenderer::Render method.This was discussed earlier in Rendering framework I: Intro to rendering, under the GameRenderer::Render method.
      4. m _ constantBufferChangesEveryPrim contém a matriz do modelo e as propriedades de material de cada primitivo.m_constantBufferChangesEveryPrim contains the model matrix and material properties of each primitive. A matriz do modelo transforma os vértices das coordenadas locais em coordenadas mundiais.The model matrix transforms vertices from local coordinates into world coordinates. Essas constantes são específicas de cada primitiva e atualizadas para cada chamada de desenho.These constants are specific to each primitive and are updated for every draw call. Isso foi abordado anteriormente em Estrutura de renderização I: introdução à renderização, em Primitive rendering.This was discussed earlier in Rendering framework I: Intro to rendering, under the Primitive rendering.
  • Os objetos de recurso de sombreador que contêm as texturas das primitivas também são definidos nesta classe.Shader resource objects that hold textures for the primitives are also defined in this class.
    • Alguns texturas são predefinidas. (O DDS é um formato de arquivo que pode ser usado para armazenar texturas compactadas e descompactadas.Some textures are pre-defined (DDS is a file format that can be used to store compressed and uncompressed textures. As texturas DDS são usadas para as paredes e o piso do mundo, bem como para as esferas de munição.)DDS textures are used for the walls and floor of the world as well as the ammo spheres.)
    • Neste jogo de exemplo, os objetos de recurso do sombreador são: 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.
  • Os objetos de sombreador são definidos nesta classe para calcular nossas primitivas e texturas.Shader objects are defined in this class to compute our primitives and textures.
    • Neste jogo de exemplo, os objetos do sombreador são 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.
    • O sombreador de vértice processa as primitivas e a iluminação básica, e o sombreador de pixel (às vezes, chamou um sombreador de fragmento) processa as texturas e quaisquer efeitos por 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.
    • Existem duas versões desses sombreadores (regulares e planos) para a renderização de diferentes primitivas.There are two versions of these shaders (regular and flat) for rendering different primitives. O motivo pelo qual temos diferentes versões é que as versões planas são muito mais simples e não fazem realces especulares por efeitos de iluminação de 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. Elas são usadas nas paredes e tornam a renderização mais rápida em dispositivos com pouco consumo de energia.These are used for the walls and make rendering faster on lower powered devices.

GameRenderer.hGameRenderer.h

Agora, vamos examinar o código no objeto de classe do renderizador do jogo de exemplo.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;
};

ConstrutorConstructor

Em seguida, vamos examinar o construtor GameRenderer do jogo de exemplo e compará-lo com o construtor Sample3DSceneRenderer fornecido no modelo de aplicativo 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();
}

Criar e carregar recursos gráficos DirectXCreate and load DirectX graphic resources

No jogo de exemplo (e no modelo de aplicativo DirectX 11 do Visual Studio (universal do Windows) ), a criação e o carregamento de recursos do jogo são implementados usando esses dois métodos que são chamados do construtor 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étodo CreateDeviceDependentResourcesCreateDeviceDependentResources method

No modelo do app DirectX 11, este método é usado para carregar o sombreador de vértice e de pixel de forma assíncrona, criar o buffer de constantes e de sombreador, criar uma malha com vértices que contêm informações de posição e de cor.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.

No jogo de exemplo, essas operações dos objetos de cena são divididas entre os métodos CreateGameDeviceResourcesAsync e FinalizeCreateGameDeviceResources.In the sample game, these operations of the scene objects are instead split among the CreateGameDeviceResourcesAsync and FinalizeCreateGameDeviceResources methods.

Para este jogo de exemplo, o que entra nesse método?For this sample game, what goes into this method?

  • Variáveis instanciadas (m _ gameResourcesLoaded = false e m _ levelResourcesLoaded = false) que indicam se os recursos foram carregados antes de avançar para render, já que estamos carregando-os de forma assíncrona.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.
  • Como a HUD e a renderização de sobreposição estão em objetos de classe separados, chame os métodos GameHud::CreateDeviceDependentResources and GameInfoOverlay::CreateDeviceDependentResources aqui.Since HUD and overlay rendering are in separate class objects, call GameHud::CreateDeviceDependentResources and GameInfoOverlay::CreateDeviceDependentResources methods here.

Este é o código 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();
}

Abaixo está uma lista dos métodos que são usados para criar e carregar recursos.Below is a list of the methods that are used to create and load resources.

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

Antes de aprofundarmos nos outros métodos usados para criar e carregar recursos, vamos primeiro criar o renderizador e observar como ele se encaixa no loop do jogo.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.

Criar o renderizadorCreate the renderer

O GameRenderer é criado no construtor do GameMain.The GameRenderer is created in the GameMain's constructor. Ele também chama os dois outros métodos, CreateGameDeviceResourcesAsync e FinalizeCreateGameDeviceResources, que são adicionados para criar e carregar os recursos.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étodo CreateGameDeviceResourcesAsyncCreateGameDeviceResourcesAsync method

CreateGameDeviceResourcesAsync é chamado a partir do método de construtor GameMain no loop de criação de _ tarefa , já que estamos carregando os recursos do jogo de forma assíncrona.CreateGameDeviceResourcesAsync is called from the GameMain constructor method in the create_task loop since we're loading game resources asynchronously.

CreateDeviceResourcesAsync é um método executado como um conjunto separado de tarefas assíncronas para carregar os recursos do jogo.CreateDeviceResourcesAsync is a method that runs as a separate set of async tasks to load the game resources. Como é esperado que ele seja executado em um thread separado, ele só tem acesso aos métodos de dispositivo Direct3D 11 (os definidos no ID3D11Device), e não aos métodos de contexto de dispositivo (os métodos definidos no ID3D11DeviceContext); sendo assim, ele não faz nenhuma renderização.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.

O método FinalizeCreateGameDeviceResources é executado no thread principal e não tem acesso aos métodos de contexto de dispositivo Direct3D 11.FinalizeCreateGameDeviceResources method runs on the main thread and does have access to the Direct3D 11 device context methods.

Em princípio:In principle:

  • Use apenas os métodos ID3D11Device de CreateGameDeviceResourcesAsync porque eles não têm threads, o que significa que eles podem ser executados em qualquer thread.Use only ID3D11Device methods in CreateGameDeviceResourcesAsync because they are free-threaded, which means that they are able to run on any thread. Também é esperado que eles não sejam executados no mesmo thread em que o GameRenderer foi criado.It is also expected that they do not run on the same thread as the one GameRenderer was created on.
  • Não use métodos do ID3D11DeviceContext aqui porque eles precisam ser executados em um único thread e no mesmo thread de GameRenderer.Do not use methods in ID3D11DeviceContext here because they need to run on a single thread and on the same thread as GameRenderer.
  • Use este método para criar buffers de constantes.Use this method to create constant buffers.
  • Use este método para carregar texturas (como os arquivos .dds) e informações de sombreador (como os arquivos .cso) nos sombreadores.Use this method to load textures (like the .dds files) and shader info (like the .cso files) into the shaders.

Este método é usado para:This method is used to:

  • Crie os 4 buffers constantes: m _ constantBufferNeverChanges, m _ constantBufferChangeOnResize, m _ constantBufferChangesEveryFrame, m _ constantBufferChangesEveryPrimCreate the 4 constant buffers: m_constantBufferNeverChanges, m_constantBufferChangeOnResize, m_constantBufferChangesEveryFrame, m_constantBufferChangesEveryPrim
  • Criar um objeto de estado de amostra que encapsula informações de amostragem de uma texturaCreate a sampler-state object that encapsulates sampling information for a texture
  • Crie um grupo de tarefas que contenha todas as tarefas assíncronas criadas pelo método.Create a task group that contains all async tasks created by the method. Ele aguarda a conclusão de todas essas tarefas assíncronas e chama FinalizeCreateGameDeviceResources.It waits for the completion of all these async tasks, and then calls FinalizeCreateGameDeviceResources.
  • Crie um carregador usando Carregador básico.Create a loader using Basic Loader. Adicione as operações de carregamento assíncrono do carregador como tarefas ao grupo de tarefas criado anteriormente.Add the loader's async loading operations as tasks into the task group created earlier.
  • Métodos como BasicLoader::LoadShaderAsync e BasicLoader::LoadTextureAsync são usados para carregar:Methods like BasicLoader::LoadShaderAsync and BasicLoader::LoadTextureAsync are used to load:
    • objetos de sombreador compilados (VertextShader.cso, VertexShaderFlat.cso, PixelShader.cso e PixelShaderFlat.cso).compiled shader objects (VertextShader.cso, VertexShaderFlat.cso, PixelShader.cso, and PixelShaderFlat.cso). Para obter mais informações, acesse Vários formatos de arquivo de sombreador.For more info, go to Various shader file formats.
    • texturas específicas do jogo (metal_texture assets 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étodo FinalizeCreateGameDeviceResourcesFinalizeCreateGameDeviceResources method

O método FinalizeCreateGameDeviceResources é chamado depois que todas as tarefas de carregamento de recursos do método CreateGameDeviceResourcesAsync são concluídas.FinalizeCreateGameDeviceResources method is called after all the load resources tasks that are in the CreateGameDeviceResourcesAsync method completes.

  • Inicialize constantBufferNeverChanges com as posições de luz e cor.Initialize constantBufferNeverChanges with the light positions and color. Carrega os dados iniciais nos buffers de constantes com uma chamada de método de contexto de dispositivo para ID3D11DeviceContext::UpdateSubresource.Loads the initial data into the constant buffers with a device context method call to ID3D11DeviceContext::UpdateSubresource.
  • Assim que os recursos carregados de forma assíncrona terminam o carregamento, é possível associá-los aos objetos de jogo apropriados.Since asynchronously loaded resources have completed loading, it's time to associate them with the appropriate game objects.
  • Para cada objeto de jogo, crie a malha e o material usando as texturas que foram carregadas.For each game object, create the mesh and the material using the textures that have been loaded. Em seguida, associe a malha e o material ao objeto de jogo.Then associate the mesh and material to the game object.
  • Para o objeto de jogo de destino, a textura composta por anéis concêntricos coloridos, com um valor numérico na parte superior, não é carregada a partir de um arquivo de textura.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. Em vez disso, ele é gerado por procedimento através do código em TargetTexture.cpp.Instead, it's procedurally generated using the code in TargetTexture.cpp. A classe TargetTexture cria os recursos necessários para desenhar a textura em um recurso de tela desativada no momento da inicialização.The TargetTexture class creates the necessary resources to draw the texture into an off screen resource at initialization time. A textura resultante é, então, associada aos objetos de jogo de destino apropriados.The resulting texture is then associated with the appropriate target game objects.

FinalizeCreateGameDeviceResources e CreateWindowSizeDependentResources compartilham partes similares do código para isso:FinalizeCreateGameDeviceResources and CreateWindowSizeDependentResources share similar portions of code for these:

  • Use SetProjParams para garantir que a câmera esteja com a matriz de projeção correta.Use SetProjParams to ensure that the camera has the right projection matrix. Para obter mais informações, acesse Câmera e espaço de coordenadas.For more info, go to Camera and coordinate space.
  • Manipule a rotação da tela pós-multiplicando a matriz de rotação 3D na matriz de projeção da câmera.Handle screen rotation by post multiplying the 3D rotation matrix to the camera's projection matrix. Em seguida, atualize o buffer de constantes ConstantBufferChangeOnResize com a matriz de projeção resultante.Then update the ConstantBufferChangeOnResize constant buffer with the resulting projection matrix.
  • Defina a variável global booleana do m _ gameResourcesLoaded para indicar que os recursos agora estão carregados nos buffers, prontos para a próxima etapa.Set the m_gameResourcesLoaded Boolean global variable to indicate that the resources are now loaded in the buffers, ready for the next step. Lembre-se de que inicializamos primeiro essa variável como FALSE no método de construtor do GameRenderer, por meio do método GameRenderer::CreateDeviceDependentResources.Recall that we first initialized this variable as FALSE in the GameRenderer's constructor method, through the GameRenderer::CreateDeviceDependentResources method.
  • Quando esse m _ GameResourcesLoaded é verdadeiro, a renderização dos objetos de cena pode ocorrer.When this m_gameResourcesLoaded is TRUE, rendering of the scene objects can take place. Isso foi abordado no artigo Estrutura de renderização I: introdução à renderização, no método 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étodo CreateWindowSizeDependentResourceCreateWindowSizeDependentResource method

Os métodos CreateWindowSizeDependentResources são chamados sempre que o tamanho da janela, a orientação, a renderização habilitada para estéreo ou a resolução é alterada.CreateWindowSizeDependentResources methods are called every time the window size, orientation, stereo-enabled rendering, or resolution changes. No jogo de exemplo, ele atualiza a matriz de projeção em ConstantBufferChangeOnResize.In the sample game, it updates the projection matrix in ConstantBufferChangeOnResize.

Os recursos de tamanho de janela são atualizados desta maneira:Window size resources are updated in this manner:

  • A estrutura do app recebe um dos vários eventos possíveis, indicando uma alteração no estado da janela.The App framework gets one of several possible events indicating a change in the window state.
  • O loop principal do jogo é, então, informado sobre o evento e chama CreateWindowSizeDependentResources na instância da classe principal (GameMain), que, por sua vez, chama a implementação CreateWindowSizeDependentResources na classe do renderizador do jogo (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.
  • O principal trabalho desse método é verificar se os objetos visuais não ficaram confusos ou se tornaram inválidos devido a uma alteração nas propriedades da janela.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.

Para este jogo de exemplo, várias chamadas de método são as mesmas que o método FinalizeCreateGameDeviceResources .For this sample game, a number of method calls are the same as the FinalizeCreateGameDeviceResources method. Para obter instruções passo a passo sobre o código, vá para a seção anterior.For code walkthrough, go to the previous section.

Os ajustes de renderização de tamanho da HUD e da janela de sobreposição do jogo são abordados em Adicionar uma interface do usuário.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
            );
    }
}

Próximas etapasNext steps

Esse é o processo básico de implementação da estrutura de renderização de elementos gráficos de um jogo.This is the basic process for implementing the graphics rendering framework of a game. Quanto maior o jogo, mais abstrações precisarão ser empregadas para lidar com as hierarquias de tipos de objeto e comportamentos de animação.The larger your game, the more abstractions you would have to put in place to handle hierarchies of object types and animation behaviors. Você precisa implementar métodos mais complexos para carregar e gerenciar ativos, como malhas e texturas.You need to implement more complex methods for loading and managing assets such as meshes and textures. Em seguida, vamos aprender a adicionar uma interface do usuário.Next, let's learn how to add a user interface.