Grafická vazba
Aby bylo možné používat Azure Remote Rendering ve vlastní aplikaci, je potřeba ji integrovat do kanálu vykreslování aplikace. Tato integrace je zodpovědností grafické vazby.
Po nastavení poskytuje grafická vazba přístup k různým funkcím, které ovlivňují vykreslený obrázek. Tyto funkce lze rozdělit do dvou kategorií: obecné funkce, které jsou vždy dostupné a specifické funkce, které jsou relevantní pouze pro vybraný Microsoft.Azure.RemoteRendering.GraphicsApiType
.
Grafická vazba v Unity
V Unity je celá vazba zpracována strukturou RemoteUnityClientInit
předanou do RemoteManagerUnity.InitializeManager
. Pokud chcete nastavit grafický režim, GraphicsApiType
musí být pole nastaveno na zvolenou vazbu. Pole se automaticky vyplní v závislosti na tom, jestli je k dispozici XRDevice. Toto chování lze ručně přepsat následujícím chováním:
- HoloLens 2: OpenXR nebo grafická vazba Windows Mixed Reality se používá v závislosti na aktivním modulu plug-in Unity XR.
- Desktopová aplikace pro ploché UPW: Simulace se vždy používá.
- Editor Unity: Simulace se vždy používá, pokud není připojená náhlavní souprava WMR VR, v takovém případě bude funkce ARR zakázaná, aby bylo možné ladit jiné části aplikace, které nesouvisejí s ARR. Viz také holografické vzdálené komunikace.
Jedinou další relevantní částí Unity je přístup k základní vazbě, všechny ostatní části níže je možné přeskočit.
Nastavení grafické vazby ve vlastních aplikacích
Chcete-li vybrat grafickou vazbu, proveďte následující dva kroky: Nejprve musí být grafická vazba staticky inicializována při inicializaci programu:
RemoteRenderingInitialization managerInit = new RemoteRenderingInitialization();
managerInit.GraphicsApi = GraphicsApiType.OpenXrD3D11;
managerInit.ConnectionType = ConnectionType.General;
managerInit.Right = ///...
RemoteManagerStatic.StartupRemoteRendering(managerInit);
RemoteRenderingInitialization managerInit;
managerInit.GraphicsApi = GraphicsApiType::OpenXrD3D11;
managerInit.ConnectionType = ConnectionType::General;
managerInit.Right = ///...
StartupRemoteRendering(managerInit); // static function in namespace Microsoft::Azure::RemoteRendering
Výše uvedená volání musí být volána před přístupem k jiným rozhraním API vzdáleného vykreslování.
Podobně by měla být odpovídající funkce RemoteManagerStatic.ShutdownRemoteRendering();
de-init volána poté, co jsou všechny ostatní objekty vzdáleného vykreslování již zničeny.
Aby bylo možné WMR StartupRemoteRendering
volat také před voláním libovolného holografického rozhraní API. Pro OpenXR platí totéž pro všechna rozhraní API související s OpenXR.
Přístup k grafické vazbě
Po nastavení klienta je možné získat přístup k základní grafické vazbě pomocí getteru RenderingSession.GraphicsBinding
. Například poslední statistiku rámce je možné načíst takto:
RenderingSession currentSession = ...;
if (currentSession.GraphicsBinding != null)
{
FrameStatistics frameStatistics;
if (currentSession.GraphicsBinding.GetLastFrameStatistics(out frameStatistics) == Result.Success)
{
...
}
}
ApiHandle<RenderingSession> currentSession = ...;
if (ApiHandle<GraphicsBinding> binding = currentSession->GetGraphicsBinding())
{
FrameStatistics frameStatistics;
if (binding->GetLastFrameStatistics(&frameStatistics) == Result::Success)
{
...
}
}
Grafická rozhraní API
Aktuálně existují tři grafická rozhraní API, která lze vybrat, OpenXrD3D11
WmrD3D11
a SimD3D11
. Čtvrtý z nich Headless
existuje, ale zatím není na straně klienta podporovaný.
OpenXR
GraphicsApiType.OpenXrD3D11
je výchozí vazba pro spuštění na HoloLens 2. GraphicsBindingOpenXrD3d11
Vytvoří vazbu. V tomto režimu Azure Remote Rendering vytvoří vrstvu rozhraní API OpenXR, která se integruje do modulu runtime OpenXR.
Pro přístup k odvozené grafické vazbě musí být základ GraphicsBinding
přetypován.
Aby bylo možné použít vazbu OpenXR, musíte udělat tři věci:
Zabalení vlastního souboru JSON vrstvy OpenXR
Pokud chcete používat vzdálené vykreslování s OpenXR, musí být aktivována vlastní vrstva rozhraní API OpenXR. To se provádí voláním StartupRemoteRendering
uvedeným v předchozí části. Jako předpoklad však XrApiLayer_msft_holographic_remoting.json
musí být zabalena s aplikací, aby ji bylo možné načíst. To se provádí automaticky, pokud se do projektu přidá balíček NuGet Microsoft.Azure.RemoteRendering.Cpp .
Informování vzdáleného vykreslování využitého prostoru XR
To je potřeba k zarovnání vzdáleného a místně vykresleného obsahu.
RenderingSession currentSession = ...;
ulong space = ...; // XrSpace cast to ulong
GraphicsBindingOpenXrD3d11 openXrBinding = (currentSession.GraphicsBinding as GraphicsBindingOpenXrD3d11);
if (openXrBinding.UpdateAppSpace(space) == Result.Success)
{
...
}
ApiHandle<RenderingSession> currentSession = ...;
XrSpace space = ...;
ApiHandle<GraphicsBindingOpenXrD3d11> openXrBinding = currentSession->GetGraphicsBinding().as<GraphicsBindingOpenXrD3d11>();
#ifdef _M_ARM64
if (openXrBinding->UpdateAppSpace(reinterpret_cast<uint64_t>(space)) == Result::Success)
#else
if (openXrBinding->UpdateAppSpace(space) == Result::Success)
#endif
{
...
}
Kde výše uvedený XrSpace
je ten, který používá aplikace, která definuje světový souřadnicový systém prostorů, ve kterém jsou souřadnice v rozhraní API vyjádřeny.
Vykreslení vzdáleného obrázku (OpenXR)
Na začátku každého rámce je potřeba vzdálený rámec vykreslit do zadní vyrovnávací paměti. To se provádí voláním BlitRemoteFrame
, která vyplní informace o barvě i hloubkách obou očí do aktuálně vázaného cíle vykreslení. Proto je důležité to udělat po vytvoření úplného vyrovnávací paměti zpět jako cíle vykreslení.
Upozorňující
Po rozsvícení vzdáleného obrázku do backbufferu by se místní obsah měl vykreslit pomocí techniky stereo vykreslování s jedním průchodem, například pomocí SV_RenderTargetArrayIndex. Použití jiných stereo vykreslovacích technik, jako je například vykreslení každého oka v samostatném průchodu, může vést ke snížení výkonu nebo grafickým artefaktům a mělo by se jim vyhnout.
RenderingSession currentSession = ...;
GraphicsBindingOpenXrD3d11 openXrBinding = (currentSession.GraphicsBinding as GraphicsBindingOpenXrD3d11);
openXrBinding.BlitRemoteFrame();
ApiHandle<RenderingSession> currentSession = ...;
ApiHandle<GraphicsBindingOpenXrD3d11> openXrBinding = currentSession->GetGraphicsBinding().as<GraphicsBindingOpenXrD3d11>();
openXrBinding->BlitRemoteFrame();
Windows Mixed Reality
GraphicsApiType.WmrD3D11
je dříve použitá grafická vazba ke spuštění na HoloLens 2. GraphicsBindingWmrD3d11
Vytvoří vazbu. V tomto režimu se Azure Remote Rendering připojí přímo k holografickým rozhraním API.
Pro přístup k odvozené grafické vazbě musí být základ GraphicsBinding
přetypován.
Aby bylo možné použít vazbu WMR, je potřeba udělat dvě věci:
Informování vzdáleného vykreslování o použitém souřadnicovém systému
To je potřeba k zarovnání vzdáleného a místně vykresleného obsahu.
RenderingSession currentSession = ...;
IntPtr ptr = ...; // native pointer to ISpatialCoordinateSystem
GraphicsBindingWmrD3d11 wmrBinding = (currentSession.GraphicsBinding as GraphicsBindingWmrD3d11);
if (wmrBinding.UpdateUserCoordinateSystem(ptr) == Result.Success)
{
...
}
ApiHandle<RenderingSession> currentSession = ...;
void* ptr = ...; // native pointer to ISpatialCoordinateSystem
ApiHandle<GraphicsBindingWmrD3d11> wmrBinding = currentSession->GetGraphicsBinding().as<GraphicsBindingWmrD3d11>();
if (wmrBinding->UpdateUserCoordinateSystem(ptr) == Result::Success)
{
...
}
Kde výše uvedené ptr
musí být ukazatel na nativní ABI::Windows::Perception::Spatial::ISpatialCoordinateSystem
objekt, který definuje světový souřadnicový systém prostorů, ve kterém jsou souřadnice v rozhraní API vyjádřeny.
Vykreslení vzdáleného obrázku (WMR)
Stejné aspekty jako v případě OpenXR výše platí tady. Volání rozhraní API vypadají takto:
RenderingSession currentSession = ...;
GraphicsBindingWmrD3d11 wmrBinding = (currentSession.GraphicsBinding as GraphicsBindingWmrD3d11);
wmrBinding.BlitRemoteFrame();
ApiHandle<RenderingSession> currentSession = ...;
ApiHandle<GraphicsBindingWmrD3d11> wmrBinding = currentSession->GetGraphicsBinding().as<GraphicsBindingWmrD3d11>();
wmrBinding->BlitRemoteFrame();
Simulace
GraphicsApiType.SimD3D11
je simulační vazba a pokud je vybrána, vytvoří grafickou GraphicsBindingSimD3d11
vazbu. Toto rozhraní se používá k simulaci pohybu hlavy, například v desktopové aplikaci a vykreslení monoskopického obrázku.
Pokud chcete implementovat vazbu simulace, je důležité pochopit rozdíl mezi místním fotoaparátem a vzdáleným snímkem, jak je popsáno na stránce kamery .
Jsou potřeba dvě kamery:
- Místní kamera: Tato kamera představuje aktuální pozici kamery řízenou logikou aplikace.
- Proxy kamera: Tato kamera odpovídá aktuálnímu vzdálenému snímku odeslanému serverem. Vzhledem k tomu, že mezi klientem žádajícím o rámec a jeho příchodem dochází k časovému zpoždění, vzdálený rámec je vždy trochu za pohybem místní kamery.
Základním přístupem je, že vzdálený i místní obsah se vykreslují do cíle mimo obrazovku pomocí proxy kamery. Obrázek proxy serveru se pak přeprojektuje do prostoru místní kamery, který je podrobněji vysvětlen v pozdější fázi reprojektování.
GraphicsApiType.SimD3D11
také podporuje stereoskopické vykreslování, které je potřeba povolit během InitSimulation
následujícího volání nastavení. Nastavení je trochu více zapojené a funguje takto:
Vytvoření cíle vykreslení proxy serveru
Vzdálený a místní obsah se musí vykreslit do cíle vykreslení mimo obrazovku nebo hloubky s názvem "proxy" pomocí dat z fotoaparátu proxy, která GraphicsBindingSimD3d11.Update
funkce poskytuje.
Proxy server musí odpovídat rozlišení vyrovnávací paměti back a musí být ve formátu DXGI_FORMAT_R8G8B8A8_UNORM nebo DXGI_FORMAT_B8G8R8A8_UNORM . V případě stereoskopického vykreslování musí mít textura barevného proxy serveru a pokud se používá hloubka, musí mít textura proxy hloubky dvě vrstvy pole místo jedné. Jakmile je relace připravená, GraphicsBindingSimD3d11.InitSimulation
je potřeba před připojením k ní volat:
RenderingSession currentSession = ...;
IntPtr d3dDevice = ...; // native pointer to ID3D11Device
IntPtr color = ...; // native pointer to ID3D11Texture2D
IntPtr depth = ...; // native pointer to ID3D11Texture2D
float refreshRate = 60.0f; // Monitor refresh rate up to 60hz.
bool flipBlitRemoteFrameTextureVertically = false;
bool flipReprojectTextureVertically = false;
bool stereoscopicRendering = false;
GraphicsBindingSimD3d11 simBinding = (currentSession.GraphicsBinding as GraphicsBindingSimD3d11);
simBinding.InitSimulation(d3dDevice, depth, color, refreshRate, flipBlitRemoteFrameTextureVertically, flipReprojectTextureVertically, stereoscopicRendering);
ApiHandle<RenderingSession> currentSession = ...;
void* d3dDevice = ...; // native pointer to ID3D11Device
void* color = ...; // native pointer to ID3D11Texture2D
void* depth = ...; // native pointer to ID3D11Texture2D
float refreshRate = 60.0f; // Monitor refresh rate up to 60hz.
bool flipBlitRemoteFrameTextureVertically = false;
bool flipReprojectTextureVertically = false;
bool stereoscopicRendering = false;
ApiHandle<GraphicsBindingSimD3d11> simBinding = currentSession->GetGraphicsBinding().as<GraphicsBindingSimD3d11>();
simBinding->InitSimulation(d3dDevice, depth, color, refreshRate, flipBlitRemoteFrameTextureVertically, flipReprojectTextureVertically, stereoscopicRendering);
Funkce inicializační funkce musí být k dispozici s ukazateli na nativní zařízení d3d a také na barvu a hloubkovou texturu cíle vykreslování proxy serveru. Po inicializaci RenderingSession.ConnectAsync
a Disconnect
může být volána vícekrát, GraphicsBindingSimD3d11.DeinitSimulation
ale při přepnutí na jinou relaci je potřeba nejprve volat ve staré relaci, aby GraphicsBindingSimD3d11.InitSimulation
bylo možné volat v jiné relaci.
Aktualizace smyčky vykreslování
Aktualizace smyčky vykreslování se skládá z několika kroků:
- Každý snímek před provedením vykreslování
GraphicsBindingSimD3d11.Update
se volá s aktuální transformací fotoaparátu, která se odešle na server, který se má vykreslit. Ve stejnou dobu by se vrácená transformace proxy serveru měla použít na proxy kameru, aby se vykreslovala do cíle vykreslování proxy serveru. Pokud vrácená aktualizaceSimulationUpdate.frameId
proxy serveru má hodnotu null, ještě neexistují žádná vzdálená data. V takovém případě by se místo vykreslení do cíle vykreslování proxy serveru měl vykreslit veškerý místní obsah do vyrovnávací paměti zpět přímo pomocí aktuálních dat fotoaparátu a další dva kroky se přeskočí. - Aplikace by teď měla svázat cíl vykreslení proxy serveru a volat
GraphicsBindingSimD3d11.BlitRemoteFrameToProxy
. Tím se vyplní vzdálená barva a podrobné informace do cíle vykreslení proxy serveru. Jakýkoli místní obsah se teď dá vykreslit na proxy server pomocí transformace proxy kamery. - V dalším kroku musí být vyrovnávací paměť zpět svázána jako cíl vykreslení a
GraphicsBindingSimD3d11.ReprojectProxy
volána v jakém okamžiku může být vrácena zpět vyrovnávací paměť.
RenderingSession currentSession = ...;
GraphicsBindingSimD3d11 simBinding = (currentSession.GraphicsBinding as GraphicsBindingSimD3d11);
SimulationUpdateParameters updateParameters = new SimulationUpdateParameters();
// Fill out camera data with current camera data
// (see "Simulation Update structures" section below)
...
SimulationUpdateResult updateResult = new SimulationUpdateResult();
simBinding.Update(updateParameters, out updateResult);
// Is the frame data valid?
if (updateResult.FrameId != 0)
{
// Bind proxy render target
simBinding.BlitRemoteFrameToProxy();
// Use proxy camera data to render local content
...
// Bind back buffer
simBinding.ReprojectProxy();
}
else
{
// Bind back buffer
// Use current camera data to render local content
...
}
ApiHandle<RenderingSession> currentSession;
ApiHandle<GraphicsBindingSimD3d11> simBinding = currentSession->GetGraphicsBinding().as<GraphicsBindingSimD3d11>();
SimulationUpdateParameters updateParameters;
// Fill out camera data with current camera data
// (see "Simulation Update structures" section below)
...
SimulationUpdateResult updateResult;
simBinding->Update(updateParameters, &updateResult);
// Is the frame data valid?
if (updateResult.FrameId != 0)
{
// Bind proxy render target
simBinding->BlitRemoteFrameToProxy();
// Use proxy camera data to render local content
...
// Bind back buffer
simBinding->ReprojectProxy();
}
else
{
// Bind back buffer
// Use current camera data to render local content
...
}
Struktury aktualizace simulace
Každý snímek, aktualizace smyčky vykreslení z předchozí části vyžaduje, abyste zadali rozsah parametrů kamery odpovídající místní kameře a vrátí sadu parametrů kamery, které odpovídají další dostupné kameře. Tyto dvě sady jsou zachyceny v objektech SimulationUpdateParameters
a SimulationUpdateResult
strukturách:
public struct SimulationUpdateParameters
{
public int FrameId;
public StereoMatrix4x4 ViewTransform;
public StereoCameraFov FieldOfView;
};
public struct SimulationUpdateResult
{
public int FrameId;
public float NearPlaneDistance;
public float FarPlaneDistance;
public StereoMatrix4x4 ViewTransform;
public StereoCameraFov FieldOfView;
};
Členy struktury mají následující význam:
Člen | Popis |
---|---|
FrameId | Identifikátor spojitého rámce. Nezbytné pro vstup SimulationUpdateParameters a pro každý nový rámec je potřeba průběžně inkrementovat. Pokud ještě nejsou k dispozici žádná data rámce, bude v Aplikaci SimulationUpdateResult hodnota 0. |
ViewTransform | Transformační matice zobrazení kamery zleva doprava. Pro monoskopické vykreslování je platný pouze Left člen. |
Fieldofview | Stereofonní pár zleva doprava v polích rámeček kamery v poli OpenXR zobrazení konvence zobrazení. Pro monoskopické vykreslování je platný pouze Left člen. |
PoblížPlaneDistance | vzdálenost téměř roviny použitá pro projekční matici aktuálního vzdáleného rámce. |
FarPlaneDistance | vzdálenost daleko roviny použitá pro projekční matici aktuálního vzdáleného rámce. |
Stereo-páry ViewTransform
a FieldOfView
umožňují nastavit obě hodnoty oční kamery v případě, že je povoleno stereoskopické vykreslování. Right
V opačném případě budou členové ignorováni. Jak vidíte, transformace kamery se předává jako jednoduché matice transformace 4x4, zatímco nejsou zadány žádné matice projekce. Skutečné matice se počítají interně službou Azure Remote Rendering pomocí zadaných polí zobrazení a aktuální téměř roviny a vzdálené roviny nastavené v rozhraní API Kamera Nastavení.
Vzhledem k tomu, že při požadovaném běhu můžete změnit téměř rovinu a daleko rovinu na Kamera Nastavení a služba tato nastavení použije asynchronně, každá simulaceUpdateResult také nese konkrétní blízko rovinu a daleko rovinu použitou při vykreslování odpovídajícího rámce. Tyto hodnoty roviny můžete použít k přizpůsobení matic projekce pro vykreslení místních objektů tak, aby odpovídaly vykreslování vzdáleného rámce.
A konečně, zatímco volání simulace update vyžaduje pole zobrazení v rámci konvence OpenXR z důvodu standardizace a zabezpečení algoritmů, můžete využít konverzní funkce znázorněné v následujících příkladech základního souboru struktury:
public SimulationUpdateParameters CreateSimulationUpdateParameters(int frameId, Matrix4x4 viewTransform, Matrix4x4 projectionMatrix)
{
SimulationUpdateParameters parameters = default;
parameters.FrameId = frameId;
parameters.ViewTransform.Left = viewTransform;
if (parameters.FieldOfView.Left.FromProjectionMatrix(projectionMatrix) != Result.Success)
{
// Invalid projection matrix
throw new ArgumentException("Invalid projection settings");
}
return parameters;
}
public void GetCameraSettingsFromSimulationUpdateResult(SimulationUpdateResult result, out Matrix4x4 projectionMatrix, out Matrix4x4 viewTransform, out int frameId)
{
projectionMatrix = default;
viewTransform = default;
frameId = 0;
if (result.FrameId == 0)
{
// Invalid frame data
return;
}
// Use the screenspace depth convention you expect for your projection matrix locally
if (result.FieldOfView.Left.ToProjectionMatrix(result.NearPlaneDistance, result.FarPlaneDistance, DepthConvention.ZeroToOne, out projectionMatrix) != Result.Success)
{
// Invalid field-of-view
return;
}
viewTransform = result.ViewTransform.Left;
frameId = result.FrameId;
}
SimulationUpdateParameters CreateSimulationUpdateParameters(uint32_t frameId, Matrix4x4 viewTransform, Matrix4x4 projectionMatrix)
{
SimulationUpdateParameters parameters;
parameters.FrameId = frameId;
parameters.ViewTransform.Left = viewTransform;
if (FovFromProjectionMatrix(projectionMatrix, parameters.FieldOfView.Left) != Result::Success)
{
// Invalid projection matrix
return {};
}
return parameters;
}
void GetCameraSettingsFromSimulationUpdateResult(const SimulationUpdateResult& result, Matrix4x4& projectionMatrix, Matrix4x4& viewTransform, uint32_t& frameId)
{
if (result.FrameId == 0)
{
// Invalid frame data
return;
}
// Use the screenspace depth convention you expect for your projection matrix locally
if (FovToProjectionMatrix(result.FieldOfView.Left, result.NearPlaneDistance, result.FarPlaneDistance, DepthConvention::ZeroToOne, projectionMatrix) != Result::Success)
{
// Invalid field-of-view
return;
}
viewTransform = result.ViewTransform.Left;
frameId = result.FrameId;
}
Tyto převodní funkce umožňují rychlé přepínání mezi specifikací zobrazení pole a prostým 4x4 perspektivním zobrazením v závislosti na vašich potřebách pro místní vykreslování. Tyto funkce převodu obsahují logiku ověření a vrátí chyby bez nastavení platného výsledku v případě, že vstupní matice projekce nebo vstupní pole zobrazení nejsou platná.
Dokumentace k rozhraní API
- C# RemoteManagerStatic.StartupRemoteRendering()
- C# GraphicsBinding – třída
- C# GraphicsBindingWmrD3d11 – třída
- C# GraphicsBindingSimD3d11 – třída
- C++ RemoteRenderingInitialization – struktura
- C++ GraphicsBinding – třída
- C++ GraphicsBindingWmrD3d11 – třída
- C++ GraphicsBindingSimD3d11 – třída