렌더링 프레임워크 II: 게임 렌더링Rendering framework II: Game rendering

렌더링 프레임워크 I에서 장면 정보를 가지고 표시 화면에 제시하는 방법을 다루었습니다.In Rendering framework I, we've covered how we take the scene info and present it to the display screen. 이제 한 단계 돌아가 렌더링하기 위해 데이터를 준비하는 방법을 알아보겠습니다.Now, we'll take a step back and learn how to prepare the data for rendering.

참고

이 샘플의 최신 게임 코드를 다운로드하지 않은 경우 Direct3D 게임 샘플로 이동합니다.If you haven't downloaded the latest game code for this sample, go to Direct3D game sample. 이 샘플은 UWP 기능 샘플의 큰 컬렉션의 일부입니다.This sample is part of a large collection of UWP feature samples. 샘플을 다운로드하는 방법에 대한 지침은 GitHub에서 UWP 샘플 가져오기를 참조하세요.For instructions on how to download the sample, see Get the UWP samples from GitHub.

목표Objective

목표에 대한 빠른 요약입니다.Quick recap on the objective. UWP DirectX 게임의 그래픽 출력을 표시하도록 기본 렌더링 프레임워크를 설정하는 방법을 이해하기 위해 고안되었습니다.It is to understand how to set up a basic rendering framework to display the graphics output for a UWP DirectX game. 이를 느슨하게 이러한 3단계로 그룹화할 수 있습니다.We can loosely group them into these three steps.

  1. 그래픽 인터페이스에 연결 구축Establish a connection to our graphics interface
  2. 준비: 그래픽 그리는 데 필요한 리소스 만들기Preparation: Create the resources we need to draw the graphics
  3. 그래픽 표시: 프레임 렌더링Display the graphics: Render the frame

렌더링 프레임워크 I: 렌더링 소개에서는 1단계와 3단계에서 다루는 그래픽이 렌더링되는 방식을 설명했습니다.Rendering framework I: Intro to rendering explained how graphics are rendered, covering Steps 1 and 3.

이 문서에서는 이 프레임워크의 다른 부분을 설정하고 렌더링하기 전에 필요한 데이터를 준비하는 프로세스의 2단계를 설명합니다.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.

렌더러 디자인Design the renderer

렌더러는 게임 시각 효과를 생성하는 데 사용되는 모든 D3D11 및 D2D 개체를 만들고 관리합니다.The renderer is responsible for creating and maintaining all the D3D11 and D2D objects used to generate the game visuals. GameRenderer 클래스는 이 샘플 게임의 렌더러이며 게임의 렌더링 요구를 충족하도록 설계되었습니다.The GameRenderer class is the renderer for this sample game and is designed to meet the game's rendering needs.

다음은 게임에 대한 렌더러를 디자인하는 데 도움이 되는 몇 가지 개념입니다.These are some concepts you can use to help design the renderer for your game:

  • Direct3D 11 API는 COM API로 정의되므로 이러한 API가 정의하는 개체에 대한 ComPtr 참조를 제공해야 합니다.Because Direct3D 11 APIs are defined as COM APIs, you must provide ComPtr references to the objects defined by these APIs. 앱이 종료될 때 마지막 참조가 범위를 벗어나면 이러한 개체가 자동으로 해제됩니다.These objects are automatically freed when their last reference goes out of scope when the app terminates. 자세한 내용은 ComPtr를 참조하세요.For more information, see ComPtr. 이러한 개체의 예로는 상수 버퍼, 셰이더 개체- 꼭짓점 셰이더, 픽셀 셰이더, 셰이더 리소스 개체가 있습니다.Example of these objects: constant buffers, shader objects - vertex shader, pixel shader, and shader resource objects.
  • 렌더링을 위해 필요한 다양한 데이터를 저장하기 위해 상수 버퍼가 이 클래스에서 정의됩니다.Constant buffers are defined in this class to hold various data needed for rendering.
    • 각각 빈도가 다른 여러 상수 버퍼를 사용하면 프레임당 GPU로 전송해야 하는 데이터 양이 감소합니다.Use multiple constant buffers with different frequencies to reduce the amount of data that must be sent to the GPU per frame. 이 샘플에서는 업데이트해야 하는 빈도를 기준으로 상수를 각기 다른 버퍼로 구분합니다.This sample separates constants into different buffers based on the frequency that they must be updated. 이 방법은 Direct3D 프로그래밍의 모범 사례입니다.This is a best practice for Direct3D programming.
    • 이 게임 샘플에서 4 상수 버퍼가 정의됩니다.In this game sample, 4 constant buffers are defined.
      1. m_constantBufferNeverChanges 는 조명 매개 변수를 포함 합니다.m_constantBufferNeverChanges contains the lighting parameters. 이는 FinalizeCreateGameDeviceResources 메서드에서 한 번 설정되고 다시 변경되지 않습니다.It's set one time in the FinalizeCreateGameDeviceResources method and never changes again.
      2. m_constantBufferChangeOnResize 는 프로젝션 매트릭스를 포함 합니다.m_constantBufferChangeOnResize contains the projection matrix. 투영 행렬은 창의 크기와 가로 세로 비율에 따라 달라집니다.The projection matrix is dependent on the size and aspect ratio of the window. 이는 CreateWindowSizeDependentResources에서 설정된 다음 리소스가 FinalizeCreateGameDeviceResources 메서드에 로드된 후 업데이트됩니다.It's set in CreateWindowSizeDependentResources and then updated after resources are loaded in the FinalizeCreateGameDeviceResources method. 3D로 렌더링하면 이 또한 프레임별로 두 번 변경됩니다.If rendering in 3D, it is also changed twice per frame.
      3. m_constantBufferChangesEveryFrame 는 뷰 매트릭스를 포함 합니다.m_constantBufferChangesEveryFrame contains the view matrix. 이 행렬은 카메라 위치와 보기 방향(프로젝션에 수직)에 따라 달라지며 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. 이는 이전에 GameRenderer::Render 메서드 아래의 __렌더링 프레임워크 I: 렌더링 소개__에서 다루었습니다.This was discussed earlier in Rendering framework I: Intro to rendering, under the GameRenderer::Render method.
      4. m_constantBufferChangesEveryPrim 에는 각 기본 형식의 모델 행렬과 재질 속성이 포함 되어 있습니다.m_constantBufferChangesEveryPrim contains the model matrix and material properties of each primitive. 모델 행렬은 로컬 좌표에서 월드 좌표로 꼭짓점을 변환합니다.The model matrix transforms vertices from local coordinates into world coordinates. 이러한 상수는 각 원형과 관련이 있으며 그리기 호출 시마다 업데이트됩니다.These constants are specific to each primitive and are updated for every draw call. 이는 이전에 원형 렌더링 아래의 __렌더링 프레임워크 I: 렌더링 소개__에서 다루었습니다.This was discussed earlier in Rendering framework I: Intro to rendering, under the Primitive rendering.
  • 이 클래스에서 원형의 텍스처를 포함하는 셰이더 리소스 개체도 정의됩니다.Shader resource objects that hold textures for the primitives are also defined in this class.
    • 일부 텍스처는 미리 정의됩니다.(DDS는 압축되거나 압축되지 않은 텍스처를 저장하기 위해 사용할 수 있는 파일 형식입니다.Some textures are pre-defined (DDS is a file format that can be used to store compressed and uncompressed textures. DDS 텍스처는 월드의 벽과 마루, 탄약 구형에 사용됩니다.DDS textures are used for the walls and floor of the world as well as the ammo spheres.)
    • 이 게임 샘플에서 셰이더 리소스 개체는 m_sphereTexture, m_cylinderTexture, m_ceilingTexture, m_floorTexture, __m_wallsTexture__입니다.In this game sample, shader resource objects are: m_sphereTexture, m_cylinderTexture, m_ceilingTexture, m_floorTexture, m_wallsTexture.
  • 이 클래스의 셰이더 개체가 원형 및 텍스처를 계산하기 위해 정의됩니다.Shader objects are defined in this class to compute our primitives and textures.
    • 이 게임 샘플에서 셰이더 개체는 m_vertexShader, __m_vertexShaderFlat__및 m_shadereffect, __m_pixelShaderFlat__입니다.In this game sample, the shader objects are m_vertexShader, m_vertexShaderFlat, and m_pixelShader, m_pixelShaderFlat.
    • 꼭짓점 셰이더는 원형과 기본 조명을 처리하고, 픽셀 셰이더(조각 셰이더라고 함)는 텍스처와 픽셀당 효과를 처리합니다.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.
    • 이러한 셰이더는 다른 원형을 렌더링하기 위한 두 가지 버전(일반 및 평면)이 있습니다.There are two versions of these shaders (regular and flat) for rendering different primitives. 다른 버전이 있는 이유는 평면 버전이 더 단순하며 반사 하이라이트나 픽셀당 조명 효과가 없기 때문입니다.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. 이 버전은 벽에 사용되며 저전력 디바이스에서 렌더링 속도를 늘립니다.These are used for the walls and make rendering faster on lower powered devices.

GameRenderer.hGameRenderer.h

이제 게임 샘플의 렌더러 클래스 개체를 살펴보고 이를 DirectX 11 앱 템플릿에서 제공되는 __Sample3DSceneRenderer.h__와 비교해 보겠습니다.Now let's look at the code in the game sample's renderer class object and compare it with the Sample3DSceneRenderer.h provided in the DirectX 11 App template.

// Class object handling the rendering of the game
ref class GameRenderer
{
internal:
    GameRenderer(const std::shared_ptr<DX::DeviceResources>& deviceResources);
    
    // Compared with Sample3DSceneRenderer.h in the DirectX 11 App template sample. 
    
    // These methods are present.
    void CreateDeviceDependentResources();
    void CreateWindowSizeDependentResources();
    void ReleaseDeviceDependentResources();
    void Render();

    // Added: Async related methods to prepare 3D game objects for rendering
    concurrency::task<void> CreateGameDeviceResourcesAsync(_In_ Simple3DGame^ game);
    void FinalizeCreateGameDeviceResources();
    concurrency::task<void> LoadLevelResourcesAsync();
    void FinalizeLoadLevelResources();
    // --- end of async related methods section

    // Added: Methods for rendering overlay
    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

    //...
    protected private:
    // Cached pointer to device resources.
    std::shared_ptr<DX::DeviceResources>                m_deviceResources;

    // ...

    // Shader resource objects
    Microsoft::WRL::ComPtr<ID3D11ShaderResourceView>    m_sphereTexture;
    Microsoft::WRL::ComPtr<ID3D11ShaderResourceView>    m_cylinderTexture;
    Microsoft::WRL::ComPtr<ID3D11ShaderResourceView>    m_ceilingTexture;
    Microsoft::WRL::ComPtr<ID3D11ShaderResourceView>    m_floorTexture;
    Microsoft::WRL::ComPtr<ID3D11ShaderResourceView>    m_wallsTexture;

    // Constant Buffers
    Microsoft::WRL::ComPtr<ID3D11Buffer>                m_constantBufferNeverChanges;
    Microsoft::WRL::ComPtr<ID3D11Buffer>                m_constantBufferChangeOnResize;
    Microsoft::WRL::ComPtr<ID3D11Buffer>                m_constantBufferChangesEveryFrame;
    Microsoft::WRL::ComPtr<ID3D11Buffer>                m_constantBufferChangesEveryPrim;

    // Texture sampler
    Microsoft::WRL::ComPtr<ID3D11SamplerState>          m_samplerLinear;

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

생성자Constructor

다음으로 게임 샘플의 GameRenderer 생성자를 살펴보고 이를 DirectX 11 앱 템플릿에서 제공되는 Sample3DSceneRenderer 생성자와 비교해 보겠습니다.Next, let's examine the game sample'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(const std::shared_ptr<DX::DeviceResources>& deviceResources) : //...
{
    // Compared with Sample3DSceneRenderer::Sample3DSceneRenderer in the DirectX 11 App template sample. 
    
    // Added: Create a new GameHud object to rendered text on the top left corner of the screen
    m_gameHud = ref new GameHud(
        deviceResources,
        "Windows platform samples",
        "DirectX first-person game sample"
        );
    //--- end of new GameHud object section
        
    // Added: Game info rendered as an overlay on the top right corner of the screen (eg. Hits, Shots, Time)    
    m_gameInfoOverlay = ref new GameInfoOverlay(deviceResources);
    //--- end of game info rendered as overlay section

    // These methods are present.
    CreateDeviceDependentResources();
    CreateWindowSizeDependentResources();
}

DirectX 그래픽 리소스 만들기 및 로드Create and load DirectX graphic resources

게임 샘플(그리고 Visual Studio의 DirectX 11 앱(유니버설 Windows) 템플릿)에서 게임 리소스 만들기 및 로딩은 GameRenderer 생성자에서 호출되는 이 두 메서드를 사용하여 구현됩니다.In the game sample (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:

CreateDeviceDependentResources 메서드CreateDeviceDependentResources method

DirectX 11 앱 템플릿에서 이 메서드는 꼭짓점 및 픽셀 셰이더를 비동기적으로 로드하고, 셰이더 및 상수 버퍼를 만들고, 위치 및 색 정보를 포함하는 꼭짓점이 있는 메시를 만드는 데 사용됩니다.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.

샘플 게임에서 장면 개체의 이러한 작업은 대신 CreateGameDeviceResourcesAsyncFinalizeCreateGameDeviceResources 메서드 간에 분할됩니다.In the sample game, these operations of the scene objects are instead split among the CreateGameDeviceResourcesAsync and FinalizeCreateGameDeviceResources methods.

이 게임 샘플의 경우 이 메서드에 무엇이 분할됩니까?For this game sample, what goes into this method?

  • 비동기적으로 로드 하는 중 이므로 렌더링으로 이동 하기 전에 리소스를 로드 했는지 여부를 나타내는 인스턴스화된 변수 (m_gameResourcesLoaded = false 및 m_levelresourcesloaded = false)입니다.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.
  • HUD 및 오버레이 렌더링은 별도의 클래스 개체이지 때문에 여기에서 GameHud::CreateDeviceDependentResourcesGameInfoOverlay::CreateDeviceDependentResources 메서드를 호출합니다.Since HUD and overlay rendering are in separate class objects, call GameHud::CreateDeviceDependentResources and GameInfoOverlay::CreateDeviceDependentResources methods here.

다음은 __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 if 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();
}

아래 표에서 리소스를 만들고 로드하는 데 사용되는 메서드를 나열합니다.The table below lists the methods that are used to create and load resources. 리소스를 비동기적으로 로드할 수 있도록 CreateGameDeviceResourcesAsync 및 __FinalizeCreateGameDeviceResources__가 샘플 게임에 추가됩니다.CreateGameDeviceResourcesAsync and FinalizeCreateGameDeviceResources are added in the sample game so that resources are loaded asynchronously.

원본 DirectX 11 앱 템플릿Original DirectX 11 App template 샘플 게임Sample game
CreateDeviceDependentResourcesCreateDeviceDependentResources CreateDeviceDependentResourcesCreateDeviceDependentResources
- CreateGameDeviceResourcesAsync(추가됨)- CreateGameDeviceResourcesAsync (Added)
- FinalizeCreateGameDeviceResources(추가됨)- FinalizeCreateGameDeviceResources (Added)
CreateWindowSizeDependentResourcesCreateWindowSizeDependentResources CreateWindowSizeDependentResourcesCreateWindowSizeDependentResources

리소스를 만들고 로드하는 데 사용하는 다른 메서드에 대해 자세히 알아보기 전에 먼저 렌더러를 만들고 해당 렌더러가 어떻게 게임 루프에 적용되는지 확인해 보겠습니다.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.

렌더러 만들기Create the renderer

__GameRenderer__는 __GameMain__의 생성자에서 생성됩니다.The GameRenderer is created in the GameMain's constructor. 이는 리소스를 만들고 로드하는 것을 돕기 위해 추가되는 두 가지 다른 메서드 CreateGameDeviceResourcesAsyncFinalizeCreateGameDeviceResources를 호출합니다.It also calls the two other methods, CreateGameDeviceResourcesAsync and FinalizeCreateGameDeviceResources that are added to help create and load resources.


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

    // These methods are used in the DirectX 11 App template to create the class objects used for rendering. 
    // But are replaced in this game sample with GameRenderer as shown below.
    // m_sceneRenderer = std::unique_ptr<Sample3DSceneRenderer>(new Sample3DSceneRenderer(m_deviceResources));
    // m_fpsTextRenderer = std::unique_ptr<SampleFpsTextRenderer>(new SampleFpsTextRenderer(m_deviceResources));
    
    // Creation of GameRenderer
    m_renderer = ref new GameRenderer(m_deviceResources);
    
    //...

     create_task([this]()
    {
        // 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);

        return m_renderer->CreateGameDeviceResourcesAsync(m_game);

    }).then([this]()
    {
        // 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.
        m_renderer->FinalizeCreateGameDeviceResources();

        InitializeGameState();
    
    //...
}

CreateGameDeviceResourcesAsync 메서드CreateGameDeviceResourcesAsync method

CreateGameDeviceResourcesAsync 는 게임 리소스를 비동기적으로 로드 하므로 create_작업 루프의 GameMain constructor 메서드에서 호출 됩니다.CreateGameDeviceResourcesAsync is called from the GameMain constructor method in the create_task loop since we're loading game resources asynchronously.

__CreateDeviceResourcesAsync__는 게임 리소스를 로드하기 위해 별도의 비동기 작업 집합으로 실행되는 메서드입니다.CreateDeviceResourcesAsync is a method that runs as a separate set of async tasks to load the game resources. 이 메서드는 별도의 스레드에서 실행되어야 하므로 Direct3D 11 디바이스 메서드(__ID3D11Device__에 정의되어 있음)에만 액세스할 수 있고 디바이스 컨텍스트 메서드(__ID3D11DeviceContext__에 정의된 메서드)에는 액세스할 수 없습니다. 따라서 렌더링을 수행하지 않습니다.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.

FinalizeCreateGameDeviceResources 메서드는 주 스레드에서 실행되고 Direct3D 11 디바이스 컨텍스트 메서드에 액세스할 수 있습니다.FinalizeCreateGameDeviceResources method runs on the main thread and does have access to the Direct3D 11 device context methods.

원칙:In principle:

  • __CreateGameDeviceResourcesAsync__의 ID3D11Device 메서드만 사용합니다. 자유 스레드 방식, 즉 모든 스레드에서 실행할 수 있기 때문입니다.Use only ID3D11Device methods in CreateGameDeviceResourcesAsync because they are free-threaded, which means that they are able to run on any thread. 또한 __GameRenderer__가 생성된 것과 동일한 스레드에서 실행하지 않는 것이 좋습니다.It is also expected that they do not run on the same thread as the one GameRenderer was created on.
  • 이 메서드는 단일 스레드, 그리고 __GameRenderer__와 동일한 스레드에서 실행해야 하므로 여기에서 __ID3D11DeviceContext__에 메서드를 사용하지 마십시오.Do not use methods in ID3D11DeviceContext here because they need to run on a single thread and on the same thread as GameRenderer.
  • 이 메서드를 사용하여 상수 버퍼를 만들 수 있습니다.Use this method to create constant buffers.
  • 이 메서드를 사용하여 텍스처(예: .dds 파일) 및 셰이더 정보(예: .cso 파일)를 셰이더에 로드할 수 있습니다.Use this method to load textures (like the .dds files) and shader info (like the .cso files) into the shaders.

이 메서드는 다음을 위해 사용됩니다.This method is used to:

  • 4 개의 상수 버퍼를 만듭니다 . m_constantBufferNeverChanges, m_constantBufferChangeOnResize, m _constantBufferChangesEveryFrame, m_constantBufferChangesEveryPrimCreate the 4 constant buffers: m_constantBufferNeverChanges, m_constantBufferChangeOnResize, m_constantBufferChangesEveryFrame, m_constantBufferChangesEveryPrim
  • 텍스처에 대한 샘플 정보를 캡슐화하는 sampler-state 개체 만들기Create a sampler-state object that encapsulates sampling information for a texture
  • 메서드에서 만든 모든 비동기 작업이 포함된 작업 그룹을 만듭니다.Create a task group that contains all async tasks created by the method. 이러한 모든 비동기 작업이 완료될 때까지 기다린 다음 __FinalizeCreateGameDeviceResources__를 호출합니다.It waits for the completion of all these async tasks, and then calls FinalizeCreateGameDeviceResources.
  • 기본 로더를 사용하여 로더를 만듭니다.Create a loader using Basic Loader. 로더의 비동기 로딩 작업을 이전에 만든 작업 그룹에 추가합니다.Add the loader's async loading operations as tasks into the task group created earlier.
  • BasicLoader::LoadShaderAsyncBasicLoader::LoadTextureAsync 등의 메서드가 다음을 로드하는 데 사용됩니다.Methods like BasicLoader::LoadShaderAsync and BasicLoader::LoadTextureAsync are used to load:
    • 컴파일된 셰이더 개체(VertextShader.cso, VertexShaderFlat.cso, PixelShader.cso, PixelShaderFlat.cso).compiled shader objects (VertextShader.cso, VertexShaderFlat.cso, PixelShader.cso, and PixelShaderFlat.cso). 자세한 내용은 다양한 셰이더 파일 형식으로 이동합니다.For more info, go to Various shader file formats.
    • game 특정 질감 (자산\seafloor, metal_texture, 셀, 셀)입니다.game specific textures (Assets\seafloor.dds, metal_texture.dds, cellceiling.dds, cellfloor.dds, cellwall.dds).
task<void> GameRenderer::CreateGameDeviceResourcesAsync(_In_ Simple3DGame^ game)
{
    // 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.
    // For API ref, go to: https://msdn.microsoft.com/library/windows/desktop/ff476092.aspx
    D3D11_BUFFER_DESC bd;
    ZeroMemory(&bd, sizeof(bd));
    
    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, go to: https://msdn.microsoft.com/library/windows/desktop/ff476501.aspx
    
    DX::ThrowIfFailed(
        d3dDevice->CreateBuffer(&bd, nullptr, &m_constantBufferNeverChanges) 
        );
    // ...
    
    // Define D3D11_SAMPLER_DESC. For API ref, go to: https://msdn.microsoft.com/library/windows/desktop/ff476207.aspx
    D3D11_SAMPLER_DESC sampDesc;

    // ZeroMemory fills a block of memory with zeros. 
    // For API ref, go to: https://msdn.microsoft.com/library/windows/desktop/aa366920(v=vs.85).aspx
    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.
    DX::ThrowIfFailed(
        d3dDevice->CreateSamplerState(&sampDesc, &m_samplerLinear)
        );

    // Start the async tasks to load the shaders and textures (resources).
    
    // 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, go to: https://docs.microsoft.com/windows/uwp/gaming/complete-code-for-basicloader
    BasicLoader^ loader = ref new BasicLoader(d3dDevice);

    std::vector<task<void>> tasks;

    uint32 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("VertexShader.cso", PNTVertexLayout, numElements, &m_vertexShader, &m_vertexLayout));
    tasks.push_back(loader->LoadShaderAsync("VertexShaderFlat.cso", nullptr, numElements, &m_vertexShaderFlat, nullptr));
    tasks.push_back(loader->LoadShaderAsync("PixelShader.cso", &m_pixelShader));
    tasks.push_back(loader->LoadShaderAsync("PixelShaderFlat.cso", &m_pixelShaderFlat));

    // Make sure the previous versions are set to NULL before any of the textures are loaded.
    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("Assets\\seafloor.dds", nullptr, &m_sphereTexture));
    // ...
    
    tasks.push_back(create_task([]()
    {
        // Simulate loading additional resources by introducing a delay.
        wait(GameConstants::InitialLoadingDelay);
    }));

    // Returns when all the async tasks for loading the shader and texture assets have completed.
    return when_all(tasks.begin(), tasks.end());
}

FinalizeCreateGameDeviceResources 메서드FinalizeCreateGameDeviceResources method

FinalizeCreateGameDeviceResources 메서드는 CreateGameDeviceResourcesAsync 메서드의 모든 리소스 로드 작업이 완료된 후에 호출됩니다.FinalizeCreateGameDeviceResources method is called after all the load resources tasks that are in the CreateGameDeviceResourcesAsync method completes.

  • 위치와 색의 constantBufferNeverChanges를 초기화합니다.Initialize constantBufferNeverChanges with the light positions and color. __ID3D11DeviceContext::UpdateSubresource__에 대한 디바이스 컨텍스트 메서드 호출을 사용하여 초기 데이터를 상수 버퍼에 로드합니다. .Loads the initial data into the constant buffers with a device context method call to ID3D11DeviceContext::UpdateSubresource.
  • 비동기적으로 로드된 리소스 로드가 완료되었으므로 이제 이를 적절한 게임 개체와 연결해야 합니다.Since asynchronously loaded resources have completed loading, it's time to associate them with the appropriate game objects.
  • 각 게임 개체에 대해 로드된 텍스처를 사용하여 메시 및 재질을 만듭니다.For each game object, create the mesh and the material using the textures that have been loaded. 그런 다음 메시 및 재질을 게임 개체에 연결합니다.Then associate the mesh and material to the game object.
  • 대상 게임 개체에 대해 맨 위에 숫자 값이 있고 동심원으로 색상이 지정된 고리로 구성된 텍스처는 텍스처 파일에서 로드되지 않습니다.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. 대신 절차에 따라 __TargetTexture.cpp__의 코드를 사용하여 생성됩니다.Instead, it's procedurally generated using the code in TargetTexture.cpp. TargetTexture 클래스는 초기화 시 화면 외부 리소스에 텍스처를 그리는 데 필요한 리소스를 만듭니다.The TargetTexture class creates the necessary resources to draw the texture into an off screen resource at initialization time. 결과 텍스처는 적절한 대상 게임 개체와 연결됩니다.The resulting texture is then associated with the appropriate target game objects.

FinalizeCreateGameDeviceResourcesCreateWindowSizeDependentResources는 다음과 코드의 유사한 부분을 공유합니다.FinalizeCreateGameDeviceResources and CreateWindowSizeDependentResources share similar portions of code for these:

  • __SetProjParams__를 사용하여 카메라에 오른쪽 투영 행렬이 있는지 확인합니다.Use SetProjParams to ensure that the camera has the right projection matrix. 자세한 내용은 카메라 및 좌표 공간을 참조하세요.For more info, go to Camera and coordinate space.
  • 3D 회전 행렬을 카메라의 투영 행렬에 곱하는 포스트로 화면 회전을 처리합니다.Handle screen rotation by post multiplying the 3D rotation matrix to the camera's projection matrix. 그런 다음 ConstantBufferChangeOnResize 상수 버퍼를 결과 투영 행렬로 업데이트합니다.Then update the ConstantBufferChangeOnResize constant buffer with the resulting projection matrix.
  • GameResourcesLoaded 부울 전역 변수를_설정 하 여 리소스가 버퍼에 로드 되었음을 표시 하 고 다음 단계를 준비 합니다.Set the m_gameResourcesLoaded Boolean global variable to indicate that the resources are now loaded in the buffers, ready for the next step. 처음에 GameRenderer::CreateDeviceDependentResources 메서드를 통해 __GameRenderer__의 생성자 메서드에서 이 변수를 __FALSE__로 초기화했습니다.Recall that we first initialized this variable as FALSE in the GameRenderer's constructor method, through the GameRenderer::CreateDeviceDependentResources method.
  • m_gameResourcesLoaded 가 __TRUE__인 경우 장면 개체 렌더링이 수행 될 수 있습니다.When this m_gameResourcesLoaded is TRUE, rendering of the scene objects can take place. 이는 이전에 GameRenderer::Render 메서드 아래의 렌더링 프레임워크 I: 렌더링 소개 문서에서 다루었습니다.This was covered in the Rendering framework I: Intro to rendering article, under GameRenderer::Render method.
// When creating this sample game using the DirectX 11 App template, this method needs to be created.
// This new method is called from GameMain constructor in the .then loop.
// Make sure the 2D rendering is occurring on the same thread as the main rendering.
// Note: Helper class .h and .cpp files used in this method are located in the SharedContent/cpp/GameContent folder
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 constantBufferNeverChanges with the light positions and color.
    // 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 = ref new TargetTexture(
        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)

    MeshObject^ cylinderMesh = ref new CylinderMesh(d3dDevice, 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.

    Material^ cylinderMaterial = ref new 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()
        );

    // ...
    auto objects = m_game->RenderObjects();

    // Attach the textures to the appropriate game objects.
    // We'll loop through all the objects that need to be rendered.
    for (auto object = objects.begin(); object != objects.end(); object++)
    {

        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(
                ref new 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(ref new WorldFloorMesh(d3dDevice));
        }
        // ...
        else if (Cylinder^ cylinder = dynamic_cast<Cylinder^>(*object))
        {
            cylinder->Mesh(cylinderMesh);
            cylinder->NormalMaterial(cylinderMaterial);
        }
        else if (Face^ target = dynamic_cast<Face^>(*object))
        {
            const int bufferLength = 16;
            char16 str[bufferLength];
            int len = swprintf_s(str, bufferLength, L"%d", target->TargetId());
            Platform::String^ string = ref new Platform::String(str, len);

            ComPtr<ID3D11ShaderResourceView> texture;
            textureGenerator->CreateTextureResourceView(string, &texture);
            target->NormalMaterial(
                ref new 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()
                    )
                );

            textureGenerator->CreateHitTextureResourceView(string, &texture);
            target->HitMaterial(
                ref new 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;
}

CreateWindowSizeDependentResource 메서드CreateWindowSizeDependentResource method

CreateWindowSizeDependentResources 메서드는 창 크기, 방향, 스테레오 활성화 렌더링, 또는 해상도 변경 시마다 호출됩니다.CreateWindowSizeDependentResources methods are called every time the window size, orientation, stereo-enabled rendering, or resolution changes. 샘플 게임에서는 __ConstantBufferChangeOnResize__의 프로젝션 매트릭스를 업데이트 합니다.In the sample game, it updates the projection matrix in ConstantBufferChangeOnResize.

창 크기 리소스는 이러한 방식으로 업데이트됩니다.Window size resources are updated in this manner:

  • 앱 프레임워크는 창 상태의 변경을 나타내는 몇 가지 가능한 이벤트 중 하나를 가져옵니다.The App framework gets one of several possible events indicating a change in the window state.
  • 그러면 기본 게임 루프에 이벤트에 대해 알리며 기본 클래스(GameMain) 인스턴스에 __CreateWindowSizeDependentResources__가 호출되고, 이어 게임 렌더러(GameRenderer) 클래스에 CreateWindowSizeDependentResources 구현이 호출됩니다.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.
  • 이 메서드의 주요 작업은 창 속성의 변경으로 인해 시각 효과가 혼동되거나 잘못되지 않는지 확인하는 것입니다.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.

이 게임 샘플에서는 여러 메서드 호출은 FinalizeCreateGameDeviceResources 메서드와 동일합니다.For this game sample, a number of method calls are the same as the FinalizeCreateGameDeviceResources method. 코드 연습의 경우 이전 섹션으로 이동합니다.For code walkthrough, go to the previous section.

게임 HUD 및 오버레이 창 크기 렌더링 조정은 사용자 인터페이스 추가에서 다룹니다.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
            );
    }
}

다음 단계Next steps

이것이 게임의 프레임워크를 렌더링하는 그래픽을 구현하는 기본 프로세스입니다.This is the basic process for implementing the graphics rendering framework of a game. 게임의 규모가 클수록 개체 유형 및 애니메이션 동작의 계층 구조를 처리하기 위해 더 많은 추상화를 배치해야 할 것입니다.The larger your game, the more abstractions you would have to put in place to handle hierarchies of object types and animation behaviors. 메시 및 텍스처와 같은 자산을 로드 및 관리하기 위한 더 복잡한 메서드를 구현해야 합니다.You need to implement more complex methods for loading and managing assets such as meshes and textures. 다음으로 사용자 인터페이스 추가에 대해 알아봅니다.Next, let's learn how to add a user interface.