다음을 통해 공유


렌더링 프레임워크 I: 렌더링 소개

비고

이 항목은 DirectX 자습서 시리즈를 사용하여 간단한 UWP(유니버설 Windows 플랫폼) 게임 만들기의 일부입니다. 그 링크에 있는 주제가 시리즈의 맥락을 설정합니다.

지금까지 UWP(유니버설 Windows 플랫폼) 게임을 구성하는 방법과 게임의 흐름을 처리할 상태 컴퓨터를 정의하는 방법을 설명했습니다. 이제 렌더링 프레임워크를 개발하는 방법을 알아볼 차례입니다. 샘플 게임이 Direct3D 11을 사용하여 게임 장면을 렌더링하는 방법을 살펴보겠습니다.

Direct3D 11에는 게임과 같은 그래픽 집약적 애플리케이션용 3D 그래픽을 만드는 데 사용할 수 있는 고성능 그래픽 하드웨어의 고급 기능에 액세스할 수 있는 API 집합이 포함되어 있습니다.

화면에 게임 그래픽을 렌더링하는 것은 기본적으로 화면에 프레임 시퀀스를 렌더링하는 것을 의미합니다. 각 프레임에서 뷰를 기준으로 장면에서 보이는 개체를 렌더링해야 합니다.

프레임을 렌더링하려면 필요한 장면 정보를 하드웨어에 전달하여 화면에 표시해야 합니다. 화면에 아무것도 표시하려면 게임이 실행되기 시작하는 즉시 렌더링을 시작해야 합니다.

목표

UWP DirectX 게임의 그래픽 출력을 표시하기 위한 기본 렌더링 프레임워크를 설정하는 방법. 이 세 단계로 대략적으로 나눌 수 있습니다.

  1. 그래픽 인터페이스에 대한 연결을 설정합니다.
  2. 그래픽을 그리는 데 필요한 리소스를 만듭니다.
  3. 프레임을 렌더링하여 그래픽을 표시합니다.

이 항목에서는 1단계와 3단계를 다루며 그래픽이 렌더링되는 방법을 설명합니다.

렌더링 프레임워크 II: 게임 렌더링 2단계( 렌더링 프레임워크를 설정하는 방법 및 렌더링이 발생하기 전에 데이터를 준비하는 방법)를 다룹니다.

시작하기

기본 그래픽 및 렌더링 개념을 숙지하는 것이 좋습니다. Direct3D 및 렌더링을 처음으로 사용하는 경우 이 항목에서 사용되는 그래픽 및 렌더링 용어에 대한 간략한 설명은 용어 및 개념 참조하세요.

이 게임의 경우 GameRenderer 클래스는 이 샘플 게임의 렌더러를 나타냅니다. 게임 시각적 개체를 생성하는 데 사용되는 모든 Direct3D 11 및 Direct2D 개체를 만들고 유지 관리해야 합니다. 또한 렌더링할 개체 목록을 검색하는 데 사용되는 Simple3DGame 개체에 대한 참조와 HUD(헤드업 디스플레이)에 대한 게임 상태를 유지 관리합니다.

자습서의 이 부분에서는 게임에서 3D 개체를 렌더링하는 데 집중합니다.

그래픽 인터페이스에 대한 연결 설정

렌더링을 위해 하드웨어에 액세스하는 방법에 대한 자세한 내용은 게임의 UWP 앱 프레임워크 정의 항목을 참조하세요.

App::Initialize 메서드

아래와 같이 std::make_shared 함수는 shared_ptrDX::DeviceResources에 생성하는 데 사용됩니다. 이 함수는 장치에 대한 액세스도 제공합니다.

Direct3D 11에서는 디바이스 사용하여 개체를 할당 및 삭제하고, 기본 형식을 렌더링하고, 그래픽 드라이버를 통해 그래픽 카드와 통신합니다.

void Initialize(CoreApplicationView const& applicationView)
{
    ...

    // At this point we have access to the device. 
    // We can create the device-dependent resources.
    m_deviceResources = std::make_shared<DX::DeviceResources>();
}

프레임을 렌더링하여 그래픽 표시

게임이 시작될 때 게임 장면을 렌더링해야 합니다. 렌더링 지침은 아래와 같이 GameMain::Run 메서드에서 시작합니다.

간단한 흐름은 이것입니다.

  1. 업데이트
  2. 렌더링
  3. 현재

GameMain::Run 메서드

void GameMain::Run()
{
    while (!m_windowClosed)
    {
        if (m_visible) // if the window is visible
        {
            switch (m_updateState)
            {
            ...
            default:
                CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
                Update();
                m_renderer->Render();
                m_deviceResources->Present();
                m_renderNeeded = false;
            }
        }
        else
        {
            CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
        }
    }
    m_game->OnSuspending();  // Exiting due to window close, so save state.
}

업데이트

GameMain::Update 메서드에서 게임 상태를 업데이트하는 방법에 대한 자세한 내용은 게임 흐름 관리 항목을 참조하세요.

렌더링

렌더링은 GameMain::Run메서드에서 GameRenderer::Render 메서드를 호출하여 구현됩니다.

스테레오 렌더링 이 활성화된 경우, 오른쪽 눈과 왼쪽 눈 각각에 대해 두 번의 렌더링 패스가 있습니다. 각 렌더링 패스에서 렌더 타겟과 뎁스-스텐실 뷰를 디바이스에 바인딩합니다. 이후에 깊이 및 스텐실 뷰를 지웁니다.

비고

스테레오 렌더링은 버텍스 인스턴싱 또는 지오메트리 셰이더를 사용하는 단일 패스 스테레오와 같은 다른 방법을 통해 수행할 수 있습니다. 투 렌더링 패스 메서드는 스테레오 렌더링을 수행하는 속도가 느리지만 더 편리한 방법입니다.

게임이 실행되고 리소스가 로드되면 렌더링 패스당 한 번씩 프로젝션 매트릭스업데이트합니다. 개체는 각 보기에서 약간 다릅니다. 다음으로, 그래픽 렌더링 파이프라인을 설정합니다.

비고

리소스 로드 방법에 대한 자세한 내용은 DirectX 그래픽 리소스 만들기 및 로드를 참조하세요.

이 샘플 게임에서 렌더러는 모든 개체에서 표준 꼭짓점 레이아웃을 사용하도록 설계되었습니다. 이렇게 하면 셰이더 디자인이 간소화되고 개체의 기하 도형과 관계없이 셰이더 간에 쉽게 변경할 수 있습니다.

GameRenderer::Render 메서드

입력 꼭짓점 레이아웃을 사용하도록 Direct3D 컨텍스트를 설정합니다. 입력 레이아웃 객체는 꼭짓점 버퍼 데이터를 렌더링 파이프라인으로 스트리밍하는 방법을 설명합니다.

다음으로, 앞에서 정의한 상수 버퍼를 사용하도록 Direct3D 컨텍스트를 설정합니다. 이 버퍼는 꼭짓점 셰이더 파이프라인 단계 및 픽셀 셰이더 파이프라인 단계에서 사용됩니다.

비고

상수 버퍼 정의에 대한 자세한 내용은 렌더링 프레임워크 II: 게임 렌더링 참조하세요.

동일한 입력 레이아웃과 상수 버퍼 집합이 파이프라인에 있는 모든 셰이더에 사용되므로 프레임당 한 번씩 설정됩니다.

void GameRenderer::Render()
{
    bool stereoEnabled{ m_deviceResources->GetStereoState() };

    auto d3dContext{ m_deviceResources->GetD3DDeviceContext() };
    auto d2dContext{ m_deviceResources->GetD2DDeviceContext() };

    int renderingPasses = 1;
    if (stereoEnabled)
    {
        renderingPasses = 2;
    }

    for (int i = 0; i < renderingPasses; i++)
    {
        // Iterate through the number of rendering passes to be completed.
        // 2 rendering passes if stereo is enabled.
        if (i > 0)
        {
            // Doing the Right Eye View.
            ID3D11RenderTargetView* const targets[1] = { m_deviceResources->GetBackBufferRenderTargetViewRight() };

            // Resets render targets to the screen.
            // OMSetRenderTargets binds 2 things to the device.
            // 1. Binds one render target atomically to the device.
            // 2. Binds the depth-stencil view, as returned by the GetDepthStencilView method, to the device.
            // For more info, see
            // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-omsetrendertargets

            d3dContext->OMSetRenderTargets(1, targets, m_deviceResources->GetDepthStencilView());

            // Clears the depth stencil view.
            // A depth stencil view contains the format and buffer to hold depth and stencil info.
            // For more info about depth stencil view, go to: 
            // https://learn.microsoft.com/windows/uwp/graphics-concepts/depth-stencil-view--dsv-
            // A depth buffer is used to store depth information to control which areas of 
            // polygons are rendered rather than hidden from view. To learn more about a depth buffer,
            // go to: https://learn.microsoft.com/windows/uwp/graphics-concepts/depth-buffers
            // A stencil buffer is used to mask pixels in an image, to produce special effects. 
            // The mask determines whether a pixel is drawn or not,
            // by setting the bit to a 1 or 0. To learn more about a stencil buffer,
            // go to: https://learn.microsoft.com/windows/uwp/graphics-concepts/stencil-buffers

            d3dContext->ClearDepthStencilView(m_deviceResources->GetDepthStencilView(), D3D11_CLEAR_DEPTH, 1.0f, 0);

            // Direct2D -- discussed later
            d2dContext->SetTarget(m_deviceResources->GetD2DTargetBitmapRight());
        }
        else
        {
            // Doing the Mono or Left Eye View.
            // As compared to the right eye:
            // m_deviceResources->GetBackBufferRenderTargetView instead of GetBackBufferRenderTargetViewRight
            ID3D11RenderTargetView* const targets[1] = { m_deviceResources->GetBackBufferRenderTargetView() };

            // Same as the Right Eye View.
            d3dContext->OMSetRenderTargets(1, targets, m_deviceResources->GetDepthStencilView());
            d3dContext->ClearDepthStencilView(m_deviceResources->GetDepthStencilView(), D3D11_CLEAR_DEPTH, 1.0f, 0);

            // d2d -- Discussed later under Adding UI
            d2dContext->SetTarget(m_deviceResources->GetD2DTargetBitmap());
        }

        const float clearColor[4] = { 0.5f, 0.5f, 0.8f, 1.0f };

        // Only need to clear the background when not rendering the full 3D scene since
        // the 3D world is a fully enclosed box and the dynamics prevents the camera from
        // moving outside this space.
        if (i > 0)
        {
            // Doing the Right Eye View.
            d3dContext->ClearRenderTargetView(m_deviceResources->GetBackBufferRenderTargetViewRight(), clearColor);
        }
        else
        {
            // Doing the Mono or Left Eye View.
            d3dContext->ClearRenderTargetView(m_deviceResources->GetBackBufferRenderTargetView(), clearColor);
        }

        // Render the scene objects
        if (m_game != nullptr && m_gameResourcesLoaded && m_levelResourcesLoaded)
        {
            // This section is only used after the game state has been initialized and all device
            // resources needed for the game have been created and associated with the game objects.
            if (stereoEnabled)
            {
                // When doing stereo, it is necessary to update the projection matrix once per rendering pass.

                auto orientation = m_deviceResources->GetOrientationTransform3D();

                ConstantBufferChangeOnResize changesOnResize;
                // Apply either a left or right eye projection, which is an offset from the middle
                XMStoreFloat4x4(
                    &changesOnResize.projection,
                    XMMatrixMultiply(
                        XMMatrixTranspose(
                            i == 0 ?
                            m_game->GameCamera().LeftEyeProjection() :
                            m_game->GameCamera().RightEyeProjection()
                            ),
                        XMMatrixTranspose(XMLoadFloat4x4(&orientation))
                        )
                    );

                d3dContext->UpdateSubresource(
                    m_constantBufferChangeOnResize.get(),
                    0,
                    nullptr,
                    &changesOnResize,
                    0,
                    0
                    );
            }

            // Update variables that change once per frame.
            ConstantBufferChangesEveryFrame constantBufferChangesEveryFrameValue;
            XMStoreFloat4x4(
                &constantBufferChangesEveryFrameValue.view,
                XMMatrixTranspose(m_game->GameCamera().View())
                );
            d3dContext->UpdateSubresource(
                m_constantBufferChangesEveryFrame.get(),
                0,
                nullptr,
                &constantBufferChangesEveryFrameValue,
                0,
                0
                );

            // Set up the graphics pipeline. This sample uses the same InputLayout and set of
            // constant buffers for all shaders, so they only need to be set once per frame.
            // For more info about the graphics or rendering pipeline, see
            // https://learn.microsoft.com/windows/win32/direct3d11/overviews-direct3d-11-graphics-pipeline

            // IASetInputLayout binds an input-layout object to the input-assembler (IA) stage. 
            // Input-layout objects describe how vertex buffer data is streamed into the IA pipeline stage.
            // Set up the Direct3D context to use this vertex layout. For more info, see
            // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-iasetinputlayout
            d3dContext->IASetInputLayout(m_vertexLayout.get());

            // VSSetConstantBuffers sets the constant buffers used by the vertex shader pipeline stage.
            // Set up the Direct3D context to use these constant buffers. For more info, see
            // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-vssetconstantbuffers

            ID3D11Buffer* constantBufferNeverChanges{ m_constantBufferNeverChanges.get() };
            d3dContext->VSSetConstantBuffers(0, 1, &constantBufferNeverChanges);
            ID3D11Buffer* constantBufferChangeOnResize{ m_constantBufferChangeOnResize.get() };
            d3dContext->VSSetConstantBuffers(1, 1, &constantBufferChangeOnResize);
            ID3D11Buffer* constantBufferChangesEveryFrame{ m_constantBufferChangesEveryFrame.get() };
            d3dContext->VSSetConstantBuffers(2, 1, &constantBufferChangesEveryFrame);
            ID3D11Buffer* constantBufferChangesEveryPrim{ m_constantBufferChangesEveryPrim.get() };
            d3dContext->VSSetConstantBuffers(3, 1, &constantBufferChangesEveryPrim);

            // Sets the constant buffers used by the pixel shader pipeline stage. 
            // For more info, see
            // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-pssetconstantbuffers

            d3dContext->PSSetConstantBuffers(2, 1, &constantBufferChangesEveryFrame);
            d3dContext->PSSetConstantBuffers(3, 1, &constantBufferChangesEveryPrim);
            ID3D11SamplerState* samplerLinear{ m_samplerLinear.get() };
            d3dContext->PSSetSamplers(0, 1, &samplerLinear);

            for (auto&& object : m_game->RenderObjects())
            {
                // The 3D object render method handles the rendering.
                // For more info, see Primitive rendering below.
                object->Render(d3dContext, m_constantBufferChangesEveryPrim.get());
            }
        }

        // Start of 2D rendering
        ...
    }
}

기본 렌더링

장면을 렌더링할 때, 렌더링하여야 하는 모든 개체를 순환 처리합니다. 아래 단계는 각 개체(기본 형식)에 대해 반복됩니다.

  • 상수 버퍼(m_constantBufferChangesEveryPrim)를 모델의 월드 변환 매트릭스 및 재료 정보로 업데이트합니다.
  • m_constantBufferChangesEveryPrim 각 개체에 대한 매개 변수를 포함합니다. 여기에는 개체-세계 변환 매트릭스뿐만 아니라 조명 계산을 위한 색 및 반사 지수와 같은 재질 속성도 포함됩니다.
  • Direct3D 컨텍스트를 설정하여 메시 객체 데이터의 입력 꼭짓점 레이아웃이 렌더링 파이프라인의 입력 조립기(IA) 단계로 스트리밍되도록 합니다.
  • IA 단계에서 인덱스 버퍼 사용하도록 Direct3D 컨텍스트를 설정합니다. 기본 정보 형식, 데이터 순서를 제공합니다.
  • 드로우 콜을 제출하여 인덱싱된 비인스턴스화된 기본 형식을 그립니다. GameObject::Render 메서드는 주어진 기본 형식과 관련된 데이터로 기본형 상수 버퍼를 업데이트합니다. 이렇게 하면 컨텍스트에서 DrawIndexed 호출이 발생하여 각 기본 형식의 기하 도형을 그리게 됩니다. 특히 이 드로우 콜은 명령 및 데이터를 GPU(그래픽 처리 장치)로 큐에 추가하며, 상수 버퍼 데이터에 의해 매개변수가 설정됩니다. 각 그리기 호출은 꼭짓점당 꼭짓점 셰이더를 한 번 실행한 후, 픽셀 셰이더를 원시의 각 삼각형에 있는 모든 픽셀에 대해 한 번씩 실행합니다. 텍스처는 픽셀 셰이더가 렌더링을 수행하는 데 사용하는 상태의 일부입니다.

다음은 여러 상수 버퍼를 사용하는 이유입니다.

  • 게임은 여러 상수 버퍼를 사용하지만 이러한 버퍼를 기본 형식당 한 번만 업데이트하면 됩니다. 앞에서 설명한 것처럼 상수 버퍼는 각 기본 형식에 대해 실행되는 셰이더에 대한 입력과 같습니다. 일부 데이터는 정적입니다 (m_constantBufferNeverChanges); 일부 데이터는 카메라의 위치와 같이 프레임마다 일정하게 유지됩니다 (m_constantBufferChangesEveryFrame); 그리고 일부 데이터는 색상 및 질감과 같은 개체별로 특정합니다 (m_constantBufferChangesEveryPrim).
  • 게임 렌더러는 이러한 입력을 다른 상수 버퍼로 분리하여 CPU 및 GPU에서 사용하는 메모리 대역폭을 최적화합니다. 이 방법은 GPU가 추적해야 하는 데이터의 양을 최소화하는 데도 도움이 됩니다. GPU에는 명령의 큰 큐가 있으며, 게임이Draw를 호출할 때마다 해당 명령이 연결된 데이터와 함께 큐에 대기됩니다. 게임이 기본 상수 버퍼를 업데이트하고 다음 그리기 명령을 실행하면 그래픽 드라이버는 이 다음 명령과 연결된 데이터를 큐에 추가합니다. 게임이 100개의 기본 형식을 그리면 큐에 상수 버퍼 데이터의 복사본이 100개 있을 수 있습니다. 게임이 GPU에 보내는 데이터의 양을 최소화하기 위해 게임은 각 기본 형식에 대한 업데이트만 포함하는 별도의 기본 상수 버퍼를 사용합니다.

GameObject::Render 메서드

void GameObject::Render(
    _In_ ID3D11DeviceContext* context,
    _In_ ID3D11Buffer* primitiveConstantBuffer
    )
{
    if (!m_active || (m_mesh == nullptr) || (m_normalMaterial == nullptr))
    {
        return;
    }

    ConstantBufferChangesEveryPrim constantBuffer;

    // Put the model matrix info into a constant buffer, in world matrix.
    XMStoreFloat4x4(
        &constantBuffer.worldMatrix,
        XMMatrixTranspose(ModelMatrix())
        );

    // Check to see which material to use on the object.
    // If a collision (a hit) is detected, GameObject::Render checks the current context, which 
    // indicates whether the target has been hit by an ammo sphere. If the target has been hit, 
    // this method applies a hit material, which reverses the colors of the rings of the target to 
    // indicate a successful hit to the player. Otherwise, it applies the default material 
    // with the same method. In both cases, it sets the material by calling Material::RenderSetup, 
    // which sets the appropriate constants into the constant buffer. Then, it calls 
    // ID3D11DeviceContext::PSSetShaderResources to set the corresponding texture resource for the 
    // pixel shader, and ID3D11DeviceContext::VSSetShader and ID3D11DeviceContext::PSSetShader 
    // to set the vertex shader and pixel shader objects themselves, respectively.

    if (m_hit && m_hitMaterial != nullptr)
    {
        m_hitMaterial->RenderSetup(context, &constantBuffer);
    }
    else
    {
        m_normalMaterial->RenderSetup(context, &constantBuffer);
    }

    // Update the primitive constant buffer with the object model's info.
    context->UpdateSubresource(primitiveConstantBuffer, 0, nullptr, &constantBuffer, 0, 0);

    // Render the mesh.
    // See MeshObject::Render method below.
    m_mesh->Render(context);
}

MeshObject::Render 메서드

void MeshObject::Render(_In_ ID3D11DeviceContext* context)
{
    // PNTVertex is a struct. stride provides us the size required for all the mesh data
    // struct PNTVertex
    //{
    //  DirectX::XMFLOAT3 position;
    //  DirectX::XMFLOAT3 normal;
    //  DirectX::XMFLOAT2 textureCoordinate;
    //};
    uint32_t stride{ sizeof(PNTVertex) };
    uint32_t offset{ 0 };

    // Similar to the main render loop.
    // Input-layout objects describe how vertex buffer data is streamed into the IA pipeline stage.
    ID3D11Buffer* vertexBuffer{ m_vertexBuffer.get() };
    context->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);

    // IASetIndexBuffer binds an index buffer to the input-assembler stage.
    // For more info, see
    // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-iasetindexbuffer.
    context->IASetIndexBuffer(m_indexBuffer.get(), DXGI_FORMAT_R16_UINT, 0);

    // Binds information about the primitive type, and data order that describes input data for the input assembler stage.
    // For more info, see
    // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-iasetprimitivetopology.
    context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    // Draw indexed, non-instanced primitives. A draw API submits work to the rendering pipeline.
    // For more info, see
    // https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-drawindexed.
    context->DrawIndexed(m_indexCount, 0, 0);
}

DeviceResources::Present 메서드

DeviceResources::Present 메서드를 호출해 버퍼에 배치한 내용을 표시합니다.

사용자에게 프레임을 표시하는 데 사용되는 버퍼 컬렉션에 스왑 체인이라는 용어를 사용합니다. 애플리케이션이 표시할 새 프레임을 표시할 때마다 스왑 체인의 첫 번째 버퍼가 표시된 버퍼를 대신합니다. 이 과정을 교환 또는 전환이라고 합니다. 자세한 내용은 스왑 체인참조하세요.

  • IDXGISwapChain1 인터페이스의 Present 메서드는 DXGI에게 VSync(수직 동기화)가 이루어질 때까지 차단하도록 지시하여 애플리케이션이 다음 VSync가 발생할 때까지 대기 상태로 전환되도록 합니다. 이렇게 하면 화면에 표시되지 않는 프레임 렌더링 주기를 낭비하지 않습니다.
  • ID3D11DeviceContext3 인터페이스의 DiscardView 메서드는 렌더 타겟내용을 버립니다. 이는 기존 콘텐츠를 완전히 덮어쓸 때만 유효한 작업입니다. 더티 또는 스크롤 사각형을 사용하는 경우 이 호출을 제거해야 합니다.
  • 동일한 DiscardView 메서드를 사용하여 깊이 스텐실내용을 삭제합니다.
  • HandleDeviceLost 메서드는 제거되는 디바이스 시나리오를 관리하는 데 사용됩니다. 연결 끊기 또는 드라이버 업그레이드로 디바이스가 제거된 경우 모든 디바이스 리소스를 다시 만들어야 합니다. 자세한 내용은 Direct3D 11디바이스 제거 시나리오 처리를 참조하세요.

팁 (조언)

원활한 프레임 속도를 얻으려면 프레임을 렌더링하는 작업의 양이 VSyncs 사이의 시간에 맞는지 확인해야 합니다.

// Present the contents of the swap chain to the screen.
void DX::DeviceResources::Present()
{
    // The first argument instructs DXGI to block until VSync, putting the application
    // to sleep until the next VSync. This ensures we don't waste any cycles rendering
    // frames that will never be displayed to the screen.
    HRESULT hr = m_swapChain->Present(1, 0);

    // Discard the contents of the render target.
    // This is a valid operation only when the existing contents will be entirely
    // overwritten. If dirty or scroll rects are used, this call should be removed.
    m_d3dContext->DiscardView(m_d3dRenderTargetView.get());

    // Discard the contents of the depth stencil.
    m_d3dContext->DiscardView(m_d3dDepthStencilView.get());

    // If the device was removed either by a disconnection or a driver upgrade, we 
    // must recreate all device resources.
    if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
    {
        HandleDeviceLost();
    }
    else
    {
        winrt::check_hresult(hr);
    }
}

다음 단계

이 항목에서는 그래픽이 디스플레이에 렌더링되는 방법을 설명했으며, 사용된 일부 렌더링 용어(아래)에 대한 간단한 설명을 제공합니다. 렌더링 프레임워크 II: 게임 렌더링 항목의 렌더링에 대해 자세히 알아보고 렌더링하기 전에 필요한 데이터를 준비하는 방법을 알아봅니다.

용어 및 개념

간단한 게임 장면

간단한 게임 장면은 여러 광원이 있는 몇 가지 개체로 구성됩니다.

개체의 셰이프는 공간의 X, Y, Z 좌표 집합으로 정의됩니다. 게임 세계의 실제 렌더링 위치는 위치 X, Y, Z 좌표에 변환 매트릭스를 적용하여 확인할 수 있습니다. 또한 개체에 재질이 적용되는 방식을 지정하는 U 및 V 텍스처 좌표 집합이 있을 수도 있습니다. 이렇게 하면 개체의 표면 속성이 정의되고 개체에 거친 표면(예: 테니스공) 또는 부드러운 광택 표면(예: 볼링공)이 있는지 확인할 수 있습니다.

장면 및 개체 정보는 렌더링 프레임워크에서 프레임별로 장면 프레임을 다시 만들어 디스플레이 모니터에 생동감 있게 만드는 데 사용됩니다.

렌더링 파이프라인

렌더링 파이프라인은 3D 장면 정보가 화면에 표시되는 이미지로 변환되는 프로세스입니다. Direct3D 11에서는 이 파이프라인을 프로그래밍할 수 있습니다. 렌더링 요구 사항을 지원하도록 스테이지를 조정할 수 있습니다. 일반적인 셰이더 코어를 특징으로 하는 단계는 HLSL 프로그래밍 언어를 사용하여 프로그래밍할 수 있습니다. 파이프라인로도 알려진 그래픽 렌더링 파이프라인, 또는 단순히 파이프라인.

이 파이프라인을 만들려면 이러한 세부 정보를 잘 알고 있어야 합니다.

자세한 내용은 Direct3D 11 렌더링 파이프라인 이해 및 그래픽 파이프라인을 참조하세요.

HLSL

HLSL은 DirectX의 상위 수준 셰이더 언어입니다. HLSL을 사용하여 Direct3D 파이프라인에 대한 C와 유사한 프로그래밍 가능 셰이더를 만들 수 있습니다. 자세한 내용은 HLSL참조하세요.

셰이더

셰이더는 렌더링될 때 개체의 표면이 표시되는 방식을 결정하는 명령 집합으로 간주할 수 있습니다. HLSL로 프로그래밍된 것들은 HLSL 셰이더라고 불립니다. [HLSL])(#hlsl) 셰이더의 소스 코드 파일에는 .hlsl 파일 확장명이 있습니다. 이러한 셰이더는 빌드 타임 또는 런타임에 컴파일할 수 있으며 런타임에 적절한 파이프라인 단계로 설정할 수 있습니다. 컴파일된 셰이더 개체에는 .cso 파일 확장자가 있습니다.

Direct3D 9 셰이더는 셰이더 모델 1, 셰이더 모델 2 및 셰이더 모델 3을 사용하여 설계할 수 있습니다. Direct3D 10 셰이더는 셰이더 모델 4에서만 디자인할 수 있습니다. Direct3D 11 셰이더는 셰이더 모델 5에서 디자인할 수 있습니다. Direct3D 11.3 및 Direct3D 12는 셰이더 모델 5.1에서 설계할 수 있으며 Direct3D 12는 셰이더 모델 6에서도 설계할 수 있습니다.

꼭짓점 셰이더 및 픽셀 셰이더

데이터는 기본 형식의 스트림으로 그래픽 파이프라인에 입력되며, 버텍스 셰이더와 픽셀 셰이더 등 다양한 셰이더에 의해 처리됩니다.

꼭짓점 셰이더는 꼭짓점들을 처리하며, 일반적으로 변환, 스키닝, 조명 같은 작업을 수행합니다. 픽셀 셰이더를 사용하면 개별 픽셀 조명 및 후처리와 같은 다양한 셰이딩 기법을 사용할 수 있습니다. 상수 변수, 보간된 꼭짓점 값, 텍스처 데이터 및 기타 데이터를 결합하여 픽셀당 출력을 생성합니다.

셰이더 단계

이러한 기본 형식 스트림을 처리하기 위해 정의된 이러한 다양한 셰이더 시퀀스를 렌더링 파이프라인의 셰이더 스테이지라고 합니다. 실제 단계는 Direct3D 버전에 따라 달라지지만 일반적으로 꼭짓점, 기하 도형 및 픽셀 스테이지를 포함합니다. 테셀레이션을 위한 헐 및 도메인 셰이더 및 컴퓨팅 셰이더와 같은 다른 단계도 있습니다. 이러한 모든 단계는 HLSL사용하여 완전히 프로그래밍할 수 있습니다. 자세한 내용은 그래픽스 파이프라인을 참조하십시오.

다양한 셰이더 파일 형식

셰이더 코드 파일 확장명은 다음과 같습니다.

  • .hlsl 확장이 있는 파일에는 [HLSL])(#hlsl) 소스 코드가 있습니다.
  • .cso 확장이 있는 파일에는 컴파일된 셰이더 개체가 있습니다.
  • .h 확장이 있는 파일은 헤더 파일이지만 셰이더 코드 컨텍스트에서 이 헤더 파일은 셰이더 데이터를 보유하는 바이트 배열을 정의합니다.
  • .hlsli 확장이 있는 파일에는 상수 버퍼의 형식이 포함됩니다. 샘플 게임에서 파일은 셰이더>ConstantBuffers.hlsli입니다.

비고

런타임에 .cso 파일을 로드하거나 실행 코드에 .h 파일을 추가하여 셰이더를 포함했습니다. 그러나 동일한 셰이더에는 둘 다 사용하지 않습니다.

DirectX에 대한 심층 이해

Direct3D 11은 게임과 같은 그래픽 집약적 애플리케이션에 대한 그래픽을 만드는 데 도움이 되는 API 집합으로, 집약적인 계산을 처리하기 위해 좋은 그래픽 카드를 원합니다. 이 섹션에서는 리소스, 하위 리소스, 디바이스 및 디바이스 컨텍스트와 같은 Direct3D 11 그래픽 프로그래밍 개념을 간략하게 설명합니다.

리소스

리소스(디바이스 리소스라고도 함)는 질감, 위치 또는 색과 같은 개체를 렌더링하는 방법에 대한 정보로 생각할 수 있습니다. 리소스는 파이프라인에 데이터를 제공하고 장면 중에 렌더링되는 항목을 정의합니다. 리소스는 게임 미디어에서 로드하거나 런타임에 동적으로 만들 수 있습니다.

실제로 리소스는 Direct3D 파이프라인에서 액세스할 수 있는 메모리 영역입니다. 파이프라인이 메모리에 효율적으로 액세스하려면 파이프라인에 제공된 데이터(예: 입력 기하 도형, 셰이더 리소스 및 텍스처)를 리소스에 저장해야 합니다. 모든 Direct3D 리소스가 파생되는 리소스에는 버퍼 또는 텍스처의 두 가지 유형이 있습니다. 각 파이프라인 단계에 대해 최대 128개의 리소스를 활성화할 수 있습니다. 자세한 내용은 리소스참조하세요.

하위 리소스

하위 리소스라는 용어는 리소스의 하위 집합을 나타냅니다. Direct3D는 전체 리소스를 참조하거나 리소스의 하위 집합을 참조할 수 있습니다. 자세한 내용은 하위 리소스참조하세요.

깊이 스텐실

깊이 스텐실 리소스에는 깊이 및 스텐실 정보를 보관하는 형식과 버퍼가 포함됩니다. 텍스처 리소스를 사용하여 만들어집니다. 깊이-스텐실 리소스를 만드는 방법에 대한 자세한 정보는 구성 Depth-Stencil 기능을 참조하세요. ID3D11DepthStencilView 인터페이스를 사용하여 구현된 깊이 스텐실 보기를 통해 깊이 스텐실 리소스에 액세스합니다.

깊이 정보는 어떤 다각형 영역이 다른 영역 뒤에 있는지 알려주므로 숨겨진 영역을 확인할 수 있습니다. 스텐실 정보는 마스킹되는 픽셀을 알려줍니다. 픽셀이 그려지는지 여부를 결정하여 특수 효과를 생성하는 데 사용할 수 있으며, 비트를 1 또는 0으로 설정합니다.

자세한 내용은 깊이-스텐실 뷰, 깊이 버퍼, 및 스텐실 버퍼를 참조하세요.

렌더링 대상

렌더링 대상은 렌더링 패스의 끝에 쓸 수 있는 리소스입니다. 일반적으로 ID3D11Device::CreateRenderTargetView 메서드를 사용하여 스왑 체인 백 버퍼(리소스이기도 함)를 입력 매개 변수로 사용하여 생성됩니다.

각 렌더 타겟에는 해당하는 깊이 스텐실 뷰도 있어야 합니다. 이는 OMSetRenderTargets를 사용하여 렌더 타겟을 설정할 때 깊이 스텐실 뷰가 필요하기 때문입니다. ID3D11RenderTargetView 인터페이스를 사용하여 구현된 렌더링 대상 보기를 통해 렌더링 대상 리소스에 액세스합니다.

디바이스

디바이스를 개체를 할당 및 삭제하고, 기본 형식을 렌더링하고, 그래픽 드라이버를 통해 그래픽 카드와 통신하는 방법으로 상상할 수 있습니다.

보다 정확한 설명을 위해 Direct3D 디바이스는 Direct3D의 렌더링 구성 요소입니다. 디바이스는 렌더링 상태를 캡슐화 및 저장하고, 변환 및 조명 작업을 수행하고, 이미지를 표면에 래스터화합니다. 자세한 내용은 디바이스 참조하세요.

디바이스는 ID3D11Device 인터페이스로 표시됩니다. 즉, ID3D11Device 인터페이스는 가상 디스플레이 어댑터를 나타내며 디바이스가 소유한 리소스를 만드는 데 사용됩니다.

ID3D11Device의 버전이 다릅니다. ID3D11Device5는 최신 버전으로, ID3D11Device4에 있는 메서드에 새 메서드를 추가합니다. Direct3D가 기본 하드웨어와 통신하는 방법에 대한 자세한 내용은 WDDM(Windows 디바이스 드라이버 모델) 아키텍처참조하세요.

각 애플리케이션에는 하나 이상의 디바이스가 있어야 합니다. 대부분의 애플리케이션은 하나만 만듭니다. D3D11CreateDevice 또는 D3D11CreateDeviceAndSwapChain 호출하고 D3D_DRIVER_TYPE 플래그를 사용하여 드라이버 유형을 지정하여 컴퓨터에 설치된 하드웨어 드라이버 중 하나에 대한 디바이스를 만듭니다. 각 디바이스는 원하는 기능에 따라 하나 이상의 디바이스 컨텍스트를 사용할 수 있습니다. 자세한 내용은 D3D11CreateDevice 함수참조하세요.

디바이스 컨텍스트

디바이스 컨텍스트는 파이프라인 상태를 설정하고 디바이스소유하는 리소스를 사용하여 렌더링 명령을 생성하는 데 사용됩니다.

Direct3D 11은 즉시 렌더링을 위한 컨텍스트와 지연된 렌더링을 위한 두 가지 유형의 디바이스 컨텍스트를 구현합니다. 두 컨텍스트 모두 ID3D11DeviceContext 인터페이스로 표시됩니다.

ID3D11DeviceContext 인터페이스는 서로 다른 버전을 가지고 있습니다. ID3D11DeviceContext4ID3D11DeviceContext3에 새로운 메서드를 추가합니다.

ID3D11DeviceContext4 Windows 10 크리에이터스 업데이트에 도입되었으며 ID3D11DeviceContext 인터페이스의 최신 버전입니다. Windows 10 크리에이터스 업데이트 이상을 대상으로 하는 애플리케이션은 이전 버전 대신 이 인터페이스를 사용해야 합니다. 자세한 내용은 ID3D11DeviceContext4참조하세요.

DX::D eviceResources

DX::DeviceResources 클래스는 DeviceResources.cpp/.h 파일에 포함되어 있으며 모든 DirectX 디바이스 리소스를 제어합니다.

버퍼

버퍼 리소스는 요소로 그룹화된 완전히 형식화된 데이터의 컬렉션입니다. 버퍼를 사용하여 위치 벡터, 일반 벡터, 꼭짓점 버퍼의 텍스처 좌표, 인덱스 버퍼의 인덱스 또는 디바이스 상태를 비롯한 다양한 데이터를 저장할 수 있습니다. 버퍼 요소에는 압축된 데이터 값(예: R8G8B8A8 표면 값), 단일 8비트 정수 또는 4개의 32비트 부동 소수점 값이 포함될 수 있습니다.

사용할 수 있는 버퍼에는 꼭짓점 버퍼, 인덱스 버퍼 및 상수 버퍼의 세 가지 유형이 있습니다.

꼭짓점 버퍼

기하 도형을 정의하는 데 사용되는 꼭짓점 데이터를 포함합니다. 꼭짓점 데이터에는 위치 좌표, 색 데이터, 질감 좌표 데이터, 일반 데이터 등이 포함됩니다.

인덱스 버퍼

꼭짓점 버퍼에 정수 오프셋을 포함하며 기본 형식을 보다 효율적으로 렌더링하는 데 사용됩니다. 인덱스 버퍼에는 16비트 또는 32비트 인덱스의 순차 집합이 포함됩니다. 각 인덱스는 꼭짓점 버퍼에서 꼭짓점을 식별하는 데 사용됩니다.

상수 버퍼 또는 셰이더 상수 버퍼

파이프라인에 셰이더 데이터를 효율적으로 제공할 수 있습니다. 상수 버퍼를 각 기본 형식에 대해 실행되는 셰이더에 대한 입력으로 사용하고 렌더링 파이프라인의 스트림 출력 단계 결과를 저장할 수 있습니다. 개념적으로 상수 버퍼는 단일 요소 꼭짓점 버퍼처럼 보입니다.

버퍼 디자인 및 구현

예를 들어 샘플 게임과 같이 데이터 형식을 기반으로 버퍼를 디자인할 수 있습니다. 정적 데이터에 대해 하나의 버퍼가 생성되고, 다른 하나는 프레임에 상수인 데이터에 대해, 다른 하나는 기본 형식과 관련된 데이터에 대해 만들어집니다.

모든 버퍼 형식은 ID3D11Buffer 인터페이스에 의해 캡슐화되며 ID3D11Device::CreateBuffer호출하여 버퍼 리소스를 만들 수 있습니다. 그러나 버퍼에 액세스하려면 먼저 파이프라인에 바인딩되어야 합니다. 버퍼는 읽기를 위해 동시에 여러 파이프라인 단계에 바인딩될 수 있습니다. 버퍼는 쓰기를 위해 단일 파이프라인 단계에 바인딩될 수도 있습니다. 그러나 읽기와 쓰기 모두에 대해 동일한 버퍼를 동시에 바인딩할 수 없습니다.

이러한 방식으로 버퍼를 바인딩할 수 있습니다.

  • ID3D11DeviceContext::IASetVertexBuffersID3D11DeviceContext::IASetIndexBuffer같은 ID3D11DeviceContext 메서드를 호출하여 입력 어셈블러 스테이지로.
  • ID3D11DeviceContext::SOSetTargets를 호출하여 스트림 출력 단계로 전환합니다.
  • ID3D11DeviceContext::VSSetConstantBuffers 같은 셰이더 메소드를 호출하여 셰이더 스테이지에 전달합니다.

자세한 내용은 Direct3D 11버퍼 소개를 참조하세요.

DXGI

Microsoft DXGI(DirectX Graphics Infrastructure)는 Direct3D에 필요한 일부 하위 수준 작업을 캡슐화하는 하위 시스템입니다. 교착 상태가 발생하지 않도록 다중 스레드 애플리케이션에서 DXGI를 사용할 때는 특별히 주의해야 합니다. 자세한 내용은 다중 스레딩 및 DXGI을 참조해 주세요.

기능 수준

기능 수준은 신규 및 기존 컴퓨터에서 비디오 카드의 다양성을 처리하기 위해 Direct3D 11에 도입된 개념입니다. 기능 수준은 잘 정의된 GPU(그래픽 처리 장치) 기능 집합입니다.

각 비디오 카드는 설치된 GPU에 따라 특정 수준의 DirectX 기능을 구현합니다. 이전 버전의 Microsoft Direct3D에서는 비디오 카드가 구현된 Direct3D 버전을 확인하고 그에 따라 애플리케이션을 프로그래밍할 수 있습니다.

기능 수준을 사용하면 디바이스를 만들 때 요청하려는 기능 수준에 대한 디바이스를 만들 수 있습니다. 디바이스 만들기가 작동하는 경우 해당 기능 수준이 존재하며, 그렇지 않은 경우 하드웨어는 해당 기능 수준을 지원하지 않습니다. 더 낮은 기능 수준에서 디바이스를 다시 만들거나 애플리케이션을 종료하도록 선택할 수 있습니다. 예를 들어 12_0 기능 수준에는 Direct3D 11.3 또는 Direct3D 12 및 셰이더 모델 5.1이 필요합니다. 자세한 내용은 Direct3D 기능 수준: 각 기능 수준대한 개요를 참조하세요.

기능 수준을 사용하여 Direct3D 9, Microsoft Direct3D 10 또는 Direct3D 11용 애플리케이션을 개발한 다음 9, 10 또는 11 하드웨어에서 실행할 수 있습니다(일부 예외 제외). 자세한 내용은 Direct3D 기능 수준참조하세요.

스테레오 렌더링

스테레오 렌더링은 깊이의 환상을 향상시키는 데 사용됩니다. 왼쪽 눈과 오른쪽 눈의 두 이미지를 사용하여 디스플레이 화면에 장면을 표시합니다.

수학적으로, 이를 달성하기 위해 일반 모노 프로젝션 행렬의 오른쪽과 왼쪽에 약간의 가로 오프셋인 스테레오 프로젝션 매트릭스를 적용합니다.

이 샘플 게임에서 스테레오 렌더링을 달성하기 위해 두 번의 렌더링 패스를 수행했습니다.

  • 오른쪽 렌더링 대상에 바인딩하고 오른쪽 프로젝션을 적용한 다음 기본 개체를 그립니다.
  • 왼쪽 렌더링 대상에 바인딩하고 왼쪽 프로젝션을 적용한 다음 기본 개체를 그립니다.

카메라 및 좌표 공간

게임에는 자체 좌표계(세계 공간 또는 장면 공간이라고도 함)에서 세계를 업데이트하는 코드가 있습니다. 카메라를 포함한 모든 개체는 이 공간에 배치되고 방향을 지정합니다. 자세한 내용은 좌표계참조하세요.

꼭짓점 셰이더는 다음 알고리즘을 사용하여 모델 좌표에서 디바이스 좌표로 변환하는 작업을 많이 수행합니다(여기서 V는 벡터이고 M은 행렬).

V(device) = V(model) x M(model-to-world) x M(world-to-view) x M(view-to-device)

  • M(model-to-world)은(는) 모델 좌표를 세계 좌표로 변환하는 변환 매트릭스로, World 변환 매트릭스으로도 알려져 있습니다. 원시 값에서 제공됩니다.
  • M(world-to-view)는 세계 좌표를 보기 좌표로 변환하는 변환 행렬이며, 뷰 변환 행렬라고도 합니다.
    • 이는 카메라의 뷰 매트릭스에서 제공됩니다. 이는 카메라의 위치와 관찰 벡터(카메라에서 장면으로 직접 바라보는 벡터, 그리고 위로 향해 수직으로 놓인 벡터)에 의해 정의됩니다.
    • 샘플 게임에서 m_viewMatrix 뷰 변환 매트릭스이며 Camera::SetViewParams사용하여 계산됩니다.
  • M(view-to-device)은 보기 좌표를 디바이스 좌표로 변환하는 변환 매트릭스로서, 프로젝션 변환 매트릭스로도 알려져 있습니다.
    • 이는 카메라의 프로젝션에 의해 제공됩니다. 최종 장면에서 실제로 표시되는 공간의 양에 대한 정보를 제공합니다. FoV(뷰 필드), 가로 세로 비율 및 클리핑 평면은 프로젝션 변환 행렬을 정의합니다.
    • 샘플 게임에서 m_projectionMatrixCamera::SetProjParams를 사용하여 계산된 프로젝션 좌표로의 변환을 정의합니다. 스테레오 프로젝션의 경우, 각 눈의 보기용으로 하나씩 두 개의 프로젝션 행렬을 사용합니다.

VertexShader.hlsl 셰이더 코드는 상수 버퍼의 이러한 벡터 및 행렬과 함께 로드되며 모든 꼭짓점에서 이 변환을 수행합니다.

좌표 변환

Direct3D는 세 가지 변환을 사용하여 3D 모델 좌표를 픽셀 좌표(화면 공간)로 변경합니다. 이러한 변환은 월드 변환, 뷰 변환 및 프로젝션 변환입니다. 자세한 내용은 변환 개요참조하세요.

세계 변환 행렬

월드 변환은 모델의 로컬 원점을 기준으로 꼭짓점이 정의된 모델 공간에서 장면의 모든 개체에 공통된 원점을 기준으로 꼭짓점이 정의되는 세계 공간으로 좌표를 변경합니다. 본질적으로, 세계 변환은 세계에 모델을 배치합니다. 그래서 그 이름이 붙었습니다. 자세한 내용은 World 변환참조하세요.

변환 행렬 보기

뷰 변환은 세계 공간에서 뷰어의 위치를 지정하고 꼭짓점을 카메라 공간으로 변환합니다. 카메라 공간에서 카메라 또는 관찰자는 원점에 있으며, 양의 z 방향을 바라보고 있습니다. 자세한 내용은 변환보기로 이동하시기 바랍니다.

프로젝션 변환 매트릭스

프로젝션 변환은 보기 절두체를 큐보이드 모양으로 변환합니다. 관찰 프러스텀은 뷰포트의 카메라를 기준으로 배치된 장면의 3D 볼륨입니다. 뷰포트는 3D 장면이 투영되는 2D 사각형입니다. 자세한 내용은 뷰포트 및 클리핑 참조하세요.

시야각 피라미드의 가까운 끝이 먼 끝보다 작기 때문에, 카메라에 가까운 개체들이 확장되어 보이는 효과가 있습니다. 이는 장면에 원근법이 적용되는 방식입니다. 따라서 플레이어에 가까운 개체는 더 크게 나타납니다. 더 멀리 떨어져 있는 개체는 더 작게 표시됩니다.

수학적으로, 프로젝션 변환은 일반적으로 배율 및 원근 투영 역할을 모두 하는 행렬입니다. 그것은 카메라의 렌즈처럼 작동합니다. 자세한 내용은 프로젝션 변환참고하세요.

샘플러 상태

샘플러 상태는 텍스처 주소 지정 모드, 필터링 및 세부 수준을 사용하여 텍스처 데이터를 샘플링하는 방법을 결정합니다. 샘플링은 텍스처 픽셀(또는 텍셀)을 텍스처에서 읽을 때마다 수행됩니다.

텍스처에는 텍셀 배열이 포함되어 있습니다. 각 텍셀의 위치는 (u,v)표시됩니다. 여기서 u 너비이고 v 높이이며 텍스처 너비와 높이에 따라 0에서 1 사이로 매핑됩니다. 결과 텍스처 좌표는 텍스처를 샘플링할 때 텍셀을 지정하는 데 사용됩니다.

텍스처 좌표가 0보다 작거나 1보다 큰 경우, 텍스처 주소 모드는 텍스처 좌표가 텍셀 위치를 지정하는 방법을 정의합니다. 예를 들어 TextureAddressMode.Clamp사용하는 경우 0-1 범위 밖의 모든 좌표는 샘플링 전에 최대값 1, 최소값 0으로 고정됩니다.

질감이 다각형에 비해 너무 크거나 너무 작으면 텍스처가 공간에 맞게 필터링됩니다. 배율 필터는 질감을 확대하고, 축소 필터는 더 작은 영역에 맞게 질감을 줄입니다. 텍스처 확대는 하나 이상의 주소에 대해 샘플 텍셀을 반복 사용하여 이미지가 더 흐릿하게 됩니다. 텍스처 축소는 둘 이상의 텍셀 값을 단일 값으로 결합해야 하기 때문에 더 복잡합니다. 이로 인해 텍스처 데이터에 따라 별칭 또는 톱니 모양 가장자리가 발생할 수 있습니다. 축소를 위한 가장 인기 있는 방법은 mipmap을 사용하는 것입니다. Mipmap은 다단계 텍스처입니다. 각 레벨의 크기는 1x1 텍스처에 이르기까지 이전 레벨보다 2의 제곱수만큼 작습니다. 축소를 사용하는 경우 게임은 렌더링 시간에 필요한 크기에 가장 가까운 mipmap 수준을 선택합니다.

BasicLoader 클래스

BasicLoader 디스크의 파일에서 셰이더, 텍스처 및 메시를 로드할 수 있도록 지원하는 간단한 로더 클래스입니다. 동기 메서드와 비동기 메서드를 모두 제공합니다. 이 샘플 게임에서 BasicLoader.h/.cpp 파일은 유틸리티 폴더에 있습니다.

자세한 내용은 기본 로더참조하세요.