Poznámka
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Aplikace pro Univerzální platformu Windows (UPW) může podporovat více orientací obrazovky při zpracování události DisplayInformation::OrientationChanged. V této části probereme osvědčené postupy pro zpracování rotace obrazovky v aplikaci DirectX pro UPW, aby se grafický hardware zařízení s Windows 10 efektivně a efektivně používal.
Než začnete, nezapomeňte, že grafický hardware vždy vypíše pixelová data stejným způsobem bez ohledu na orientaci zařízení. Zařízení s Windows 10 můžou určit aktuální orientaci zobrazení (s nějakým druhem senzoru nebo softwarovým přepínačem) a umožnit uživatelům změnit nastavení zobrazení. Z tohoto důvodu systém Windows 10 zpracovává otáčení obrázků, aby se zajistilo, že jsou "vzpřímené" na základě orientace zařízení. Ve výchozím nastavení aplikace obdrží oznámení, že se něco změnilo v orientaci, například velikost okna. Když k tomu dojde, Windows 10 okamžitě otočí obrázek pro konečný displej. Pro tři ze čtyř specifických orientací obrazovky (probírané později) používá Windows 10 další grafické prostředky a výpočty k zobrazení konečného obrázku.
U aplikací DirectX pro UPW poskytuje objekt DisplayInformation základní data orientace zobrazení, která může aplikace dotazovat. Výchozí orientace je na šířku, kde je šířka displeje v pixelech větší než výška; alternativní orientace je na výšku, kde je displej otočený o 90 stupňů jedním či druhým směrem a šířka se změní na menší než výška.
Windows 10 definuje čtyři specifické režimy orientace zobrazení:
- Krajina—výchozí orientace zobrazení pro Windows 10, je považována za základní úhel otáčení (0 stupňů).
- Na výšku – zobrazení bylo otočeno po směru hodinových ručiček o 90 stupňů (nebo proti směru těchto ručiček o 270 stupňů).
- Na šířku, překlopené – zobrazení bylo otočené o 180 stupňů (otočené vzhůru nohama).
- Na výšku, překlopené – displej byl otočen o 270 stupňů proti směru hodinových ručiček (nebo o 90 stupňů ve směru hodinových ručiček).
Když se displej otočí z jedné orientace do druhé, Windows 10 interně provede operaci otáčení, která zarovná nakreslený obrázek s novou orientací a uživatel uvidí na obrazovce přímou image.
Ve Windows 10 se také zobrazují animace automatického přechodu, aby se při přechodu z jedné orientace na druhou vytvořil hladký uživatelský zážitek. Když se orientace zobrazení posune, uživatel vidí tyto směny jako pevnou animaci přiblížení a otočení zobrazeného obrázku obrazovky. Windows 10 přiděluje čas aplikaci pro úpravu rozložení v nové orientaci.
Celkově se jedná o obecný proces zpracování změn v orientaci obrazovky:
- Pomocí kombinace hodnot ohraničení okna a dat orientace zobrazení udržujte swap chain vyrovnaný s nativní orientací zobrazení zařízení.
- Informujte Windows 10 o orientaci swap chainu pomocí IDXGISwapChain1::SetRotation.
- Změňte kód vykreslování tak, aby generoval obrázky zarovnané s orientací uživatele zařízení.
Změna velikosti swap chainu a předběžná rotace jeho obsahu
Pokud chcete provést základní změnu velikosti zobrazení a předem otočit její obsah v aplikaci DirectX pro UPW, implementujte tyto kroky:
- Zpracujte událost DisplayInformation::OrientationChanged.
- Přizpůsobte swap chain novým rozměrům okna.
- Proveďte volání IDXGISwapChain1::SetRotation k nastavení orientace swap řetězce.
- Znovu vytvořte jakékoliv prostředky závislé na velikosti okna, jako například cíle vykreslení a vyrovnávací paměti dat v pixelech.
Teď se na tyto kroky podíváme podrobněji.
Prvním krokem je registrace obslužné rutiny pro událost DisplayInformation::OrientationChanged. Tato událost se v aplikaci vyvolá pokaždé, když se změní orientace obrazovky, například při otočení displeje.
Chcete-li zpracovat událost DisplayInformation::OrientationChanged, připojíte obslužnou rutinu pro DisplayInformation::OrientationChanged v požadované metodě SetWindow, což je jedna z metod rozhraní IFrameworkView, které musí váš zprostředkovatel zobrazení implementovat.
V tomto příkladu kódu je obslužná rutina události pro DisplayInformation::OrientationChanged nazvaná metoda OnOrientationChanged. Když je DisplayInformation::OrientationChanged vyvolána, následně zavolá metodu SetCurrentOrientation, která pak zavolá CreateWindowSizeDependentResources.
void App::SetWindow(CoreWindow^ window)
{
// ... Other UI event handlers assigned here ...
currentDisplayInformation->OrientationChanged +=
ref new TypedEventHandler<DisplayInformation^, Object^>(this, &App::OnOrientationChanged);
// ...
}
}
void App::OnOrientationChanged(DisplayInformation^ sender, Object^ args)
{
m_deviceResources->SetCurrentOrientation(sender->CurrentOrientation);
m_main->CreateWindowSizeDependentResources();
}
// This method is called in the event handler for the OrientationChanged event.
void DX::DeviceResources::SetCurrentOrientation(DisplayOrientations currentOrientation)
{
if (m_currentOrientation != currentOrientation)
{
m_currentOrientation = currentOrientation;
CreateWindowSizeDependentResources();
}
}
Dále změníte velikost swap řetězce pro novou orientaci obrazovky a připravíte ho na otočení obsahu grafického potrubí, když je prováděno vykreslování. V tomto příkladu je DirectXBase::CreateWindowSizeDependentResources metoda, která má na starosti volání IDXGISwapChain::ResizeBuffers, nastavení 3D a 2D matice otočení, volání SetRotation a opětovné vytvoření vašich prostředků.
void DX::DeviceResources::CreateWindowSizeDependentResources()
{
// Clear the previous window size specific context.
ID3D11RenderTargetView* nullViews[] = {nullptr};
m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr);
m_d3dRenderTargetView = nullptr;
m_d2dContext->SetTarget(nullptr);
m_d2dTargetBitmap = nullptr;
m_d3dDepthStencilView = nullptr;
m_d3dContext->Flush();
// Calculate the necessary render target size in pixels.
m_outputSize.Width = DX::ConvertDipsToPixels(m_logicalSize.Width, m_dpi);
m_outputSize.Height = DX::ConvertDipsToPixels(m_logicalSize.Height, m_dpi);
// Prevent zero size DirectX content from being created.
m_outputSize.Width = max(m_outputSize.Width, 1);
m_outputSize.Height = max(m_outputSize.Height, 1);
// The width and height of the swap chain must be based on the window's
// natively-oriented width and height. If the window is not in the native
// orientation, the dimensions must be reversed.
DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation();
bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270;
m_d3dRenderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width;
m_d3dRenderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height;
if (m_swapChain != nullptr)
{
// If the swap chain already exists, resize it.
HRESULT hr = m_swapChain->ResizeBuffers(
2, // Double-buffered swap chain.
lround(m_d3dRenderTargetSize.Width),
lround(m_d3dRenderTargetSize.Height),
DXGI_FORMAT_B8G8R8A8_UNORM,
0
);
if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
// If the device was removed for any reason, a new device and swap chain will need to be created.
HandleDeviceLost();
// Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method
// and correctly set up the new device.
return;
}
else
{
DX::ThrowIfFailed(hr);
}
}
else
{
// Otherwise, create a new one using the same adapter as the existing Direct3D device.
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {0};
swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width); // Match the size of the window.
swapChainDesc.Height = lround(m_d3dRenderTargetSize.Height);
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format.
swapChainDesc.Stereo = false;
swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling.
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency.
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All UWP apps must use this SwapEffect.
swapChainDesc.Flags = 0;
swapChainDesc.Scaling = DXGI_SCALING_NONE;
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
// This sequence obtains the DXGI factory that was used to create the Direct3D device above.
ComPtr<IDXGIDevice3> dxgiDevice;
DX::ThrowIfFailed(
m_d3dDevice.As(&dxgiDevice)
);
ComPtr<IDXGIAdapter> dxgiAdapter;
DX::ThrowIfFailed(
dxgiDevice->GetAdapter(&dxgiAdapter)
);
ComPtr<IDXGIFactory2> dxgiFactory;
DX::ThrowIfFailed(
dxgiAdapter->GetParent(IID_PPV_ARGS(&dxgiFactory))
);
DX::ThrowIfFailed(
dxgiFactory->CreateSwapChainForCoreWindow(
m_d3dDevice.Get(),
reinterpret_cast<IUnknown*>(m_window.Get()),
&swapChainDesc,
nullptr,
&m_swapChain
)
);
// 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)
);
}
// Set the proper orientation for the swap chain, and generate 2D and
// 3D matrix transformations for rendering to the rotated swap chain.
// Note the rotation angle for the 2D and 3D transforms are different.
// This is due to the difference in coordinate spaces. Additionally,
// the 3D matrix is specified explicitly to avoid rounding errors.
switch (displayRotation)
{
case DXGI_MODE_ROTATION_IDENTITY:
m_orientationTransform2D = Matrix3x2F::Identity();
m_orientationTransform3D = ScreenRotation::Rotation0;
break;
case DXGI_MODE_ROTATION_ROTATE90:
m_orientationTransform2D =
Matrix3x2F::Rotation(90.0f) *
Matrix3x2F::Translation(m_logicalSize.Height, 0.0f);
m_orientationTransform3D = ScreenRotation::Rotation270;
break;
case DXGI_MODE_ROTATION_ROTATE180:
m_orientationTransform2D =
Matrix3x2F::Rotation(180.0f) *
Matrix3x2F::Translation(m_logicalSize.Width, m_logicalSize.Height);
m_orientationTransform3D = ScreenRotation::Rotation180;
break;
case DXGI_MODE_ROTATION_ROTATE270:
m_orientationTransform2D =
Matrix3x2F::Rotation(270.0f) *
Matrix3x2F::Translation(0.0f, m_logicalSize.Width);
m_orientationTransform3D = ScreenRotation::Rotation90;
break;
default:
throw ref new FailureException();
}
//SDM: only instance of SetRotation
DX::ThrowIfFailed(
m_swapChain->SetRotation(displayRotation)
);
// Create a render target view of the swap chain back buffer.
ComPtr<ID3D11Texture2D> backBuffer;
DX::ThrowIfFailed(
m_swapChain->GetBuffer(0, IID_PPV_ARGS(&backBuffer))
);
DX::ThrowIfFailed(
m_d3dDevice->CreateRenderTargetView(
backBuffer.Get(),
nullptr,
&m_d3dRenderTargetView
)
);
// Create a depth stencil view for use with 3D rendering if needed.
CD3D11_TEXTURE2D_DESC depthStencilDesc(
DXGI_FORMAT_D24_UNORM_S8_UINT,
lround(m_d3dRenderTargetSize.Width),
lround(m_d3dRenderTargetSize.Height),
1, // This depth stencil view has only one texture.
1, // Use a single mipmap level.
D3D11_BIND_DEPTH_STENCIL
);
ComPtr<ID3D11Texture2D> depthStencil;
DX::ThrowIfFailed(
m_d3dDevice->CreateTexture2D(
&depthStencilDesc,
nullptr,
&depthStencil
)
);
CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D);
DX::ThrowIfFailed(
m_d3dDevice->CreateDepthStencilView(
depthStencil.Get(),
&depthStencilViewDesc,
&m_d3dDepthStencilView
)
);
// Set the 3D rendering viewport to target the entire window.
m_screenViewport = CD3D11_VIEWPORT(
0.0f,
0.0f,
m_d3dRenderTargetSize.Width,
m_d3dRenderTargetSize.Height
);
m_d3dContext->RSSetViewports(1, &m_screenViewport);
// Create a Direct2D target bitmap associated with the
// swap chain back buffer and set it as the current target.
D2D1_BITMAP_PROPERTIES1 bitmapProperties =
D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
m_dpi,
m_dpi
);
ComPtr<IDXGISurface2> dxgiBackBuffer;
DX::ThrowIfFailed(
m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))
);
DX::ThrowIfFailed(
m_d2dContext->CreateBitmapFromDxgiSurface(
dxgiBackBuffer.Get(),
&bitmapProperties,
&m_d2dTargetBitmap
)
);
m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());
// Grayscale text anti-aliasing is recommended for all UWP apps.
m_d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
}
Po uložení aktuálních hodnot výšky a šířky okna pro příští zavolání této metody převeďte hodnoty hranic zobrazení z nezávislých pixelů zařízení (DIP) na pixely. V ukázce voláte ConvertDipsToPixels, což je jednoduchá funkce, která spouští tento kód:
floor((dips * dpi / 96.0f) + 0.5f);
Přidáte hodnotu 0,5f, abyste zajistili zaokrouhlení na nejbližší celočíselnou hodnotu.
Jen tak na okraj, souřadnice CoreWindow jsou vždy definovány v DIPy. Pro Windows 10 a starší verze Windows je DIP definován jako 1/96 palce a je v souladu s definicí operačního systému nahoru. Když se orientace zobrazení otočí do režimu na výšku, aplikace zamění šířku a výšku okna CoreWindowa odpovídajícím způsobem se musí změnit velikost cíle vykreslování (rámečky). Vzhledem k tomu, že souřadnice Direct3D jsou vždy ve fyzických pixelech, je třeba před předáním těchto hodnot do Direct3D převést hodnoty DIP z CoreWindowna celočíselné pixelové hodnoty za účelem nastavení swap řetězce.
Z hlediska procesu děláte trochu víc práce, než kdybyste jednoduše změnili rozměr swap chainu: ve skutečnosti rotujete komponenty Direct2D a Direct3D obrázku před jejich sloučením pro prezentaci a oznamujete swap chainu, že jste výsledky vykreslili v nové orientaci. Tady je trochu podrobnější popis tohoto procesu, jak je znázorněno v příkladu kódu pro DX::DeviceResources::CreateWindowSizeDependentResources:
Určete novou orientaci zobrazení. Pokud se zobrazení překlopí z šířky na výšku nebo naopak, proměňte hodnoty výšky a šířky , které se samozřejmě změní z hodnot DIP na pixely, pro hranice zobrazení.
Pak zkontrolujte, jestli vznikl řetězec swapů. Pokud ho nevytvořili, vytvořte ho voláním IDXGIFactory2::CreateSwapChainForCoreWindow. V opačném případě změňte voláním IDXGISwapchain:ResizeBuffersvelikost vyrovnávacích pamětí stávajícího přepínače na nové rozměry zobrazení. I když u události otáčení nemusíte měnit velikost řetězu prohození – vypíšete obsah, který už vykreslovací kanál otočil, existují i další události změny velikosti, jako jsou události přichycení a vyplnění, které vyžadují změnu velikosti.
Poté nastavte příslušnou 2D nebo 3D maticovou transformaci k použití na pixely nebo vrcholy (v tomto pořadí) v grafickém řetězci při jejich vykreslování do swap řetězce. Máme 4 možné matice otočení:
- orientace na šířku (DXGI_MODE_ROTATION_IDENTITY)
- portrét (DXGI_MODE_ROTATION_ROTATE270)
- na šířku, překlopené (DXGI_MODE_ROTATION_ROTATE180)
- režim na výšku, otočené (DXGI_MODE_ROTATION_ROTATE90)
Správná matice je vybrána na základě dat poskytovaných systémem Windows 10 (například výsledky DisplayInformation::OrientationChanged) pro určení orientace zobrazení a vynásobí se souřadnicemi jednotlivých pixelů (Direct2D) nebo vrcholu (Direct3D) ve scéně, čímž je efektivně otočíte tak, aby se zarovnaly na orientaci obrazovky. (Všimněte si, že v Direct2D je původ obrazovky definovaný jako levý horní roh, zatímco v Direct3D je původ definován jako logické centrum okna.)
Poznámka Další informace o prostorových transformacích používaných pro otočení a jejich definování najdete v tématu Definování matic pro otočení obrazovky (2D). Další informace o prostorových transformacích používaných pro otáčení najdete v tématu Definování matic pro otočení obrazovky (3D).
Teď je tu důležitá část: zavolejte IDXGISwapChain1::SetRotation a poskytněte svoji aktualizovanou matici otočení, jako takto:
m_swapChain->SetRotation(rotation);
Uložíte také vybranou matici otočení, do které ji metoda vykreslování získá, když vypočítá novou projekci. Tuto matici použijete při vykreslení konečné prostorové projekce nebo složeného konečného prostorového rozložení. (Automaticky se na vás nevztahuje.)
Poté vytvořte nový cíl vykreslení pro otočené 3D zobrazení a také nový hloubkový vzorníkový buffer pro toto zobrazení. Nastavte prostorové zobrazení vykreslování pro otočenou scénu pomocí volání ID3D11DeviceContext:RSSetViewports.
Pokud máte 2D obrázky, které chcete otočit nebo rozložit, vytvořte 2D cíl vykreslení jako zapisovatelný rastrový obrázek pro změněný řetězec prohození pomocí ID2D1DeviceContext::CreateBitmapFromDxgiSurface a složte nové rozložení pro aktualizovanou orientaci. Nastavte všechny vlastnosti, které potřebujete na cíl vykreslení, jako je režim anti-aliasing (jak je vidět v příkladu kódu).
Nyní zobrazte swap chain.
Snížení zpoždění otáčení pomocí CoreWindowResizeManager
Ve výchozím nastavení windows 10 poskytuje krátké, ale zřetelné časové období pro libovolnou aplikaci bez ohledu na model aplikace nebo jazyk pro dokončení otočení obrázku. Je však pravděpodobné, že když vaše aplikace provede výpočet rotace pomocí některé z technik popsaných zde, bude dokončeno dlouho před ukončením tohoto časového rámce. Chtěli byste získat tento čas zpátky a dokončit animaci otočení, že? Tady CoreWindowResizeManager přichází.
Tady je postup, jak použít CoreWindowResizeManager: když je vyvolána událost DisplayInformation::OrientationChanged, zavolejte CoreWindowResizeManager::GetForCurrentView v rámci obslužné rutiny pro tuto událost, abyste získali instanci CoreWindowResizeManager. Po dokončení a zobrazení rozložení pro novou orientaci zavolejte NotifyLayoutCompleted, abyste dali Windows vědět, že může dokončit animaci otočení a zobrazit obrazovku aplikace.
Tady je, jak může kód ve vaší obsluze události pro DisplayInformation::OrientationChanged vypadat:
CoreWindowResizeManager^ resizeManager = Windows::UI::Core::CoreWindowResizeManager::GetForCurrentView();
// ... build the layout for the new display orientation ...
resizeManager->NotifyLayoutCompleted();
Když uživatel otočí orientaci zobrazení, Windows 10 zobrazí animaci nezávislou na vaší aplikaci jako zpětnou vazbu pro uživatele. Animace má tři části, ke kterým dochází v následujícím pořadí:
- Windows 10 zmenší původní obrázek.
- Windows 10 uchovává obrázek po dobu potřebnou k opětovnému sestavení nového rozložení. Toto je časové období, které byste chtěli zkrátit, protože vaše aplikace pravděpodobně nepotřebuje všechny.
- Když vyprší platnost okna rozložení nebo když se zobrazí oznámení o dokončení rozložení, Windows obrázek otočí a pak křížově zvětší na novou orientaci.
Jak je navrhováno ve třetí odrážce, když aplikace volá NotifyLayoutCompleted, Windows 10 zastaví časový limit okna, dokončí animaci otočení a vrátí ovládací prvek do vaší aplikace, která je nyní vykreslena v nové orientaci zobrazení. Celkový efekt spočívá v tom, že vaše aplikace je nyní o něco plynulejší a citlivější a funguje o něco efektivněji!
Příloha A: Použití matic pro otočení obrazovky (2D)
V ukázkovém kódu v Změna velikosti swap řetězce a předběžnou rotaci jeho obsahu (a v příkladu rotace swapového řetězce DXGI), jste si možná všimli, že jsme měli samostatné rotační matice pro výstupy Direct2D a Direct3D. Nejprve se podíváme na 2D matice.
Existují dva důvody, proč nemůžeme použít stejné matice otočení na obsah Direct2D a Direct3D:
Jeden, používají různé kartézské souřadnicové modely. Direct2D používá pravidlo pravé ruky, kde se souřadnice y zvyšuje v kladné hodnotě, která se pohybuje směrem nahoru od počátku. Direct3D však používá levotočivé pravidlo, kde se hodnota y-ové souřadnice zvyšuje směrem doprava od počátku. Výsledkem je původ souřadnic obrazovky v levém horním rohu pro Direct2D, zatímco původ obrazovky (rovina projekce) je v levém dolním rohu pro Direct3D. (Další informace najdete o 3D souřadnicových systémech.)
Za druhé, 3D rotační matice musí být explicitně zadány, aby nedocházelo k chybám zaokrouhlování.
Řetězec prohození předpokládá, že původ je umístěn v levém dolním rohu, takže musíte provést otočení, abyste zarovnali pravostranný souřadnicový systém Direct2D s levostranným systémem používaným řetězcem prohození. Konkrétně změníte umístění obrázku pod novou orientaci levou rukou vynásobením matice rotace maticí translace pro souřadnice otočeného souřadnicového systému a transformujete obrázek ze souřadnicového prostoru CoreWindowna souřadnicový prostor řetězce výměny. Aplikace také musí tuto transformaci konzistentně použít, když je cíl vykreslení Direct2D připojený ke swap chainu. Pokud ale vaše aplikace kreslí na zprostředkující povrchy, které nejsou přidružené přímo k řetězci prohození, tuto transformaci souřadnicového prostoru nepoužívejte.
Váš kód pro výběr správné matice ze čtyř možných otočení může vypadat takto (mějte na paměti překlad do nového původu souřadnicového systému):
// Set the proper orientation for the swap chain, and generate 2D and
// 3D matrix transformations for rendering to the rotated swap chain.
// Note the rotation angle for the 2D and 3D transforms are different.
// This is due to the difference in coordinate spaces. Additionally,
// the 3D matrix is specified explicitly to avoid rounding errors.
switch (displayRotation)
{
case DXGI_MODE_ROTATION_IDENTITY:
m_orientationTransform2D = Matrix3x2F::Identity();
m_orientationTransform3D = ScreenRotation::Rotation0;
break;
case DXGI_MODE_ROTATION_ROTATE90:
m_orientationTransform2D =
Matrix3x2F::Rotation(90.0f) *
Matrix3x2F::Translation(m_logicalSize.Height, 0.0f);
m_orientationTransform3D = ScreenRotation::Rotation270;
break;
case DXGI_MODE_ROTATION_ROTATE180:
m_orientationTransform2D =
Matrix3x2F::Rotation(180.0f) *
Matrix3x2F::Translation(m_logicalSize.Width, m_logicalSize.Height);
m_orientationTransform3D = ScreenRotation::Rotation180;
break;
case DXGI_MODE_ROTATION_ROTATE270:
m_orientationTransform2D =
Matrix3x2F::Rotation(270.0f) *
Matrix3x2F::Translation(0.0f, m_logicalSize.Width);
m_orientationTransform3D = ScreenRotation::Rotation90;
break;
default:
throw ref new FailureException();
}
Jakmile budete mít správnou matici otočení a původ 2D obrázku, nastavte ji voláním ID2D1DeviceContext::SetTransform mezi voláními ID2D1DeviceContext::BeginDraw a ID2D1DeviceContext::EndDraw.
Upozornění Direct2D nemá zásobník transformace. Pokud vaše aplikace také používá ID2D1DeviceContext::SetTransform jako součást kódu výkresu, je potřeba tuto matici ponásobit na jakoukoli jinou transformaci, kterou jste použili.
ID2D1DeviceContext* context = m_deviceResources->GetD2DDeviceContext();
Windows::Foundation::Size logicalSize = m_deviceResources->GetLogicalSize();
context->SaveDrawingState(m_stateBlock.Get());
context->BeginDraw();
// Position on the bottom right corner.
D2D1::Matrix3x2F screenTranslation = D2D1::Matrix3x2F::Translation(
logicalSize.Width - m_textMetrics.layoutWidth,
logicalSize.Height - m_textMetrics.height
);
context->SetTransform(screenTranslation * m_deviceResources->GetOrientationTransform2D());
DX::ThrowIfFailed(
m_textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING)
);
context->DrawTextLayout(
D2D1::Point2F(0.f, 0.f),
m_textLayout.Get(),
m_whiteBrush.Get()
);
// Ignore D2DERR_RECREATE_TARGET here. This error indicates that the device
// is lost. It will be handled during the next call to Present.
HRESULT hr = context->EndDraw();
Při příštím předložení řetězce prohození se váš 2D obrázek otočí tak, aby odpovídal nové orientaci zobrazení.
Příloha B: Použití matic pro otáčení obrazovky (3D)
V ukázkovém kódu v Změna velikosti swap řetězce a předběžná rotace jeho obsahu (a v ukázce rotace DXGI swap řetězce ) jsme definovali specifickou transformační matici pro každou možnou orientaci obrazovky. Teď se podíváme na matice pro otáčení prostorových scén. Stejně jako předtím vytvoříte sadu matic pro každou ze 4 možných orientací. Pokud chcete zabránit chybám zaokrouhlování a tím i drobným vizuálním artefaktům, deklarujte matice explicitně ve svém kódu.
Tyto 3D matice rotace nastavíte takto. Matice zobrazené v následujícím příkladu kódu jsou standardní rotační matice pro otočení o 0, 90, 180 a 270 stupňů vrcholů, které definují body ve 3D prostoru scény kamery. Hodnota souřadnice každého vrcholu [x, y, z] ve scéně se vynásobí touto maticí otočení při výpočtu 2D projekce scény.
// 0-degree Z-rotation
static const XMFLOAT4X4 Rotation0(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
// 90-degree Z-rotation
static const XMFLOAT4X4 Rotation90(
0.0f, 1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
// 180-degree Z-rotation
static const XMFLOAT4X4 Rotation180(
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
// 270-degree Z-rotation
static const XMFLOAT4X4 Rotation270(
0.0f, -1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
);
}
Typ otáčení v řetězci přepnutí nastavíte voláním IDXGISwapChain1::SetRotation, takto:
m_swapChain->SetRotation(rotation);
Teď ve své metodě vykreslování implementujte nějaký kód podobný tomuto:
struct ConstantBuffer // This struct is provided for illustration.
{
// Other constant buffer matrices and data are defined here.
float4x4 projection; // Current matrix for projection
} ;
ConstantBuffer m_constantBufferData; // Constant buffer resource data
// ...
// Rotate the projection matrix as it will be used to render to the rotated swap chain.
m_constantBufferData.projection = mul(m_constantBufferData.projection, m_rotationTransform3D);
Když teď zavoláte metodu vykreslování, vynásobí aktuální matici otočení (jak je určená proměnnou třídy m_orientationTransform3D) s aktuální maticí projekce a přiřadí výsledky této operace jako novou projekční matici pro renderer. Použijte řetězec výměny, aby se scéna zobrazila v nové orientaci zobrazení.