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

참고

이 항목은 DirectX 자습서 시리즈 를 사용 하 여 단순 유니버설 Windows 플랫폼 (UWP) 게임 만들기 의 일부입니다.This topic is part of the Create a simple Universal Windows Platform (UWP) game with DirectX tutorial series. 해당 링크의 항목은 계열의 컨텍스트를 설정 합니다.The topic at that link sets the context for the series.

렌더링 프레임 워크에서 장면 정보를 사용 하 여 표시 화면에 표시 하는 방법을 살펴보았습니다.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 sample game로 이동 합니다.If you haven't downloaded the latest game code for this sample, go to Direct3D sample game. 이 샘플은 여러 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.

ObjectiveObjective

목표에 대 한 간략 한 요약입니다.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. 이러한 세 단계로 느슨하게 그룹화 할 수 있습니다.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 sample game, 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. 이전에는 __렌더링 프레임 워크 I: 렌더링 소개__의 GameRenderer:: Render 메서드아래에 설명 되어 있습니다.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 sample game, 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 sample game, 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.

GameRendererGameRenderer.h

이제 샘플 게임의 렌더러 클래스 개체에서 코드를 살펴보겠습니다.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;
};

생성자Constructor

이제 샘플 게임의 GameRenderer 생성자를 살펴보고 DirectX 11 앱 템플릿에서 제공 하는 Sample3DSceneRenderer 생성자와 비교 하겠습니다.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();
}

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

샘플 게임 (및 Visual Studio의 DirectX 11 앱 (유니버설 Windows) 템플릿)에서는 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:

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 sample game, 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 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();
}

다음은 리소스를 만들고 로드 하는 데 사용 되는 방법의 목록입니다.Below is a list of the methods that are used to create and load resources.

  • CreateDeviceDependentResourcesCreateDeviceDependentResources
    • CreateGameDeviceResourcesAsync (추가 됨)CreateGameDeviceResourcesAsync (Added)
    • FinalizeCreateGameDeviceResources (추가 됨)FinalizeCreateGameDeviceResources (Added)
  • 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(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();

    ...
}

CreateGameDeviceResourcesAsync 메서드CreateGameDeviceResourcesAsync method

CreateGameDeviceResourcesAsync 는 게임 리소스를 비동기적으로 로드 하므로 create _ task 루프의 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. 별도의 스레드에서 실행 될 것으로 예상 되기 때문에 __ID3D11Device__에 정의 된 Direct3D 11 장치 메서드 ( __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.
  • ID3D11DeviceContext 에서 메서드를 사용 하지 마세요 .이 메서드는 단일 스레드와 __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 this method to create constant buffers.
  • 이 메서드를 사용 하 여 질감 (예: .dfiles) 및 셰이더 정보 (예: .csfilefiles)를 셰이더에 로드 합니다.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
  • 질감에 대 한 샘플링 정보를 캡슐화 하는 샘플러 상태 개체 만들기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, VertexShaderFlat, Shadereffect, PixelShaderFlat 등)를 말합니다.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).
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;
    }
}

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__의 코드를 사용 하 여 생성 됩니다.Instead, it's procedurally generated using the code in TargetTexture.cpp. Targettexture 클래스는 초기화 시에 질감을 off 화면 리소스로 그리는 데 필요한 리소스를 만듭니다.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.
  • 이제 리소스가 버퍼에 로드 되었음을 나타내도록 m _ 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. 이에 대해서는 렌더링 프레임 워크 I: 렌더링 소개 문서 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;
}

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 를 호출 합니다. 그러면이 인스턴스는 game 렌더러 (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 sample game, 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.