Einrichten von DirectX-Ressourcen und Anzeigen eines Bilds
Hier zeigen wir Ihnen, wie Sie ein Direct3D-Gerät, eine Swapchain und eine Renderzielansicht erstellen und wie das gerenderte Bild auf der Anzeige dargestellt wird.
Ziel: So richten Sie DirectX-Ressourcen in einer C++-Universelle Windows-Plattform -App (UWP) ein und zeigen eine Volltonfarbe an.
Voraussetzungen
Wir gehen davon aus, dass Sie mit C++ vertraut sind. Außerdem benötigen Sie grundlegende Erfahrungen mit Grafikprogrammierungskonzepten.
Zeit bis zum Abschluss: 20 Minuten.
Anweisungen
1. Deklarieren von Direct3D-Schnittstellenvariablen mit ComPtr
Wir deklarieren Direct3D-Schnittstellenvariablen mit der smarten ComPtr-Zeigervorlage aus der Windows-Runtime C++-Vorlagenbibliothek (WRL), sodass wir die Lebensdauer dieser Variablen auf eine ausnahmesichere Weise verwalten können. Anschließend können wir diese Variablen verwenden, um auf die ComPtr-Klasse und die zugehörigen Member zuzugreifen. Zum Beispiel:
ComPtr<ID3D11RenderTargetView> m_renderTargetView;
m_d3dDeviceContext->OMSetRenderTargets(
1,
m_renderTargetView.GetAddressOf(),
nullptr // Use no depth stencil.
);
Wenn Sie ID3D11RenderTargetView mit ComPtr deklarieren, können Sie die GetAddressOf-Methode von ComPtr verwenden, um die Adresse des Zeigers auf ID3D11RenderTargetView (**ID3D11RenderTargetView) abzurufen, um an ID3D11DeviceContext::OMSetRenderTargets zu übergeben. OMSetRenderTargets bindet das Renderziel an die Ausgabezusammenführungsphase , um das Renderziel als Ausgabeziel anzugeben.
Nachdem die Beispiel-App gestartet wurde, initialisiert sie und lädt sie und kann dann ausgeführt werden.
2. Erstellen des Direct3D-Geräts
Um die Direct3D-API zum Rendern einer Szene zu verwenden, müssen wir zuerst ein Direct3D-Gerät erstellen, das den Grafikkarten darstellt. Zum Erstellen des Direct3D-Geräts rufen wir die D3D11CreateDevice-Funktion auf. Wir geben die Ebenen 9,1 bis 11,1 im Array D3D_FEATURE_LEVEL Werte an. Direct3D führt das Array in der Reihenfolge durch und gibt die höchste unterstützte Featureebene zurück. Um die höchste verfügbare Featureebene zu erhalten, werden also die D3D_FEATURE_LEVEL Arrayeinträge vom höchsten zum niedrigsten aufgeführt. Wir übergeben das flag D3D11_CREATE_DEVICE_BGRA_SUPPORT an den Flags-Parameter , damit Direct3D-Ressourcen mit Direct2D interoperiert werden. Wenn wir den Debugbuild verwenden, übergeben wir auch das D3D11_CREATE_DEVICE_DEBUG Flag. Weitere Informationen zum Debuggen von Apps finden Sie unter Verwenden der Debugebene zum Debuggen von Apps.
Wir rufen das Direct3D 11.1-Gerät (ID3D11Device1) und den Gerätekontext (ID3D11DeviceContext1) ab, indem wir den Direct3D 11-Geräte- und Gerätekontext abfragen, der von D3D11CreateDevice zurückgegeben wird.
// 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. Erstellen der Swapchain
Als Nächstes erstellen wir eine Swapchain, die das Gerät zum Rendern und Anzeigen verwendet. Wir deklarieren und initialisieren eine DXGI_SWAP_CHAIN_DESC1 Struktur zur Beschreibung der Swapchain. Anschließend richten wir die Swapchain als Flip-Modell ein (d. h. eine Swapchain, die den DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL Wert im SwapEffect-Element festgelegt hat) und legen das Format-Element auf DXGI_FORMAT_B8G8R8A8_UNORM fest. Wir legen das Count-Element der DXGI_SAMPLE_DESC-Struktur fest, das das SampleDesc-Element auf 1 und das Quality-Element von DXGI_SAMPLE_DESC auf Null angibt, da flip-modell nicht mehrere Beispiel-Antialiasing (MSAA) unterstützt. Wir legen den BufferCount-Member auf 2 fest, damit die Swapchain einen Frontpuffer verwenden kann, um dem Anzeigegerät und einem Hintergrundpuffer zu präsentieren, der als Renderziel dient.
Wir rufen das zugrunde liegende DXGI-Gerät ab, indem wir das Direct3D 11.1-Gerät abfragen. Um den Stromverbrauch zu minimieren, was für akkubetriebene Geräte wie Laptops und Tablets wichtig ist, rufen wir die IDXGIDevice1::SetMaximumFrameLatency-Methode mit 1 als maximale Anzahl von Hintergrundpufferframes auf, die DXGI in die Warteschlange stellen kann. Dadurch wird sichergestellt, dass die App erst nach dem vertikalen Leerzeichen gerendert wird.
Um schließlich die Swapchain zu erstellen, müssen wir die übergeordnete Factory vom DXGI-Gerät abrufen. Wir rufen IDXGIDevice::GetAdapter auf, um den Adapter für das Gerät abzurufen, und rufen dann IDXGIObject::GetParent auf dem Adapter auf, um die übergeordnete Factory (IDXGIFactory2) abzurufen. Zum Erstellen der Swapchain rufen wir IDXGIFactory2::CreateSwapChainForCoreWindow mit dem Swapchaindeskriptor und dem Kernfenster der App auf.
// 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. Erstellen der Renderzielansicht
Um Grafiken im Fenster zu rendern, müssen wir eine Renderzielansicht erstellen. Wir rufen IDXGISwapChain::GetBuffer auf, um den Hintergrundpuffer der Swapchain abzurufen, der beim Erstellen der Renderzielansicht verwendet werden soll. Wir geben den Hintergrundpuffer als 2D-Textur (ID3D11Texture2D) an. Zum Erstellen der Renderzielansicht rufen wir ID3D11Device::CreateRenderTargetView mit dem Hintergrundpuffer der Swapchain auf. Wir müssen angeben, dass das gesamte Kernfenster gezeichnet werden soll, indem der Ansichtsport (D3D11_VIEWPORT) als vollständige Größe des Hintergrundpuffers der Swapchain angegeben wird. Wir verwenden den Ansichtsport in einem Aufruf von ID3D11DeviceContext::RSSetViewports, um den Ansichtsport an die Rasterizerstufe der Pipeline zu binden. In der Rasterizerstufe werden Vektorinformationen in ein Rasterbild konvertiert. In diesem Fall ist keine Konvertierung erforderlich, da nur eine Volltonfarbe angezeigt wird.
// 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. Darstellen des gerenderten Bilds
Wir geben eine Endlosschleife ein, um die Szene kontinuierlich zu rendern und anzuzeigen.
In dieser Schleife rufen wir Folgendes auf:
- ID3D11DeviceContext::OMSetRenderTargets , um das Renderziel als Ausgabeziel anzugeben.
- ID3D11DeviceContext::ClearRenderTargetView , um das Renderziel auf eine Volltonfarbe zu löschen.
- IDXGISwapChain::P resent , um das gerenderte Bild im Fenster darzustellen.
Da wir zuvor die maximale Framelatenz auf 1 festgelegt haben, verlangsamt Windows die Renderschleife in der Regel auf die Bildschirmaktualisierungsrate, in der Regel etwa 60 Hz. Windows verlangsamt die Renderschleife, indem die App in den Ruhezustand wechselt, wenn die App Present aufruft. Windows macht die App in den Ruhezustand, bis der Bildschirm aktualisiert wird.
// 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. Ändern der Größe des App-Fensters und des Swapchainpuffers
Wenn sich die Größe des App-Fensters ändert, muss die App die Größe der Swapchainpuffer ändern, die Renderzielansicht neu erstellen und dann das gerenderte Bild darstellen. Um die Größe der Swapchainpuffer zu ändern, rufen wir IDXGISwapChain::ResizeBuffers auf. In diesem Aufruf lassen wir die Anzahl der Puffer und das Format der Puffer unverändert (der Parameter BufferCount auf zwei und den NewFormat-Parameter auf DXGI_FORMAT_B8G8R8A8_UNORM). Wir machen die Größe des Swapchain-Hintergrundpuffers zur gleichen Größe wie das Fenster mit geänderter Größe.We make the size of the swapchain's back buffer the same size as the resized window. Nachdem wir die Größe der Swapchainpuffer geändert haben, erstellen wir das neue Renderziel und präsentieren das neue gerenderte Bild ähnlich wie bei der Initialisierung der App.
// If the swap chain already exists, resize it.
DX::ThrowIfFailed(
m_swapChain->ResizeBuffers(
2,
0,
0,
DXGI_FORMAT_B8G8R8A8_UNORM,
0
)
);
Zusammenfassung und nächste Schritte
Wir haben ein Direct3D-Gerät, eine Swapchain und eine Renderzielansicht erstellt und das gerenderte Bild auf der Anzeige angezeigt.
Als Nächstes zeichnen wir ein Dreieck auf der Anzeige.
Erstellen von Shadern und Zeichnen von Grundtypen