Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Uw UWP-app (Universal Windows Platform) kan meerdere schermstanden ondersteunen wanneer u de gebeurtenis DisplayInformation::OrientationChanged afhandelt. Hier bespreken we aanbevolen procedures voor het afhandelen van schermrotatie in uw UWP DirectX-app, zodat de grafische hardware van het Windows 10-apparaat efficiënt en effectief wordt gebruikt.
Voordat u begint, moet u er rekening mee houden dat grafische hardware altijd pixelgegevens op dezelfde manier uitvoert, ongeacht de afdrukstand van het apparaat. Windows 10-apparaten kunnen hun huidige weergavestand bepalen (met een bepaalde sensor of met een software-wisselknop) en gebruikers toestaan de weergave-instellingen te wijzigen. Als gevolg hiervan verwerkt Windows 10 zelf de rotatie van de afbeeldingen om ervoor te zorgen dat ze 'rechtop' zijn op basis van de afdrukstand van het apparaat. Uw app ontvangt standaard de melding dat er iets is gewijzigd in de oriëntatie, zoals de grootte van een venster. Als dit gebeurt, draait Windows 10 de afbeelding onmiddellijk om de uiteindelijke weergave. Voor drie van de vier specifieke schermstanden (later besproken), maakt Windows 10 gebruik van extra grafische bronnen en berekeningen om de uiteindelijke afbeelding weer te geven.
Voor UWP DirectX-apps biedt het DisplayInformation-object basisgegevens voor weergavestanden waarop uw app een query kan uitvoeren. De standaardstand is liggend, waarbij de pixelbreedte van het scherm groter is dan de hoogte; de alternatieve stand is staand, waarbij de weergave 90 graden in beide richtingen wordt gedraaid en de breedte kleiner wordt dan de hoogte.
In Windows 10 worden vier specifieke weergave-instellingen gedefinieerd:
- Liggend: de standaardweergavestand voor Windows 10 en wordt beschouwd als de basis- of identiteitshoek voor rotatie (0 graden).
- Staand: het beeldscherm is 90 graden rechtsom gedraaid (of 270 graden linksom).
- Het scherm in landschapsmodus is 180 graden gedraaid (ondersteboven).
- Staand, gespiegeld— het beeldscherm is 270 graden rechtsom gedraaid (of 90 graden linksom).
Wanneer de weergave van de ene richting naar de andere draait, voert Windows 10 intern een draaibewerking uit om de getekende afbeelding uit te lijnen met de nieuwe afdrukstand en ziet de gebruiker een rechtop staande afbeelding op het scherm.
In Windows 10 worden ook automatische overgangsanimaties weergegeven om een soepele gebruikerservaring te creëren bij het verschuiven van de ene richting naar de andere. Naarmate de weergavestand verandert, ziet de gebruiker deze verschuivingen als een vaste zoom- en draaianimatie van de weergegeven schermafbeelding. Tijd wordt door Windows 10 aan de app toegekend voor de indeling in de nieuwe oriëntatie.
Over het algemeen is dit het algemene proces voor het afhandelen van wijzigingen in de schermstand:
- Gebruik een combinatie van de venstergrenswaarden en de weergaverichtingsgegevens om de wisselketen uitgelijnd te houden met de systeemeigen weergavestand van het apparaat.
- Stel Windows 10 op de hoogte van de stand van de wisselketen met behulp van IDXGISwapChain1::SetRotation.
- Wijzig de renderingcode om afbeeldingen te genereren die zijn afgestemd op de gebruikersstand van het apparaat.
De swapketen aanpassen en de inhoud voorafgaand draaien
Als u het formaat van een standaardweergave wilt wijzigen en de inhoud ervan vooraf wilt draaien in uw UWP DirectX-app, implementeert u deze stappen:
- De gebeurtenis DisplayInformation::OrientationChanged verwerken.
- Pas de grootte van de swap chain aan de nieuwe afmetingen van het venster aan.
- Roep IDXGISwapChain1::SetRotation- aan om de stand van de wisselketen in te stellen.
- Maak resources die afhankelijk zijn van de venstergrootte opnieuw aan, zoals uw rendertargets en andere pixelgegevensbuffers.
Laten we deze stappen nu eens nader bekijken.
De eerste stap is het registreren van een handler voor de gebeurtenis DisplayInformation::OrientationChanged. Deze gebeurtenis wordt in uw app gegenereerd telkens wanneer de schermstand verandert, bijvoorbeeld wanneer het scherm wordt gedraaid.
Als u de gebeurtenis DisplayInformation::OrientationChanged wilt afhandelen, verbindt u uw handler voor DisplayInformation::OrientationChanged in de vereiste methode SetWindow. Dit is een van de methoden van de IFrameworkView interface die uw weergaveprovider moet implementeren.
In dit codevoorbeeld is de gebeurtenis-handler voor DisplayInformation::OrientationChanged een methode met de naam OnOrientationChanged. Wanneer DisplayInformation::OrientationChanged wordt gegenereerd, wordt op zijn beurt een methode aangeroepen met de naam SetCurrentOrientation die vervolgens CreateWindowSizeDependentResourcesaanroept.
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();
}
}
Vervolgens past u het formaat van de swap chain aan voor de nieuwe schermoriëntatie en bereidt u deze voor om de inhoud van de grafische pijplijn te roteren wanneer de rendering wordt uitgevoerd. In dit voorbeeld is DirectXBase::CreateWindowSizeDependentResources een methode voor het aanroepen van IDXGISwapChain::ResizeBuffers, het instellen van een 3D- en een 2D-draaiingsmatrix, het aanroepen van SetRotation en het opnieuw maken van uw resources.
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);
}
Nadat u de huidige hoogte- en breedtewaarden van het venster hebt opgeslagen voor de volgende keer dat deze methode wordt aangeroepen, converteert u de onafhankelijke pixelwaarden (DIP) van het apparaat voor de weergavegrenzen naar pixels. In het voorbeeld roept u ConvertDipsTo Pixelsaan. Dit is een eenvoudige functie waarmee deze code wordt uitgevoerd:
floor((dips * dpi / 96.0f) + 0.5f);
U voegt de 0,5f toe om ervoor te zorgen dat de waarde wordt afgerond op het dichtstbijzijnde gehele getal.
Afgezien daarvan worden CoreWindow coördinaten altijd gedefinieerd in DIPs. Voor Windows 10 en eerdere versies van Windows wordt een DIP gedefinieerd als 1/96e van een inch en afgestemd op de definitie van het besturingssysteem van up. Wanneer de weergave-oriëntatie wordt gedraaid naar portretmodus, worden de breedte en hoogte van de CoreWindowgewisseld en moet de grootte van het renderdoel (grenzen) dienovereenkomstig worden gewijzigd. Omdat de coördinaten van Direct3D altijd in fysieke pixels staan, moet u converteren van CoreWindowDIP-waarden naar gehele pixelwaarden voordat u deze waarden doorgeeft aan Direct3D om de wisselketen in te stellen.
Bij het proces doet u iets meer werk dan u zou doen als u gewoon de grootte van de swapchain wijzigt: u draait in feite de Direct2D- en Direct3D-componenten van uw afbeelding voordat u deze samenstelt voor presentatie, en u vertelt de swapchain dat u de resultaten in een nieuwe oriëntatie hebt weergegeven. Hier is wat meer detail over dit proces, zoals weergegeven in het codevoorbeeld voor DX::DeviceResources::CreateWindowSizeDependentResources:
Bepaal de nieuwe stand van het scherm. Als het scherm is gedraaid van liggend naar staand of omgekeerd, wissel dan de hoogte- en breedtewaarden voor de schermgrenzen, eerst omgezet van DIP-waarden naar pixels, natuurlijk.
Controleer vervolgens of de swap chain is aangemaakt. Als het nog niet is aangemaakt, maak het dan aan door IDXGIFactory2::CreateSwapChainForCoreWindowaan te roepen. Wijzig anders de grootte van de buffers van de bestaande swap chain zodat ze passen bij de nieuwe weergavedimensies, door IDXGISwapchain:ResizeBuffersaan te roepen. Hoewel u het formaat van de wisselketen voor de rotatiegebeurtenis niet hoeft te wijzigen, voert u de inhoud uit die al door uw renderingpijplijn is geroteerd, toch zijn er andere wijzigingen in grootte, zoals uitlijnings- en vulgebeurtenissen, waarvoor het formaat moet worden gewijzigd.
Daarna stelt u de juiste 2D- of 3D-matrixtransformatie in om toe te passen op de pixels of de hoekpunten, respectievelijk, in de grafische pijplijn wanneer ze worden gerenderd naar de swap chain. We hebben 4 mogelijke rotatiematrices:
- landschap (DXGI_MODE_ROTATION_IDENTITY)
- portret (gedraaid naar 270 graden)
- liggend, gedraaid (DXGI_MODE_ROTATION_ROTATE180)
- staand, gedraaid (DXGI_MODE_ROTATION_ROTATE90)
De juiste matrix wordt geselecteerd op basis van de gegevens van Windows 10 (zoals de resultaten van DisplayInformation::OrientationChanged) voor het bepalen van de weergavestand, en deze worden vermenigvuldigd met de coördinaten van elke pixel (Direct2D) of hoekpunt (Direct3D) in de scène, waardoor ze effectief worden gedraaid om uit te lijnen op de afdrukstand van het scherm. (Houd er rekening mee dat in Direct2D de oorsprong van het scherm is gedefinieerd als de linkerbovenhoek, terwijl in Direct3D de oorsprong is gedefinieerd als het logische midden van het venster.)
Opmerking Zie Matrices definiëren voor schermrotaties (2D)voor meer informatie over de 2D-transformaties die worden gebruikt voor rotatie en het definiëren van deze transformaties. Zie Matrices definiëren voor schermrotaties (3D)voor meer informatie over de 3D-transformaties die worden gebruikt voor rotatie.
Dit is nu het belangrijke deel: roep IDXGISwapChain1::SetRotation aan en geef deze op met uw bijgewerkte rotatiematrix, zoals dit:
m_swapChain->SetRotation(rotation);
U slaat ook de geselecteerde draaiingsmatrix op, waar de rendermethode deze kan ophalen wanneer de nieuwe projectie wordt berekend. U gebruikt deze matrix wanneer u de uiteindelijke 3D-projectie weergeeft of uw uiteindelijke 2D-indeling samenstelt. (Deze wordt niet automatisch voor u toegepast.)
Maak daarna een nieuw render-target voor de gedraaide 3-D-weergave, evenals een nieuwe dieptestencilbuffer voor de weergave. Stel de 3D-rendering-viewport voor de gedraaide scène in door ID3D11DeviceContext:RSSetViewportsaan te roepen.
Als u ten slotte 2D-afbeeldingen hebt om te draaien of indelen, maakt u een 2D-renderdoel als schrijfbare bitmap voor de swapketen met behulp van ID2D1DeviceContext::CreateBitmapFromDxgiSurface en stelt u uw nieuwe indeling samen voor de bijgewerkte oriëntatie. Stel alle eigenschappen in die u nodig hebt voor het renderdoel, zoals de antialiasmodus (zoals in het codevoorbeeld).
Presenteer nu de wisselketen.
De rotatievertraging verminderen met CoreWindowResizeManager
Windows 10 biedt standaard een kort maar merkbaar tijdvenster voor elke app, ongeacht het app-model of de taal, om de draaiing van de afbeelding te voltooien. De kans is echter groot dat wanneer uw app de rotatieberekening uitvoert met behulp van een van de technieken die hier worden beschreven, dit goed wordt gedaan voordat dit tijdvenster is gesloten. U wilt die tijd terughalen en de rotatieanimatie voltooien, toch? Daar komt CoreWindowResizeManager binnen.
U kunt als volgt CoreWindowResizeManagergebruiken: wanneer een DisplayInformation::OrientationChanged gebeurtenis wordt uitgelokt, roept u CoreWindowResizeManager::GetForCurrentView aan binnen de handler voor de gebeurtenis om een exemplaar van CoreWindowResizeManager te verkrijgen en, wanneer de indeling voor de nieuwe oriëntatie is voltooid en gepresenteerd, roept u de NotifyLayoutCompleted aan om Windows te laten weten dat het de rotatieanimatie kan voltooien en het app-scherm kan weergeven.
Hier ziet de code in uw gebeurtenis-handler voor DisplayInformation::OrientationChanged er als volgt uit:
CoreWindowResizeManager^ resizeManager = Windows::UI::Core::CoreWindowResizeManager::GetForCurrentView();
// ... build the layout for the new display orientation ...
resizeManager->NotifyLayoutCompleted();
Wanneer een gebruiker de oriëntatie van het scherm draait, wordt in Windows 10 een animatie weergegeven, onafhankelijk van uw app, als feedback voor de gebruiker. Er zijn drie delen voor die animatie die in de volgende volgorde plaatsvinden:
- Windows 10 verkleint de oorspronkelijke afbeelding.
- Windows 10 bevat de afbeelding voor de tijd die nodig is om de nieuwe indeling opnieuw op te bouwen. Dit is het tijdvenster dat u wilt verminderen, omdat uw app waarschijnlijk niet alles nodig heeft.
- Wanneer het indelingsvenster verloopt of wanneer een melding van voltooiing van de indeling wordt ontvangen, draait Windows de afbeelding en past vervolgens een crossfade-zoom toe naar de nieuwe oriëntatie.
Zoals wordt voorgesteld in het derde opsommingsteken, wanneer een app NotifyLayoutCompletedaanroept, stopt Windows 10 de timeout periode, voltooit de rotatieanimatie en geeft de controle terug aan uw app, die nu in de nieuwe weergaveoriëntatie tekent. Het algehele effect is dat uw app nu iets vloeiender en responsief aanvoelt en iets efficiënter werkt.
Bijlage A: matrices toepassen voor schermrotatie (2D)
In de voorbeeldcode in het formaat van de wisselketen wijzigen en de inhoud vooraf roteren (en in het voorbeeld van de DXGI-wisselketenrotatie), hebt u misschien gemerkt dat we afzonderlijke rotatiematrices hadden voor Direct2D-uitvoer en Direct3D-uitvoer. Laten we eerst de 2D-matrices bekijken.
Er zijn twee redenen waarom we dezelfde rotatiematrices niet kunnen toepassen op Direct2D- en Direct3D-inhoud:
Een, ze gebruiken verschillende Cartesische coördinaatmodellen. Direct2D maakt gebruik van de rechtshandige regel, waarbij de y-coördinaat in positieve waarde toeneemt terwijl hij omhoog gaat vanaf de oorsprong. Direct3D maakt echter gebruik van de linkshandige regel, waarbij de y-coördinaat toeneemt in positieve waarde rechts van de oorsprong. Het resultaat is de oorsprong van de schermcoördinaten in de linkerbovenhoek voor Direct2D, terwijl de oorsprong van het scherm (het projectievlak) linksonder voor Direct3D staat. (Zie 3D-coördinaatsystemen voor meer informatie.)
Twee, de 3D-rotatiematrices moeten expliciet worden opgegeven om afrondingsfouten te voorkomen.
Bij de wisselketen wordt ervan uitgegaan dat de oorsprong zich in de linkerbenedenhoek bevindt, dus u moet het Direct2D-coördinaatsysteem roteren om het rechthandige systeem uit te lijnen met het linkshandige systeem dat door de wisselketen wordt gebruikt. U herpositioneert specifiek de afbeelding onder de nieuwe linkshandige stand door de rotatiematrix te vermenigvuldigen met een translatiematrix voor de oorsprong van het geroteerde coördinatensysteem, en de afbeelding te transformeren van de coördinaatruimte van CoreWindownaar de coördinaatruimte van de wisselketen. Uw app moet deze transformatie ook consistent toepassen wanneer het Direct2D-renderdoel is verbonden met de wisselketen. Als uw app echter tekent op tussenliggende oppervlakken die niet rechtstreeks aan de wisselketen zijn gekoppeld, past u deze coördinaatruimtetransformatie niet toe.
De code voor het selecteren van de juiste matrix uit de vier mogelijke rotaties kan er als volgt uitzien (let op de vertaling naar de nieuwe oorsprong van het coördinaatsysteem):
// 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();
}
Nadat u de juiste draaiingsmatrix en oorsprong voor de 2D-afbeelding hebt, stelt u deze in met een aanroep naar ID2D1DeviceContext::SetTransform tussen uw aanroepen naar ID2D1DeviceContext::BeginDraw en ID2D1DeviceContext::EndDraw.
Waarschuwing Direct2D geen transformatiestack heeft. Als uw app ook gebruikmaakt van de ID2D1DeviceContext::SetTransform- als onderdeel van de tekencode, moet deze matrix worden vermenigvuldigd met elke andere transformatie die u hebt toegepast.
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();
De volgende keer dat u de wisselketen presenteert, wordt uw 2D-afbeelding geroteerd zodat deze overeenkomt met de nieuwe weergavestand.
Bijlage B: matrices toepassen voor schermrotatie (3D)
In de voorbeeldcode in Het formaat van de swap chain wijzigen en de inhoud vooraf draaien (en in het voorbeeld van de DXGI-swap chain-rotatie), hebben we een specifieke transformatiematrix gedefinieerd voor elke mogelijke schermoriëntatie. Laten we kijken naar de matrices voor het roteren van 3D-scenes. Net als voorheen maakt u een set matrices voor elk van de vier mogelijke oriëntaties. Als u afrondingsfouten en dus kleine visuele artefacten wilt voorkomen, declareert u de matrices expliciet in uw code.
U stelt deze 3D-rotatiematrices als volgt in. De matrices die in het volgende codevoorbeeld worden weergegeven, zijn standaard rotatiematrices voor 0, 90, 180 en 270 graden van de hoekpunten die punten definiëren in de 3D-scèneruimte van de camera. De coördinaatwaarde van elk hoekpunt [x, y, z] in de scène wordt vermenigvuldigd met deze draaimatrix wanneer de 2D-projectie van de scène wordt berekend.
// 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
);
}
U stelt het rotatietype voor de wisselketen in met een aanroep naar IDXGISwapChain1::SetRotation, zoals deze:
m_swapChain->SetRotation(rotation);
Implementeer nu in uw rendermethode een aantal code die er ongeveer als volgt uit ziet:
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);
Wanneer u nu de rendermethode aanroept, wordt de huidige draaiingsmatrix (zoals opgegeven door de klassevariabele m_orientationTransform3D) vermenigvuldigd met de huidige projectiematrix en worden de resultaten van die bewerking toegewezen als de nieuwe projectiematrix voor uw renderer. Presenteer de wisselketen om de scène in de bijgewerkte weergavestand te zien.