Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Sprzętowo przyspieszana grafika 2D i 3D coraz częściej staje się częścią aplikacji niezwiązanych z grami, a większość aplikacji do gier używa grafiki 2D w formie menu i wyświetlaczy przeziernych (HUD). Istnieje wiele korzyści, które można uzyskać, umożliwiając efektywne połączenie tradycyjnego renderowania 2-D z renderowaniem Direct3D.
W tym temacie opisano sposób integrowania grafiki 2-D i 3-W przy użyciu technologii Direct2D i Direct3D.
Warunki wstępne
W tym omówieniu założono, że znasz podstawowe operacje rysowania Direct2D. Aby zapoznać się z samouczkiem, zobacz Tworzenie prostej aplikacji Direct2D. Przyjęto również założenie, że można programować przy użyciu Direct3D 10.1.
Obsługiwane wersje Direct3D
W przypadku wersji DirectX 11.0 funkcja Direct2D obsługuje współdziałanie tylko z urządzeniami Direct3D 10.1. W przypadku wersji DirectX 11.1 lub nowszej funkcja Direct2D obsługuje również współdziałanie z usługą Direct3D 11.
Współdziałanie za pośrednictwem DXGI
Od wersji Direct3D 10 środowisko uruchomieniowe Direct3D używa DXGI na potrzeby zarządzania zasobami. Warstwa środowiska uruchomieniowego DXGI zapewnia współużytkowanie między procesami powierzchni pamięci wideo i służy jako podstawa dla innych platform środowiska uruchomieniowego opartego na pamięci wideo. Direct2D używa DXGI do współdziałania z direct3D.
Istnieją dwa podstawowe sposoby używania funkcji Direct2D i Direct3D razem:
- Zawartość Direct2D można zapisać na powierzchni Direct3D, uzyskując IDXGISurface i używając jej z CreateDxgiSurfaceRenderTarget, aby utworzyć ID2D1RenderTarget. Następnie można użyć obiektu docelowego renderowania, aby dodać dwuwymiarowy interfejs lub tło do trójwymiarowej grafiki lub użyć rysunku Direct2D jako tekstury dla obiektu trójwymiarowego.
- Za pomocą CreateSharedBitmap można utworzyć ID2D1Bitmap z IDXGISurface, aby zapisać scenę Direct3D w mapie bitowej i renderować ją za pomocą Direct2D.
Zapisywanie na powierzchni Direct3D z celem renderowania powierzchni DXGI
Aby zapisać dane na powierzchni Direct3D, uzyskasz IDXGISurface i przekażesz ją do metody CreateDxgiSurfaceRenderTarget, aby utworzyć docelową powierzchnię renderowania DXGI. Następnie możesz użyć obiektu docelowego renderowania powierzchni DXGI, aby narysować zawartość 2-D na powierzchnię DXGI.
Obiekt docelowy renderowania powierzchni DXGI jest rodzajem ID2D1RenderTarget. Podobnie jak w przypadku innych obiektów docelowych renderowania Direct2D, można go użyć do tworzenia zasobów i wydawania poleceń rysunku.
Obiekt docelowy renderowania powierzchni DXGI i powierzchnia DXGI muszą używać tego samego formatu DXGI. Jeśli podczas tworzenia obiektu docelowego renderowania określisz format DXGI_FORMAT_UNKNOWN, zostanie on automatycznie użyty zgodnie z formatem powierzchni.
Obiekt docelowy renderowania powierzchni DXGI nie wykonuje synchronizacji powierzchni DXGI.
Tworzenie powierzchni DXGI
W wersji Direct3D 10 istnieje kilka sposobów uzyskania powierzchni DXGI. Możesz utworzyć IDXGISwapChain dla urządzenia, a następnie użyć GetBuffer metody łańcucha wymiany, aby uzyskać powierzchnię DXGI. Możesz też użyć urządzenia do utworzenia tekstury, a następnie użyć tej tekstury jako powierzchni DXGI.
Bez względu na sposób, w jaki tworzysz powierzchnię DXGI, musi ona używać jednego z formatów DXGI obsługiwanych przez docelowe powierzchnie renderujące DXGI. Aby uzyskać listę, zobacz Obsługiwane formaty pikseli i tryby alfa.
Ponadto urządzenie ID3D10Device1 skojarzone z powierzchnią DXGI musi obsługiwać formaty BGRA DXGI, aby powierzchnia działała z Direct2D. Aby zapewnić tę obsługę, użyj flagi D3D10_CREATE_DEVICE_BGRA_SUPPORT podczas wywoływania metody D3D10CreateDevice1, aby utworzyć urządzenie.
Poniższy kod definiuje metodę, która tworzy ID3D10Device1. Wybiera najlepszy dostępny poziom funkcji i wraca do zaawansowanej platformy rasteryzacji systemu Windows (WARP), gdy renderowanie sprzętowe jest niedostępne.
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;
}
W następnym przykładzie kodu użyto metody CreateD3DDevice pokazanej w poprzednim przykładzie w celu utworzenia urządzenia Direct3D, które może tworzyć powierzchnie DXGI do użycia z funkcją Direct2D.
// Create device
hr = CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_HARDWARE,
nDeviceFlags,
&pDevice
);
if (FAILED(hr))
{
hr = CreateD3DDevice(
NULL,
D3D10_DRIVER_TYPE_WARP,
nDeviceFlags,
&pDevice
);
}
Zapisywanie zawartości Direct2D w buforze łańcucha wymiany
Najprostszym sposobem dodawania zawartości Direct2D do sceny Direct3D jest użycie metody GetBuffer z IDXGISwapChain w celu uzyskania powierzchni DXGI, a następnie, używając powierzchni z metodą CreateDxgiSurfaceRenderTarget, aby utworzyć ID2D1RenderTarget, za pomocą której można narysować zawartość 2-D.
To podejście nie renderuje zawartości w trzech wymiarach; nie będzie miało perspektywy ani głębokości. Jednak jest to przydatne w przypadku kilku typowych zadań:
- Tworzenie tła 2-W dla sceny 3-W.
- Tworzenie interfejsu 2D przed sceną 3D.
- Podczas renderowania zawartości Direct2D należy użyć wieloprzykładowego trybu Direct3D.
W następnej sekcji pokazano, jak utworzyć tło 2-W dla sceny 3-W.
Przykład: Narysuj 2-wymiarowe tło
W poniższych krokach opisano, jak utworzyć obiekt docelowy renderowania powierzchni DXGI i użyć go do narysowania tła gradientu.
Użyj metody CreateSwapChain, aby utworzyć łańcuch wymiany dla ID3D10Device1 (zmienna m_pDevice). Łańcuch wymiany używa formatu DXGI DXGI_FORMAT_B8G8R8A8_UNORM, jednego z formatów DXGI obsługiwanych przez Direct2D.
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); }Użyj metody łańcucha wymiany GetBuffer, aby uzyskać powierzchnię DXGI.
// Get a surface in the swap chain hr = m_pSwapChain->GetBuffer( 0, IID_PPV_ARGS(&pBackBuffer) );Użyj powierzchni DXGI, aby utworzyć obiekt docelowy renderowania 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);Użyj celu renderowania, aby narysować gradientowe tło.
// 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(); } ...
Kod zostanie pominięty w tym przykładzie.
Używanie zawartości Direct2D jako tekstury
Innym sposobem używania zawartości Direct2D z direct3D jest użycie direct2D do wygenerowania tekstury 2-W, a następnie zastosowanie tej tekstury do modelu 3-W. W tym celu należy utworzyć ID3D10Texture2D, uzyskać powierzchnię DXGI z tekstury, a następnie użyć powierzchni do utworzenia obiektu docelowego renderowania powierzchni DXGI. Powierzchnia ID3D10Texture2D musi używać flagi powiązania D3D10_BIND_RENDER_TARGET i korzystać z formatu DXGI obsługiwanego przez obiekty docelowe renderowania powierzchni DXGI. Aby uzyskać listę obsługiwanych formatów DXGI, zobacz Obsługiwane formaty pikseli i tryby alfa.
Przykład: używanie zawartości Direct2D jako tekstury
W poniższych przykładach pokazano, jak utworzyć cel renderowania powierzchni DXGI, który renderuje do tekstury 2-D (reprezentowanej przez ID3D10Texture2D).
Najpierw użyj urządzenia Direct3D, aby utworzyć teksturę 2-W. Tekstura używa D3D10_BIND_RENDER_TARGET i D3D10_BIND_SHADER_RESOURCE flagi powiązania i używa formatu DXGI_FORMAT_B8G8R8A8_UNORM DXGI, jednego z formatów DXGI obsługiwanych przez Direct2D.
// 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);Użyj tekstury, aby uzyskać powierzchnię DXGI.
IDXGISurface *pDxgiSurface = NULL; hr = m_pOffscreenTexture->QueryInterface(&pDxgiSurface);Użyj powierzchni z metodą CreateDxgiSurfaceRenderTarget, aby uzyskać docelowy obiekt renderujący 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); }
Teraz, po uzyskaniu obiektu docelowego renderowania Direct2D i skojarzeniu go z teksturą Direct3D, można użyć obiektu docelowego renderowania, aby narysować zawartość Direct2D do tej tekstury i można zastosować tę teksturę do elementów pierwotnych Direct3D.
Kod zostanie pominięty w tym przykładzie.
Zmiana rozmiaru obiektu docelowego renderowania powierzchni DXGI
Obiekty docelowe renderowania powierzchni DXGI nie obsługują metody ID2D1RenderTarget::Resize. Aby zmienić rozmiar obiektu docelowego renderowania powierzchni DXGI, aplikacja musi go zwolnić i utworzyć ponownie.
Ta operacja może potencjalnie powodować problemy z wydajnością. Element docelowy renderowania może być ostatnim aktywnym zasobem Direct2D, który przechowuje odwołanie do ID3D10Device1 skojarzone z powierzchnią DXGI obiektu docelowego renderowania. Jeśli aplikacja zwolni obiekt docelowy renderowania, a odwołanie ID3D10Device1 zostanie zniszczone, należy go utworzyć ponownie.
Możesz uniknąć tej potencjalnie kosztownej operacji, zachowując co najmniej jeden zasób Direct2D utworzony przez obiekt docelowy renderowania podczas ponownego tworzenia tego obiektu docelowego renderowania. Poniżej przedstawiono niektóre zasoby direct2D, które działają w tym podejściu:
- ID2D1Bitmap (które mogą być przechowywane pośrednio przez ID2D1BitmapBrush)
- ID2D1Layer
- ID2D1Mesh
Aby uwzględnić to podejście, metoda zmiany rozmiaru powinna sprawdzić, czy urządzenie Direct3D jest dostępne. Jeśli jest dostępna, wydaj i ponownie utwórz obiekty docelowe renderowania powierzchni DXGI, ale zachowaj wszystkie utworzone wcześniej zasoby i użyj ich ponownie. Działa to, ponieważ, zgodnie z opisem w Resources Overview, zasoby utworzone przez dwa obiekty docelowe renderowania są zgodne, gdy oba obiekty docelowe renderowania są skojarzone z tym samym urządzeniem Direct3D.