共用方式為


設定 DirectX 資源並顯示影像

在本文中,我們將說明如何建立 Direct3D 裝置、交換鏈結和轉譯目標檢視,以及如何將轉譯的影像呈現至顯示器。

目標: 在 C++ 通用 Windows 平台 (UWP) 應用程式中設定 DirectX 資源,以及顯示純色。

必要條件

我們假設您已熟悉 C++ 語言。 此外,您對於圖形程式設計概念也需要有基本的認識。

完成時間: 20 分鐘。

指示

1. 使用 ComPtr 宣告 Direct3D 介面變數

我們會使用 Windows 執行階段 C++ 範本庫 (WRL) 的 ComPtr 智慧型指標範本宣告 Direct3D 介面變數,因此即使有例外狀況也能安全地管理這些變數的存留期。 接著,我們可使用這些變數存取 ComPtr 類別 及其成員。 例如:

    ComPtr<ID3D11RenderTargetView> m_renderTargetView;
    m_d3dDeviceContext->OMSetRenderTargets(
        1,
        m_renderTargetView.GetAddressOf(),
        nullptr // Use no depth stencil.
        );

如果您使用 ComPtr 宣告 ID3D11RenderTargetView,則可使用 ComPtr 的 GetAddressOf 方法取得 ID3D11RenderTargetView (**ID3D11RenderTargetView) 指標的位址,以傳遞至 ID3D11DeviceContext::OMSetRenderTargetsOMSetRenderTargets 會將轉譯目標繫結至輸出合併階段,以將轉譯目標指定為輸出目標。

範例應用程式啟動後會初始化並載入,然後準備好執行。

2. 建立 Direct3D 裝置

如要使用 Direct3D API 轉譯場景,我們必須先建立代表顯示卡的 Direct3D 裝置。 若要建立 Direct3D 裝置,我們要呼叫 D3D11CreateDevice 函式。 我們指定 D3D_FEATURE_LEVEL 值陣列中的層級 9.1 到 11.1。 Direct3D 會依序逐步處理陣列,並傳回支援的最高功能層級。 因此,為取得可用的最高功能層級,我們會從最高到最低列出 D3D_FEATURE_LEVEL 陣列項目。 我們會將 D3D11_CREATE_DEVICE_BGRA_SUPPORT 旗標傳遞至 Flags 參數,讓 Direct3D 資源與 Direct2D 互通。 如果我們使用偵錯組建,則也會傳遞 D3D11_CREATE_DEVICE_DEBUG 旗標。 如需有關偵錯應用程式的詳細資訊,請參閱<使用偵錯層偵錯應用程式>。

我們會透過查詢 D3D11CreateDevice 傳回的 Direct3D 11 裝置和裝置內容,取得 Direct3D 11.1 裝置 (ID3D11Device1) 和裝置內容 (ID3D11DeviceContext1)。

        // First, create the Direct3D device.

        // This flag is required in order to enable compatibility with Direct2D.
        UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;

#if defined(_DEBUG)
        // If the project is in a debug build, enable debugging via SDK Layers with this flag.
        creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

        // This array defines the ordering of feature levels that D3D should attempt to create.
        D3D_FEATURE_LEVEL featureLevels[] =
        {
            D3D_FEATURE_LEVEL_11_1,
            D3D_FEATURE_LEVEL_11_0,
            D3D_FEATURE_LEVEL_10_1,
            D3D_FEATURE_LEVEL_10_0,
            D3D_FEATURE_LEVEL_9_3,
            D3D_FEATURE_LEVEL_9_1
        };

        ComPtr<ID3D11Device> d3dDevice;
        ComPtr<ID3D11DeviceContext> d3dDeviceContext;
        DX::ThrowIfFailed(
            D3D11CreateDevice(
                nullptr,                    // Specify nullptr to use the default adapter.
                D3D_DRIVER_TYPE_HARDWARE,
                nullptr,                    // leave as nullptr if hardware is used
                creationFlags,              // optionally set debug and Direct2D compatibility flags
                featureLevels,
                ARRAYSIZE(featureLevels),
                D3D11_SDK_VERSION,          // always set this to D3D11_SDK_VERSION
                &d3dDevice,
                nullptr,
                &d3dDeviceContext
                )
            );

        // Retrieve the Direct3D 11.1 interfaces.
        DX::ThrowIfFailed(
            d3dDevice.As(&m_d3dDevice)
            );

        DX::ThrowIfFailed(
            d3dDeviceContext.As(&m_d3dDeviceContext)
            );

3. 建立交換鏈結

接下來,我們會建立可讓裝置用於轉譯和顯示的交換鏈結。 我們會宣告並初始化 DXGI_SWAP_CHAIN_DESC1 結構,以描述交換鏈結。 接著,我們將交換鏈結設為翻轉模型 (也就是在 SwapEffect 成員中已設定 DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL 值的交換鏈結),然後將 Format 成員設為 DXGI_FORMAT_B8G8R8A8_UNORM。 我們將 DXGI_SAMPLE_DESC 結構 (由 SampleDesc 成員指定) 的 Count 成員設為 1,並將 DXGI_SAMPLE_DESCQuality 成員設為 0,因為翻轉模型不支援多重取樣消除鋸齒 (MSAA) 功能。 我們將 BufferCount 成員設為 2,這樣交換鏈結即可使用前端緩衝區呈現給顯示裝置,並使用作為轉譯目標的後端緩衝區。

我們會透過查詢 Direct3D 11.1 裝置取得基礎 DXGI 裝置。 降低耗電對電池供電裝置 (例如:膝上型電腦和平板電腦) 而言很重要,為將耗電量降到最低,我們會呼叫 IDXGIDevice1::SetMaximumFrameLatency 方法,其中,DXGI 可排入佇列的後端緩衝區畫面格數上限為 1。 這可確保應用程式只在垂直空白 (vertical blank) 後才轉譯。

最後,若要建立交換鏈結,我們需要從 DXGI 裝置取得上層處理站。 我們呼叫 IDXGIDevice::GetAdapter 以取得裝置介面卡,然後呼叫介面卡上的 IDXGIObject::GetParent 以取得上層處理站 (IDXGIFactory2)。 若要建立交換鏈結,我們會使用交換鏈結描述項和應用程式的核心視窗呼叫 IDXGIFactory2::CreateSwapChainForCoreWindow

            // If the swap chain does not exist, create it.
            DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};

            swapChainDesc.Stereo = false;
            swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
            swapChainDesc.Scaling = DXGI_SCALING_NONE;
            swapChainDesc.Flags = 0;

            // Use automatic sizing.
            swapChainDesc.Width = 0;
            swapChainDesc.Height = 0;

            // This is the most common swap chain format.
            swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;

            // Don't use multi-sampling.
            swapChainDesc.SampleDesc.Count = 1;
            swapChainDesc.SampleDesc.Quality = 0;

            // Use two buffers to enable the flip effect.
            swapChainDesc.BufferCount = 2;

            // We recommend using this swap effect for all applications.
            swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;


            // Once the swap chain description is configured, it must be
            // created on the same adapter as the existing D3D Device.

            // First, retrieve the underlying DXGI Device from the D3D Device.
            ComPtr<IDXGIDevice2> dxgiDevice;
            DX::ThrowIfFailed(
                m_d3dDevice.As(&dxgiDevice)
                );

            // Ensure that DXGI does not queue more than one frame at a time. This both reduces
            // latency and ensures that the application will only render after each VSync, minimizing
            // power consumption.
            DX::ThrowIfFailed(
                dxgiDevice->SetMaximumFrameLatency(1)
                );

            // Next, get the parent factory from the DXGI Device.
            ComPtr<IDXGIAdapter> dxgiAdapter;
            DX::ThrowIfFailed(
                dxgiDevice->GetAdapter(&dxgiAdapter)
                );

            ComPtr<IDXGIFactory2> dxgiFactory;
            DX::ThrowIfFailed(
                dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
                );

            // Finally, create the swap chain.
            CoreWindow^ window = m_window.Get();
            DX::ThrowIfFailed(
                dxgiFactory->CreateSwapChainForCoreWindow(
                    m_d3dDevice.Get(),
                    reinterpret_cast<IUnknown*>(window),
                    &swapChainDesc,
                    nullptr, // Allow on all displays.
                    &m_swapChain
                    )
                );

4. 建立轉譯目標檢視

若要將圖形轉譯至視窗,我們需要建立轉譯目標檢視。 我們呼叫 IDXGISwapChain::GetBuffer,取得交換鏈結的後端緩衝區,以在建立轉譯目標檢視時使用。 我們將後端緩衝區指定為 2D 紋理 (ID3D11Texture2D)。 若要建立轉譯目標檢視,我們會使用交換鏈結的後端緩衝區呼叫 ID3D11Device::CreateRenderTargetView。 我們必須將檢視區 (D3D11_VIEWPORT) 指定為完整大小的交換鏈結後端緩衝區,以繪製至整個核心視窗。 我們會在對 ID3D11DeviceContext::RSSetViewports 的呼叫中使用檢視區,以將檢視區繫結至管線的轉譯器階段。 轉譯器階段會將向量資訊轉換成點陣影像。 在此案例中,我們只是要顯示純色,因此不需要轉換。

        // Once the swap chain is created, create a render target view.  This will
        // allow Direct3D to render graphics to the window.

        ComPtr<ID3D11Texture2D> backBuffer;
        DX::ThrowIfFailed(
            m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
            );

        DX::ThrowIfFailed(
            m_d3dDevice->CreateRenderTargetView(
                backBuffer.Get(),
                nullptr,
                &m_renderTargetView
                )
            );


        // After the render target view is created, specify that the viewport,
        // which describes what portion of the window to draw to, should cover
        // the entire window.

        D3D11_TEXTURE2D_DESC backBufferDesc = {0};
        backBuffer->GetDesc(&backBufferDesc);

        D3D11_VIEWPORT viewport;
        viewport.TopLeftX = 0.0f;
        viewport.TopLeftY = 0.0f;
        viewport.Width = static_cast<float>(backBufferDesc.Width);
        viewport.Height = static_cast<float>(backBufferDesc.Height);
        viewport.MinDepth = D3D11_MIN_DEPTH;
        viewport.MaxDepth = D3D11_MAX_DEPTH;

        m_d3dDeviceContext->RSSetViewports(1, &viewport);

5. 呈現轉譯的影像

我們進入無限迴圈,以持續轉譯和顯示場景。

在此迴圈中,我們呼叫:

  1. ID3D11DeviceContext::OMSetRenderTargets:將轉譯目標指定為輸出目標。
  2. ID3D11DeviceContext::ClearRenderTargetView:將轉譯目標清除為純色。
  3. IDXGISwapChain::Present:將轉譯的影像呈現至視窗。

由於我們先前將畫面格延遲上限設為 1,Windows 一般會將轉譯迴圈減緩至螢幕重新整理率,通常是大約 60 Hz。 應用程式呼叫 Present 時,Windows 會讓應用程式進入睡眠狀態,以減緩轉譯迴圈速度。 Windows 會讓應用程式進入睡眠狀態,直到螢幕重新整理。

        // Enter the render loop.  Note that UWP apps should never exit.
        while (true)
        {
            // Process events incoming to the window.
            m_window->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);

            // Specify the render target we created as the output target.
            m_d3dDeviceContext->OMSetRenderTargets(
                1,
                m_renderTargetView.GetAddressOf(),
                nullptr // Use no depth stencil.
                );

            // Clear the render target to a solid color.
            const float clearColor[4] = { 0.071f, 0.04f, 0.561f, 1.0f };
            m_d3dDeviceContext->ClearRenderTargetView(
                m_renderTargetView.Get(),
                clearColor
                );

            // Present the rendered image to the window.  Because the maximum frame latency is set to 1,
            // the render loop will generally be throttled to the screen refresh rate, typically around
            // 60 Hz, by sleeping the application on Present until the screen is refreshed.
            DX::ThrowIfFailed(
                m_swapChain->Present(1, 0)
                );
        }

6. 調整應用程式視窗和交換鏈結緩衝區的大小

如果應用程式視窗大小已變更,則應用程式必須調整交換鏈結緩衝區的大小,並重新建立轉譯目標檢視,然後呈現已調整大小的轉譯影像。 若要調整交換鏈結緩衝區的大小,我們會呼叫 IDXGISwapChain::ResizeBuffers。 在此呼叫中,緩衝區數目和緩衝區格式保持不變 (BufferCount 參數為 2;NewFormat 參數為 DXGI_FORMAT_B8G8R8A8_UNORM)。 我們要讓交換鏈結後端緩衝區的大小與重新調整的視窗大小相同。 調整交換鏈結的緩衝區大小之後,我們會建立新的轉譯目標,並在初始化應用程式時,以類似的方法呈現新的轉譯影像。

            // If the swap chain already exists, resize it.
            DX::ThrowIfFailed(
                m_swapChain->ResizeBuffers(
                    2,
                    0,
                    0,
                    DXGI_FORMAT_B8G8R8A8_UNORM,
                    0
                    )
                );

摘要和後續步驟

我們已建立 Direct3D 裝置、交換鏈結和轉譯目標檢視,並將轉譯的影像呈現至顯示器。

接下來,我們也會在顯示器上繪製三角形。

建立著色器及繪製基本項目