Plataforma de representación II: representación de juegoRendering framework II: Game rendering

Nota

Este tema forma parte de la serie de tutoriales de creación de una plataforma universal de Windows sencilla (UWP) con DirectX .This topic is part of the Create a simple Universal Windows Platform (UWP) game with DirectX tutorial series. El tema de ese vínculo establece el contexto de la serie.The topic at that link sets the context for the series.

En el marco de representación I, hemos explicado cómo se toma la información de la escena y se presenta en la pantalla de presentación.In Rendering framework I, we've covered how we take the scene info and present it to the display screen. Ahora, tomaremos un paso atrás y aprenderemos a preparar los datos para la representación.Now, we'll take a step back and learn how to prepare the data for rendering.

Nota

Si no ha descargado el código de juego más reciente para este ejemplo, vaya a juego de ejemplo de Direct3D.If you haven't downloaded the latest game code for this sample, go to Direct3D sample game. Este ejemplo forma parte de una gran colección de ejemplos de características de UWP.This sample is part of a large collection of UWP feature samples. Para obtener instrucciones sobre cómo descargar el ejemplo, consulte obtener los ejemplos de UWP en github.For instructions on how to download the sample, see Get the UWP samples from GitHub.

ObjetivoObjective

Resumen rápido sobre el objetivo.Quick recap on the objective. Es mejor entender cómo configurar un marco de representación básico para mostrar la salida de gráficos para un juego DirectX de UWP.It is to understand how to set up a basic rendering framework to display the graphics output for a UWP DirectX game. Podemos agruparlos de forma flexible en estos tres pasos.We can loosely group them into these three steps.

  1. Establecer una conexión con nuestra interfaz de gráficosEstablish a connection to our graphics interface
  2. Preparación: cree los recursos que necesitamos para dibujar los gráficos.Preparation: Create the resources we need to draw the graphics
  3. Mostrar los gráficos: representar el marcoDisplay the graphics: Render the frame

Marco de representación I: Introducción a la representación explicando cómo se representan los gráficos, que abarcan los pasos 1 y 3.Rendering framework I: Intro to rendering explained how graphics are rendered, covering Steps 1 and 3.

En este artículo se explica cómo configurar otras partes de este marco y preparar los datos necesarios antes de que se produzca la representación, que es el paso 2 del proceso.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.

Diseño del representadorDesign the renderer

El representador es responsable de la creación y el mantenimiento de todos los objetos D3D11 y D2D que se usan para generar los objetos visuales de juego.The renderer is responsible for creating and maintaining all the D3D11 and D2D objects used to generate the game visuals. La clase GameRenderer es el representador de este juego de ejemplo y está diseñado para satisfacer las necesidades de representación del juego.The GameRenderer class is the renderer for this sample game and is designed to meet the game's rendering needs.

Estos son algunos conceptos que puede usar para diseñar el representador del juego:These are some concepts you can use to help design the renderer for your game:

  • Dado que las API de Direct3D 11 se definen como API de com , debe proporcionar referencias de ComPtr a los objetos definidos por estas API.Because Direct3D 11 APIs are defined as COM APIs, you must provide ComPtr references to the objects defined by these APIs. Estos objetos se liberan automáticamente cuando su última referencia sale del alcance cuando la aplicación finaliza.These objects are automatically freed when their last reference goes out of scope when the app terminates. Para obtener más información, vea ComPtr.For more information, see ComPtr. Ejemplo de estos objetos: búferes de constantes, objetos de sombreador: sombreador de vértices, sombreador de píxelesy objetos de recursos de sombreador.Example of these objects: constant buffers, shader objects - vertex shader, pixel shader, and shader resource objects.
  • Los búferes de constantes se definen en esta clase para contener varios datos necesarios para la representación.Constant buffers are defined in this class to hold various data needed for rendering.
    • Use varios búferes de constantes con distintas frecuencias para reducir la cantidad de datos que se deben enviar a la GPU por fotograma.Use multiple constant buffers with different frequencies to reduce the amount of data that must be sent to the GPU per frame. En este ejemplo se separan las constantes en búferes diferentes en función de la frecuencia con la que deben actualizarse.This sample separates constants into different buffers based on the frequency that they must be updated. Este es el procedimiento recomendado para la programación de Direct3D.This is a best practice for Direct3D programming.
    • En este juego de ejemplo, se definen 4 búferes de constantes.In this sample game, 4 constant buffers are defined.
      1. m _ constantBufferNeverChanges contiene los parámetros de iluminación.m_constantBufferNeverChanges contains the lighting parameters. Se establece una vez en el método FinalizeCreateGameDeviceResources y nunca vuelve a cambiar.It's set one time in the FinalizeCreateGameDeviceResources method and never changes again.
      2. m _ constantBufferChangeOnResize contiene la matriz de proyección.m_constantBufferChangeOnResize contains the projection matrix. La matriz de proyección depende del tamaño y la relación de aspecto de la ventana.The projection matrix is dependent on the size and aspect ratio of the window. Se establece en CreateWindowSizeDependentResources y luego se actualiza después de que se carguen los recursos en el método FinalizeCreateGameDeviceResources .It's set in CreateWindowSizeDependentResources and then updated after resources are loaded in the FinalizeCreateGameDeviceResources method. Si se representa en 3D, también se cambia dos veces por fotograma.If rendering in 3D, it is also changed twice per frame.
      3. m _ constantBufferChangesEveryFrame contiene la matriz de la vista.m_constantBufferChangesEveryFrame contains the view matrix. Esta matriz depende de la posición de la cámara y la dirección del aspecto (normal a la proyección) y cambia una vez por fotograma en el 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. Esto se analizó anteriormente en el marco de representación I: Introducción a la representación, en el método GameRenderer:: Render .This was discussed earlier in Rendering framework I: Intro to rendering, under the GameRenderer::Render method.
      4. m _ constantBufferChangesEveryPrim contiene la matriz del modelo y las propiedades de material de cada primitiva.m_constantBufferChangesEveryPrim contains the model matrix and material properties of each primitive. La matriz de modelos transforma los vértices desde las coordenadas locales en coordenadas globales.The model matrix transforms vertices from local coordinates into world coordinates. Estas constantes son específicas de cada primitivo y se actualizan para cada llamada de dibujo.These constants are specific to each primitive and are updated for every draw call. Esto se analizó anteriormente en el marco de representación I: Introducción a la representación, en la representación primitiva.This was discussed earlier in Rendering framework I: Intro to rendering, under the Primitive rendering.
  • Los objetos de recursos de sombreador que contienen texturas para los primitivos también se definen en esta clase.Shader resource objects that hold textures for the primitives are also defined in this class.
    • Algunas texturas están predefinidas (DDS es un formato de archivo que se puede usar para almacenar texturas comprimidas y sin comprimir.Some textures are pre-defined (DDS is a file format that can be used to store compressed and uncompressed textures. Las texturas DDS se utilizan para las paredes y el piso del mundo, así como las esferas de munición).DDS textures are used for the walls and floor of the world as well as the ammo spheres.)
    • En este juego de ejemplo, los objetos de recursos del sombreador son: 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.
  • Los objetos de sombreador se definen en esta clase para calcular los primitivos y las texturas.Shader objects are defined in this class to compute our primitives and textures.
    • En este juego de ejemplo, los objetos de sombreador son m _ vertexShader, __m _ vertexShaderFlat__y m _ u, m _ pixelShaderFlat.In this sample game, the shader objects are m_vertexShader, m_vertexShaderFlat, and m_pixelShader, m_pixelShaderFlat.
    • El sombreador de vértices procesa la iluminación básica y los primitivos y el sombreador de píxeles (en ocasiones llamado sombreador de fragmentos), procesa las texturas y cualquier efecto por píxel.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.
    • Existen dos versiones de estos sombreadores (regular y plano) para representar distintos primitivos.There are two versions of these shaders (regular and flat) for rendering different primitives. La razón por la que tenemos versiones diferentes es que las versiones planas son mucho más sencillas y no realizan resaltes especulares ni efectos de iluminación por píxel.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. Se usan en paredes y logran que las representaciones sean más rápidas en dispositivos de baja potencia.These are used for the walls and make rendering faster on lower powered devices.

GameRenderer.hGameRenderer.h

Ahora veamos el código del objeto de la clase de representador del juego de ejemplo.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;
};

ConstructorConstructor

A continuación, vamos a examinar el constructor GameRenderer del juego de ejemplo y compararlo con el constructor Sample3DSceneRenderer proporcionado en la plantilla de la aplicación 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();
}

Crear y cargar recursos gráficos de DirectXCreate and load DirectX graphic resources

En el juego de ejemplo (y en la plantilla de DirectX 11 App (universal Windows) de Visual Studio, la creación y carga de recursos de juegos se implementa mediante estos dos métodos que se llaman desde el constructor 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

En la plantilla de aplicación DirectX 11, este método se usa para cargar el sombreador de vértices y píxeles de forma asincrónica, crear el sombreador y el búfer de constantes, crear una malla con vértices que contengan información de posición y color.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.

En el juego de ejemplo, estas operaciones de los objetos de la escena se dividen entre los métodos CreateGameDeviceResourcesAsync y FinalizeCreateGameDeviceResources .In the sample game, these operations of the scene objects are instead split among the CreateGameDeviceResourcesAsync and FinalizeCreateGameDeviceResources methods.

Para este juego de ejemplo, ¿qué pasa a este método?For this sample game, what goes into this method?

  • Variables con instancias (m _ gameResourcesLoaded = false y m _ levelResourcesLoaded = false) que indican si los recursos se han cargado antes de avanzar a render, ya que se cargan de forma asincrónica.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.
  • Dado que la representación de HUD y superposición está en objetos de clase independientes, llame a los métodos GameHud:: CreateDeviceDependentResources y GameInfoOverlay:: CreateDeviceDependentResources aquí.Since HUD and overlay rendering are in separate class objects, call GameHud::CreateDeviceDependentResources and GameInfoOverlay::CreateDeviceDependentResources methods here.

Este es el 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();
}

A continuación se muestra una lista de los métodos que se usan para crear y cargar recursos.Below is a list of the methods that are used to create and load resources.

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

Antes de profundizar en los otros métodos que se usan para crear y cargar recursos, vamos a crear primero el representador y ver cómo encaja en el bucle del juego.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.

Creación del representadorCreate the renderer

El GameRenderer se crea en el constructor de GameMain.The GameRenderer is created in the GameMain's constructor. También llama a los otros dos métodos, CreateGameDeviceResourcesAsync y FinalizeCreateGameDeviceResources , que se agregan para ayudar a crear y cargar 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

Se llama a CreateGameDeviceResourcesAsync desde el método de constructor GameMain en el bucle de creación de _ tarea , ya que estamos cargando los recursos de juego de forma asincrónica.CreateGameDeviceResourcesAsync is called from the GameMain constructor method in the create_task loop since we're loading game resources asynchronously.

CreateDeviceResourcesAsync es un método que ejecuta un conjunto de tareas asincrónicas distinto para cargar los recursos del juego.CreateDeviceResourcesAsync is a method that runs as a separate set of async tasks to load the game resources. Dado que se espera que se ejecute en un subproceso independiente, solo tiene acceso a los métodos de dispositivo de Direct3D 11 (los definidos en ID3D11Device) y no a los métodos de contexto de dispositivo (los métodos definidos en ID3D11DeviceContext), por lo que no realiza ninguna representación.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.

El método FinalizeCreateGameDeviceResources se ejecuta en el subproceso principal y tiene acceso a los métodos de contexto de dispositivo de Direct3D 11.FinalizeCreateGameDeviceResources method runs on the main thread and does have access to the Direct3D 11 device context methods.

En principio:In principle:

  • Use solo métodos ID3D11Device en CreateGameDeviceResourcesAsync porque son de subprocesos libres, lo que significa que se pueden ejecutar en cualquier subproceso.Use only ID3D11Device methods in CreateGameDeviceResourcesAsync because they are free-threaded, which means that they are able to run on any thread. También se espera que no se ejecuten en el mismo subproceso en el que se creó el GameRenderer .It is also expected that they do not run on the same thread as the one GameRenderer was created on.
  • No use métodos en ID3D11DeviceContext aquí porque deben ejecutarse en un único subproceso y en el mismo subproceso que GameRenderer.Do not use methods in ID3D11DeviceContext here because they need to run on a single thread and on the same thread as GameRenderer.
  • Utilice este método para crear búferes de constantes.Use this method to create constant buffers.
  • Use este método para cargar texturas (como los archivos. DDS) e información del sombreador (como los archivos. CSO) en los sombreadores.Use this method to load textures (like the .dds files) and shader info (like the .cso files) into the shaders.

Este método se usa para:This method is used to:

  • Cree los 4 búferes de constantes: m _ constantBufferNeverChanges, m _ constantBufferChangeOnResize, m _ constantBufferChangesEveryFrame, m _ constantBufferChangesEveryPrimCreate the 4 constant buffers: m_constantBufferNeverChanges, m_constantBufferChangeOnResize, m_constantBufferChangesEveryFrame, m_constantBufferChangesEveryPrim
  • Crear un objeto de estado de muestra que encapsula la información de muestreo de una texturaCreate a sampler-state object that encapsulates sampling information for a texture
  • Cree un grupo de tareas que contenga todas las tareas asincrónicas creadas por el método.Create a task group that contains all async tasks created by the method. Espera a que se completen todas estas tareas asincrónicas y, a continuación, llama a FinalizeCreateGameDeviceResources.It waits for the completion of all these async tasks, and then calls FinalizeCreateGameDeviceResources.
  • Cree un cargador mediante el cargador básico.Create a loader using Basic Loader. Agregue las operaciones de carga asincrónica del cargador como tareas en el grupo de tareas creado anteriormente.Add the loader's async loading operations as tasks into the task group created earlier.
  • Los métodos como basicloader:: LoadShaderAsync y Basicloader:: LoadTextureAsync se usan para cargar:Methods like BasicLoader::LoadShaderAsync and BasicLoader::LoadTextureAsync are used to load:
    • objetos de sombreador compilados (VertextShader. CSO, VertexShaderFlat. CSO, u. CSO y PixelShaderFlat. CSO).compiled shader objects (VertextShader.cso, VertexShaderFlat.cso, PixelShader.cso, and PixelShaderFlat.cso). Para obtener más información, vaya a varios formatos de archivo de sombreador.For more info, go to Various shader file formats.
    • texturas específicas del juego (assets \ Seafloor. DDS, metal_texture. DDS, cellceiling. DDS, cellfloor. DDS, CELLWall. DDS).game specific textures (Assets\seafloor.dds, metal_texture.dds, cellceiling.dds, cellfloor.dds, cellwall.dds).
IAsyncAction GameRenderer::CreateGameDeviceResourcesAsync(_In_ std::shared_ptr<Simple3DGame> game)
{
    auto lifetime = shared_from_this();

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

    auto d3dDevice = m_deviceResources->GetD3DDevice();

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

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

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

    ...

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

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

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

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

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

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

    std::vector<IAsyncAction> tasks;

    uint32_t numElements = ARRAYSIZE(PNTVertexLayout);

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

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

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

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

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

Método FinalizeCreateGameDeviceResourcesFinalizeCreateGameDeviceResources method

El método FinalizeCreateGameDeviceResources se llama después de que se completen todas las tareas de recursos de carga que se encuentran en el método CreateGameDeviceResourcesAsync .FinalizeCreateGameDeviceResources method is called after all the load resources tasks that are in the CreateGameDeviceResourcesAsync method completes.

  • Inicialice constantBufferNeverChanges con las posiciones y el color de la luz.Initialize constantBufferNeverChanges with the light positions and color. Carga los datos iniciales en los búferes de constantes con una llamada del método de contexto de dispositivo a ID3D11DeviceContext:: UpdateSubresource.Loads the initial data into the constant buffers with a device context method call to ID3D11DeviceContext::UpdateSubresource.
  • Dado que los recursos cargados de forma asincrónica han finalizado la carga, es el momento de asociarlos con los objetos de juego adecuados.Since asynchronously loaded resources have completed loading, it's time to associate them with the appropriate game objects.
  • Para cada objeto de juego, cree la malla y el material usando las texturas que se han cargado.For each game object, create the mesh and the material using the textures that have been loaded. A continuación, asocie la malla y el material al objeto de juego.Then associate the mesh and material to the game object.
  • En el caso del objeto Game de destinos, la textura compuesta por anillos de color concéntricos, con un valor numérico en la parte superior, no se carga desde un archivo 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. En su lugar, se genera de procedimientos mediante el código en TargetTexture. cpp.Instead, it's procedurally generated using the code in TargetTexture.cpp. La clase TargetTexture crea los recursos necesarios para dibujar la textura en un recurso fuera de la pantalla en el momento de la inicialización.The TargetTexture class creates the necessary resources to draw the texture into an off screen resource at initialization time. A continuación, la textura resultante se asocia con los objetos de juego de destino adecuados.The resulting texture is then associated with the appropriate target game objects.

FinalizeCreateGameDeviceResources y CreateWindowSizeDependentResources comparten partes similares de código para estos:FinalizeCreateGameDeviceResources and CreateWindowSizeDependentResources share similar portions of code for these:

  • Use SetProjParams para asegurarse de que la cámara tiene la matriz de proyección adecuada.Use SetProjParams to ensure that the camera has the right projection matrix. Para obtener más información, vaya a cámara y espacio de coordenadas.For more info, go to Camera and coordinate space.
  • Controlar la rotación de la pantalla mediante post multiplicando la matriz de rotación 3D a la matriz de proyección de la cámara.Handle screen rotation by post multiplying the 3D rotation matrix to the camera's projection matrix. A continuación, actualice el búfer de constantes ConstantBufferChangeOnResize con la matriz de proyección resultante.Then update the ConstantBufferChangeOnResize constant buffer with the resulting projection matrix.
  • Establezca la variable global de valor booleano m _ gameResourcesLoaded para indicar que los recursos se cargan ahora en los búferes, listos para el siguiente paso.Set the m_gameResourcesLoaded Boolean global variable to indicate that the resources are now loaded in the buffers, ready for the next step. Recuerde que primero inicializamos esta variable como false en el método de constructor de GameRenderer, a través del método GameRenderer:: CreateDeviceDependentResources .Recall that we first initialized this variable as FALSE in the GameRenderer's constructor method, through the GameRenderer::CreateDeviceDependentResources method.
  • Cuando este m _ GameResourcesLoaded es true, se puede producir la representación de los objetos de la escena.When this m_gameResourcesLoaded is TRUE, rendering of the scene objects can take place. Esto se trató en el marco de representación I: Introducción al artículo de representación, en el 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

Se llama a los métodos CreateWindowSizeDependentResources cada vez que cambia el tamaño de la ventana, la orientación, la representación habilitada para estéreo o la resolución.CreateWindowSizeDependentResources methods are called every time the window size, orientation, stereo-enabled rendering, or resolution changes. En el juego de ejemplo, se actualiza la matriz de proyección en ConstantBufferChangeOnResize.In the sample game, it updates the projection matrix in ConstantBufferChangeOnResize.

Los recursos de tamaño de la ventana se actualizan de esta manera:Window size resources are updated in this manner:

  • El marco de trabajo de la aplicación obtiene uno de varios posibles eventos que indican un cambio en el estado de la ventana.The App framework gets one of several possible events indicating a change in the window state.
  • A continuación, el bucle principal del juego se informa del evento y llama a CreateWindowSizeDependentResources en la instancia de la clase principal (GameMain), que luego llama a la implementación de createwindowsizedependentresources en la clase de representador de juegos (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.
  • El trabajo principal de este método es asegurarse de que los objetos visuales no se confunden o no son válidos debido a un cambio en las propiedades de la ventana.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 juego de ejemplo, varias llamadas a métodos son las mismas que las del método FinalizeCreateGameDeviceResources .For this sample game, a number of method calls are the same as the FinalizeCreateGameDeviceResources method. Para ver el tutorial de código, vaya a la sección anterior.For code walkthrough, go to the previous section.

Los ajustes de representación del tamaño de la ventana de la pantalla de visualización y superposición se describen en Agregar una interfaz de usuario.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
            );
    }
}

Pasos siguientesNext steps

Este es el proceso básico para implementar el marco de representación de gráficos de un juego.This is the basic process for implementing the graphics rendering framework of a game. Cuanto más grande sea su juego, más abstracciones tendrá que poner en marcha para controlar las jerarquías de los tipos de objeto y los comportamientos de animación.The larger your game, the more abstractions you would have to put in place to handle hierarchies of object types and animation behaviors. Debe implementar métodos más complejos para cargar y administrar recursos como mallas y texturas.You need to implement more complex methods for loading and managing assets such as meshes and textures. A continuación, vamos a obtener información sobre cómo Agregar una interfaz de usuario.Next, let's learn how to add a user interface.