共用方式為


Direct2D 和 Direct3D 互作性概觀

硬體加速的 2D 和 3D 圖形正逐漸成為非遊戲應用程式的一部分,大部分的遊戲應用程式會以功能表和 Heads-Up 顯示器的形式使用 2D 圖形。 藉由讓傳統 2D 轉譯以有效率的方式與 Direct3D 轉譯混合,即可新增許多值。

本主題描述如何使用 Direct2D 和 Direct3D來整合 2D 和 3D 圖形。

先決條件

此概觀假設您已熟悉基本的 Direct2D 繪圖作業。 如需教學課程,請參閱 建立簡單的 Direct2D 應用程式。 它也假設您可以使用 Direct3D 10.1進行程序設計。

支援的 Direct3D 版本

使用 DirectX 11.0 時,Direct2D 僅支援與 Direct3D 10.1 裝置的互作性。 使用 DirectX 11.1 或更新版本時,Direct2D 也支援與 Direct3D 11 的互作性。

透過 DXGI 的互作性

自 Direct3D 10 起,Direct3D 運行時間會使用 DXGI 來管理資源。 DXGI 運行時間層提供視訊記憶體介面的跨進程共用,並做為其他視訊記憶體型運行時間平台的基礎。 Direct2D 使用 DXGI 與 Direct3D 互通。

有兩種主要方式可將 Direct2D 和 Direct3D 一起使用:

  • 您可以藉由取得 IDXGISurface,並將它與 createDxgiSurfaceRenderTarget搭配使用,以建立 ID2D1RenderTarget,將 Direct2DD 內容寫入 Direct3D 表面。 然後,您可以使用轉譯目標將二維介面或背景新增至三維圖形,或使用 Direct2D 繪圖做為三維物件的紋理。
  • 藉由使用 CreateSharedBitmap,從 IDXGISurface建立 ID2D1Bitmap,您可以將 Direct3D 場景寫入位圖,並使用 Direct2D 轉譯它。

使用 DXGI 表面的渲染目標寫入 Direct3D 表面

若要寫入 Direct3D 表面,您可以取得 IDXGISurface,並將它傳遞給 CreateDxgiSurfaceRenderTarget 方法來建立 DXGI 表面轉譯目標。 然後,您可以使用 DXGI 表面轉譯目標,將 2D 內容繪製到 DXGI 表面。

DXGI 表面轉譯目標是一種 ID2D1RenderTarget。 如同其他 Direct2D 轉譯目標,您可以使用它來建立資源並發出繪圖命令。

DXGI 表面轉譯目標和 DXGI 介面必須使用相同的 DXGI 格式。 如果您在建立轉譯目標時指定 DXGI_FORMAT_UNKOWN 格式,它會自動使用表面的格式。

DXGI 表面轉譯目標不會執行 DXGI 表面同步處理。

建立 DXGI 介面

使用 Direct3D 10 時,有數種方式可以取得 DXGI 表面。 您可以為裝置建立 IDXGISwapChain,然後使用交換鏈結的 GetBuffer 方法來取得 DXGI 表面。 或者,您可以使用裝置來建立紋理,然後使用該紋理做為 DXGI 表面。

無論您如何建立 DXGI 表面,介面都必須使用 DXGI 介面轉譯目標所支援的其中一種 DXGI 格式。 如需清單,請參閱 支援的像素格式和 Alpha 模式

此外,與 DXGI 介面相關聯的 ID3D10Device1 必須支援 BGRA DXGI 格式,讓介面使用 Direct2D。 若要確保此支援,請在呼叫 D3D10CreateDevice1 方法來建立裝置時,請使用 D3D10_CREATE_DEVICE_BGRA_SUPPORT 旗標。

下列程式碼定義了一個方法,用來建立 ID3D10Device1。 它會選取可用的最佳功能層級,並在無法使用硬體轉譯時,切換回 Windows 進階點陣化平臺 (WARP)

HRESULT DXGISampleApp::CreateD3DDevice(
    IDXGIAdapter *pAdapter,
    D3D10_DRIVER_TYPE driverType,
    UINT flags,
    ID3D10Device1 **ppDevice
    )
{
    HRESULT hr = S_OK;

    static const D3D10_FEATURE_LEVEL1 levelAttempts[] =
    {
        D3D10_FEATURE_LEVEL_10_0,
        D3D10_FEATURE_LEVEL_9_3,
        D3D10_FEATURE_LEVEL_9_2,
        D3D10_FEATURE_LEVEL_9_1,
    };

    for (UINT level = 0; level < ARRAYSIZE(levelAttempts); level++)
    {
        ID3D10Device1 *pDevice = NULL;
        hr = D3D10CreateDevice1(
            pAdapter,
            driverType,
            NULL,
            flags,
            levelAttempts[level],
            D3D10_1_SDK_VERSION,
            &pDevice
            );

        if (SUCCEEDED(hr))
        {
            // transfer reference
            *ppDevice = pDevice;
            pDevice = NULL;
            break;
        }
    }

    return hr;
}

下一個程式代碼範例會使用上一個範例所示的 CreateD3DDevice 方法來建立 Direct3D 裝置,以建立 DXGI 介面以搭配 Direct2D 使用。

// Create device
hr = CreateD3DDevice(
    NULL,
    D3D10_DRIVER_TYPE_HARDWARE,
    nDeviceFlags,
    &pDevice
    );

if (FAILED(hr))
{
    hr = CreateD3DDevice(
        NULL,
        D3D10_DRIVER_TYPE_WARP,
        nDeviceFlags,
        &pDevice
        );
}

將 Direct2D 內容寫入交換鏈結緩衝區

將 Direct2D 內容新增至 Direct3D 場景的最簡單方式,就是使用 IDXGISwapChainGetBuffer 方法來取得 DXGI 介面,然後使用這個介面搭配 CreateDxgiSurfaceRenderTarget 方法來建立用來繪製 2D 內容的 ID2D1RenderTarget

此方法不會以三個維度呈現您的內容;它不會有透視或深度。 不過,它適用於數個常見工作:

  • 為 3D 場景建立 2D 背景。
  • 在 3D 場景前面建立 2D 介面。
  • 在轉譯 Direct2D 內容時使用 Direct3D 多重取樣。

下一節說明如何為 3D 場景建立 2D 背景。

範例:繪製 2D 背景

下列步驟說明如何建立 DXGI 表面轉譯目標,並用它來繪製漸層背景。

  1. 使用 CreateSwapChain 方法來建立 ID3D10Device1 的交換鏈結(m_pDevice 變數)。 交換鏈結使用 DXGI_FORMAT_B8G8R8A8_UNORM DXGI 格式,這是 Direct2D 支援的其中一種 DXGI 格式。

    if (SUCCEEDED(hr))
    {
        hr = pDevice->QueryInterface(&m_pDevice);
    }
    if (SUCCEEDED(hr))
    {
        hr = pDevice->QueryInterface(&pDXGIDevice);
    }
    if (SUCCEEDED(hr))
    {
        hr = pDXGIDevice->GetAdapter(&pAdapter);
    }
    if (SUCCEEDED(hr))
    {
        hr = pAdapter->GetParent(IID_PPV_ARGS(&pDXGIFactory));
    }
    if (SUCCEEDED(hr))
    {
        DXGI_SWAP_CHAIN_DESC swapDesc;
        ::ZeroMemory(&swapDesc, sizeof(swapDesc));
    
        swapDesc.BufferDesc.Width = nWidth;
        swapDesc.BufferDesc.Height = nHeight;
        swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
        swapDesc.BufferDesc.RefreshRate.Numerator = 60;
        swapDesc.BufferDesc.RefreshRate.Denominator = 1;
        swapDesc.SampleDesc.Count = 1;
        swapDesc.SampleDesc.Quality = 0;
        swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        swapDesc.BufferCount = 1;
        swapDesc.OutputWindow = m_hwnd;
        swapDesc.Windowed = TRUE;
    
        hr = pDXGIFactory->CreateSwapChain(m_pDevice, &swapDesc, &m_pSwapChain);
    }
    
  2. 使用交換鏈結的 GetBuffer 方法來取得 DXGI 表面。

    // Get a surface in the swap chain
    hr = m_pSwapChain->GetBuffer(
        0,
        IID_PPV_ARGS(&pBackBuffer)
        );
    
  3. 使用 DXGI 曲面來建立 DXGI 渲染目標。

    // Initialize *hwnd* with the handle of the window displaying the rendered content.
    HWND hwnd;
    
    // Create the DXGI Surface Render Target.
    float dpi = GetDpiForWindow(hwnd);
    
    D2D1_RENDER_TARGET_PROPERTIES props =
        D2D1::RenderTargetProperties(
            D2D1_RENDER_TARGET_TYPE_DEFAULT,
            D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
            dpiX,
            dpiY);
    
    // Create a Direct2D render target that can draw into the surface in the swap chain
    
    hr = m_pD2DFactory->CreateDxgiSurfaceRenderTarget(
        pBackBuffer,
        &props,
        &m_pBackBufferRT);
    
  4. 使用渲染目標來繪製漸層背景。

    // Swap chain will tell us how big the back buffer is
    DXGI_SWAP_CHAIN_DESC swapDesc;
    hr = m_pSwapChain->GetDesc(&swapDesc);
    
    if (SUCCEEDED(hr))
    {
        // Draw a gradient background.
        if (m_pBackBufferRT)
        {
            D2D1_SIZE_F targetSize = m_pBackBufferRT->GetSize();
    
            m_pBackBufferRT->BeginDraw();
    
            m_pBackBufferGradientBrush->SetTransform(
                D2D1::Matrix3x2F::Scale(targetSize)
                );
    
            D2D1_RECT_F rect = D2D1::RectF(
                0.0f,
                0.0f,
                targetSize.width,
                targetSize.height
                );
    
            m_pBackBufferRT->FillRectangle(&rect, m_pBackBufferGradientBrush);
    
            hr = m_pBackBufferRT->EndDraw();
        }
    ...
    

此範例會省略程序代碼。

使用 Direct2D 內容做為紋理

搭配 Direct3D 使用 Direct2D 內容的另一種方式是使用 Direct2D 來產生 2D 紋理,然後將該紋理套用至 3D 模型。 您可以藉由建立 ID3D10Texture2D,然後從該紋理獲取 DXGI 表面,再利用此表面來建立 DXGI 表面渲染目標,來完成此過程。 ID3D10Texture2D 介面必須使用 D3D10_BIND_RENDER_TARGET 系結旗標,並使用 DXGI 介面轉譯目標支援的 DXGI 格式。 如需支援的 DXGI 格式清單,請參閱 支援的像素格式和 Alpha 模式

範例:使用 Direct2D 內容做為紋理

下列範例示範如何建立轉譯成 2D 紋理的 DXGI 表面轉譯目標(以 ID3D10Texture2D表示)。

  1. 首先,使用 Direct3D 裝置來建立 2D 紋理。 紋理會使用 D3D10_BIND_RENDER_TARGETD3D10_BIND_SHADER_RESOURCE 綁定標誌,並使用 DXGI_FORMAT_B8G8R8A8_UNORM DXGI 格式,這是 Direct2D 支援的其中一種 DXGI 格式。

    // Allocate an offscreen D3D surface for D2D to render our 2D content into
    D3D10_TEXTURE2D_DESC texDesc;
    texDesc.ArraySize = 1;
    texDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
    texDesc.CPUAccessFlags = 0;
    texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    texDesc.Height = 512;
    texDesc.Width = 512;
    texDesc.MipLevels = 1;
    texDesc.MiscFlags = 0;
    texDesc.SampleDesc.Count = 1;
    texDesc.SampleDesc.Quality = 0;
    texDesc.Usage = D3D10_USAGE_DEFAULT;
    
    hr = m_pDevice->CreateTexture2D(&texDesc, NULL, &m_pOffscreenTexture);
    
  2. 使用紋理來取得 DXGI 表面。

    IDXGISurface *pDxgiSurface = NULL;
    
    hr = m_pOffscreenTexture->QueryInterface(&pDxgiSurface);
    
  3. 使用 surface 搭配 CreateDxgiSurfaceRenderTarget 方法來取得 Direct2D 渲染目標。

    if (SUCCEEDED(hr))
    {
        // Create a D2D render target that can draw into our offscreen D3D
        // surface. Given that we use a constant size for the texture, we
        // fix the DPI at 96.
        D2D1_RENDER_TARGET_PROPERTIES props =
            D2D1::RenderTargetProperties(
                D2D1_RENDER_TARGET_TYPE_DEFAULT,
                D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED),
                96,
                96);
    
        hr = m_pD2DFactory->CreateDxgiSurfaceRenderTarget(
                pDxgiSurface,
                &props,
                &m_pRenderTarget);
    }
    

既然您已經取得 Direct2D 轉譯目標,並與 Direct3D 紋理建立關聯,您可以使用轉譯目標將 Direct2D 內容繪製到該紋理,而且您可以將該紋理套用至 Direct3D 基本類型。

此範例會省略程序代碼。

調整 DXGI 表面的渲染目標大小

DXGI 表面轉譯目標不支援 ID2D1RenderTarget::Resize 方法。 若要調整 DXGI 表面轉譯目標的大小,應用程式必須釋放並重新建立它。

此作業可能會導致效能問題。 轉譯目標可能是最後一個使用中的 Direct2D 資源,其會保留與轉譯目標的 DXGI 表面相關聯的 ID3D10Device1 參考。 如果應用程式釋放轉譯目標,且 ID3D10Device1 參考已終結,則必須重新建立新的轉譯目標。

您可以在重新建立轉譯目標時,保留至少一個由轉譯目標建立的 Direct2D 資源,以避免這種潛在的昂貴作業。 以下是一些適用於此方法的 Direct2D 資源:

若要配合此方法,調整大小的方法應該測試 Direct3D 裝置是否可用。 如果可用,請釋出並重新建立 DXGI 表面轉譯目標,但保留先前建立的所有資源,並重複使用它們。 這是因為,如 資源概觀中所述,當兩個轉譯目標都與相同的 Direct3D 裝置相關聯時,由兩個轉譯目標所建立的資源相容。

支援的像素格式和 Alpha 模式

CreateDxgiSurfaceRenderTarget

Windows DirectX 圖形