Tutorial: Integrieren von Remote Rendering in eine HoloLens Holographic-App
In diesem Lernprogramm lernen Sie Folgendes:
- Verwenden von Visual Studio zum Erstellen einer Holographic-App, die in HoloLens bereitgestellt werden kann
- Hinzufügen der erforderlichen Codeausschnitte und Projekteinstellungen, um lokales Rendering mit remote gerenderten Inhalten zu kombinieren
Dieses Tutorial legt den Schwerpunkt darauf, einem Beispiel für eine native Holographic App
den erforderlichen Code hinzuzufügen, um lokales Rendering mit Azure Remote Rendering zu kombinieren. Die einzige Art Statusfeedback in dieser App erfolgt über den Debugausgabebereich in Visual Studio, es empfiehlt sich daher, das Beispiel in Visual Studio zu starten. Das Hinzufügen eines ordnungsgemäßen in die App integrierten Feedbacks würde den Rahmen dieses Beispiels sprengen, da das Erstellen eines dynamischen Bereichs für die Texteingabe von Grund auf eine Menge neu zu erstellenden Code mit sich bringt. Ein guter Ausgangspunkt ist die Klasse StatusDisplay
, die Teil des Remoting Player-Beispielprojekts auf GitHub ist. Tatsächlich verwendet die vorkonfigurierte Version dieses Tutorials eine lokale Kopie dieser Klasse.
Tipp
Das ARR-Beispielrepository enthält das Ergebnis dieses Tutorials als gebrauchsfertiges Visual Studio-Projekt. Es ist außerdem dank der UI-Klasse StatusDisplay
mit ordnungsgemäßer Fehler- und Statusberichterstattung ausgestattet. Im Tutorial wird der Bereich aller ARR-spezifischen Ergänzungen durch #ifdef USE_REMOTE_RENDERING
/ #endif
definiert, die Remote Rendering-Erweiterungen sind daher leicht zu erkennen.
Voraussetzungen
Für dieses Tutorial benötigen Sie Folgendes:
- Ihre Kontoinformationen (Konto-ID, Kontoschlüssel, Kontodomäne, Abonnement-ID). Falls Sie über kein Konto verfügen, erstellen Sie ein Konto.
- Windows SDK 10.0.18362.0 (Download).
- Die aktuellste Version von Visual Studio 2022 (Download).
- Visual Studio-Tools für Mixed Reality. Insbesondere sind die folgenden Workloadinstallationen obligatorisch:
- Desktopentwicklung mit C++
- Entwicklung für die universelle Windows-Plattform (UWP)
- Die Windows Mixed Reality-App-Vorlagen für Visual Studio (Download).
Erstellen eines neuen Beispiels für eine Holographic-App
Als ersten Schritt erstellen wir ein Standardbeispiel, das die Basis für die Integration von Remote Rendering bildet. Öffnen Sie Visual Studio, wählen Sie „Neues Projekt erstellen“ aus, und suchen Sie nach „DirectX 11 Holographic-App (Universal Windows) (C++/WinRT)“.
Geben Sie einen Projektnamen Ihrer Wahl ein, wählen Sie einen Pfad und dann die Schaltfläche „Erstellen“ aus. Ändern Sie im neuen Projekt die Konfiguration in „Debug/ARM64“ . Sie sollten jetzt in der Lage sein, das Projekt zu kompilieren und auf einem verbundenen HoloLens 2-Gerät bereitzustellen. Wenn Sie es auf HoloLens ausführen, sollten Sie vor sich einen rotierenden Würfel sehen.
Hinzufügen von Remote Rendering-Abhängigkeiten mithilfe von NuGet
Der erste Schritt beim Hinzufügen von Remote Rendering-Funktionen besteht darin, die clientseitigen Abhängigkeiten hinzuzufügen. Die relevanten Abhängigkeiten sind als NuGet-Paket verfügbar. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt, und wählen Sie im Kontextmenü NuGet-Pakete verwalten...“ aus.
Navigieren Sie in dem nun angezeigten Dialogfeld zum NuGet-Paket mit dem Namen „Microsoft.Azure.RemoteRendering.Cpp“ :
und fügen Sie es dem Projekt hinzu, indem Sie das Paket auswählen und dann auf die Schaltfläche „Installieren“ klicken.
Das NuGet-Paket fügt dem Projekt die Remote Rendering-Abhängigkeiten hinzu. Dies gilt insbesondere in folgenden Fällen:
- Link zur Clientbibliothek (RemoteRenderingClient.lib)
- Einrichten der DLL-Abhängigkeiten
- Festlegen des richtigen Pfads, um das Verzeichnis einzuschließen.
Projektvorbereitung
Wir müssen geringfügige Änderungen am vorhandenen Projekt vornehmen. Diese Änderungen sind klein, aber ohne sie würde das Remote Rendering nicht funktionieren.
Aktivieren des Multithreadschutzes auf dem DirectX-Gerät
Auf dem DirectX11
-Gerät muss Multithreadschutz aktiviert sein. Um diese Einstellung zu ändern, öffnen Sie die Datei „DeviceResources.cpp“ im Ordner „Common“, und fügen Sie am Ende der Funktion DeviceResources::CreateDeviceResources()
den folgenden Code ein:
// Enable multi thread protection as now multiple threads use the immediate context.
Microsoft::WRL::ComPtr<ID3D11Multithread> contextMultithread;
if (context.As(&contextMultithread) == S_OK)
{
contextMultithread->SetMultithreadProtected(true);
}
Aktivieren von Netzwerkfunktionen im App-Manifest
Netzwerkfunktionen müssen für die bereitgestellte App explizit aktiviert werden. Wird dies nicht konfiguriert, führen Verbindungsabfragen sonst letztlich zu Timeouts. Doppelklicken Sie zum Aktivieren auf das package.appxmanifest
-Element im Projektmappen-Explorer. Wechseln Sie auf dem nächsten Bildschirm zur Registerkarte Funktionen, und wählen Sie Folgendes aus:
- Internet (Client und Server)
- Internet (Client)
Integrieren von Remote Rendering
Da das Projekt jetzt vorbereitet ist, können wir mit dem Code beginnen. Ein guter Einstiegspunkt in die Anwendung ist die Klasse HolographicAppMain
(Datei „HolographicAppMain.h/cpp“), da sie über alle notwendigen Hooks für Initialisierung, De-Initialisierung und Rendering verfügt.
Includes
Wir beginnen, indem wir die erforderlichen Includes hinzufügen. Fügen Sie der Datei „HolographicAppMain.h“ das folgende Include hinzu:
#include <AzureRemoteRendering.h>
...und diese zusätzlichen include
-Anweisungen zur Datei „ HolographicAppMain.cpp“:
#include <AzureRemoteRendering.inl>
#include <RemoteRenderingExtensions.h>
#include <windows.perception.spatial.h>
Um den Code einfach zu halten, definieren wir die folgende Namespaceverknüpfung oben in der Datei „HolographicAppMain.h“ nach den include
-Anweisungen:
namespace RR = Microsoft::Azure::RemoteRendering;
Diese Verknüpfung ist nützlich, damit wir nicht überall den gesamten Namespace auszuschreiben brauchen und die ARR-spezifischen Datenstrukturen besser erkennen können. Natürlich könnten wir auch die using namespace...
-Anweisung verwenden.
Initialisierung von Remote Rendering
Wir müssen während der Lebensdauer der Anwendung ein paar Objekte für die Sitzung usw. aufbewahren. Die Lebensdauer fällt mit der Lebensdauer des HolographicAppMain
-Objekts der Anwendung zusammen, wir fügen unsere Objekte daher der Klasse HolographicAppMain
als Member hinzu. Der nächste Schritt besteht darin, der Datei „HolographicAppMain.h“ die folgenden Klassenmember hinzuzufügen:
class HolographicAppMain
{
...
// members:
std::string m_sessionOverride; // if we have a valid session ID, we specify it here. Otherwise a new one is created
RR::ApiHandle<RR::RemoteRenderingClient> m_client; // the client instance
RR::ApiHandle<RR::RenderingSession> m_session; // the current remote rendering session
RR::ApiHandle<RR::RenderingConnection> m_api; // the API instance, that is used to perform all the actions. This is just a shortcut to m_session->Connection()
RR::ApiHandle<RR::GraphicsBindingWmrD3d11> m_graphicsBinding; // the graphics binding instance
}
Ein guter Ort zum Vornehmen der eigentlichen Implementierung ist der Konstruktor der Klasse HolographicAppMain
. Wir müssen hier drei Arten von Initialisierung vornehmen:
- Die einmalige Initialisierung des Remote Rendering-Systems
- Clienterstellung (Authentifizierung)
- Sitzungserstellung
All dies erledigen wir nacheinander im Konstruktor. In echten Anwendungsfällen kann es aber sinnvoll sein, diese Schritte einzeln auszuführen.
Fügen Sie den folgenden Code am Anfang des Konstruktor-Textkörpers in der Datei „HolographicAppMain.cpp“ hinzu:
HolographicAppMain::HolographicAppMain(std::shared_ptr<DX::DeviceResources> const& deviceResources) :
m_deviceResources(deviceResources)
{
// 1. One time initialization
{
RR::RemoteRenderingInitialization clientInit;
clientInit.ConnectionType = RR::ConnectionType::General;
clientInit.GraphicsApi = RR::GraphicsApiType::WmrD3D11;
clientInit.ToolId = "<sample name goes here>"; // <put your sample name here>
clientInit.UnitsPerMeter = 1.0f;
clientInit.Forward = RR::Axis::NegativeZ;
clientInit.Right = RR::Axis::X;
clientInit.Up = RR::Axis::Y;
if (RR::StartupRemoteRendering(clientInit) != RR::Result::Success)
{
// something fundamental went wrong with the initialization
throw std::exception("Failed to start remote rendering. Invalid client init data.");
}
}
// 2. Create Client
{
// Users need to fill out the following with their account data and model
RR::SessionConfiguration init;
init.AccountId = "00000000-0000-0000-0000-000000000000";
init.AccountKey = "<account key>";
init.RemoteRenderingDomain = "westus2.mixedreality.azure.com"; // <change to the region that the rendering session should be created in>
init.AccountDomain = "westus2.mixedreality.azure.com"; // <change to the region the account was created in>
m_modelURI = "builtin://Engine";
m_sessionOverride = ""; // If there is a valid session ID to re-use, put it here. Otherwise a new one is created
m_client = RR::ApiHandle(RR::RemoteRenderingClient(init));
}
// 3. Open/create rendering session
{
auto SessionHandler = [&](RR::Status status, RR::ApiHandle<RR::CreateRenderingSessionResult> result)
{
if (status == RR::Status::OK)
{
auto ctx = result->GetContext();
if (ctx.Result == RR::Result::Success)
{
SetNewSession(result->GetSession());
}
else
{
SetNewState(AppConnectionStatus::ConnectionFailed, ctx.ErrorMessage.c_str());
}
}
else
{
SetNewState(AppConnectionStatus::ConnectionFailed, "failed");
}
};
// If we had an old (valid) session that we can recycle, we call async function m_client->OpenRenderingSessionAsync
if (!m_sessionOverride.empty())
{
m_client->OpenRenderingSessionAsync(m_sessionOverride, SessionHandler);
SetNewState(AppConnectionStatus::CreatingSession, nullptr);
}
else
{
// create a new session
RR::RenderingSessionCreationOptions init;
init.MaxLeaseInMinutes = 10; // session is leased for 10 minutes
init.Size = RR::RenderingSessionVmSize::Standard;
m_client->CreateNewRenderingSessionAsync(init, SessionHandler);
SetNewState(AppConnectionStatus::CreatingSession, nullptr);
}
}
// Rest of constructor code:
...
}
Der Code ruft die Memberfunktionen SetNewSession
und SetNewState
auf, die Sie im nächsten Absatz zusammen mit dem restlichen Code des Zustandsautomaten implementieren.
Beachten Sie, dass die Anmeldeinformationen im Beispiel hartcodiert sind und an Ort und Stelle eingegeben werden müssen (Konto-ID, Kontoschlüssel, Kontodomäne und Remote Rendering-Domäne).
Am Ende des Destruktor-Textkörpers nehmen wir die De-Initialisierung symmetrisch und in umgekehrter Reihenfolge vor:
HolographicAppMain::~HolographicAppMain()
{
// Existing destructor code:
...
// Destroy session:
if (m_session != nullptr)
{
m_session->Disconnect();
m_session = nullptr;
}
// Destroy front end:
m_client = nullptr;
// One-time de-initialization:
RR::ShutdownRemoteRendering();
}
Zustandsautomat
Beim Remote Rendering sind wichtige Funktionen, etwa das Erstellen einer Sitzung und das Laden eines Modells, asynchrone Funktionen. Um dem Rechnung zu tragen, benötigen wir einen einfachen Zustandsautomaten, der den Übergang durch die folgenden Zustände im Wesentlichen automaisch durchläuft:
Initialisierung -> Sitzungserstellung -> Sitzungsstart -> Laden von Modellen (mit Statusanzeige)
Entsprechend fügen wir der Klasse im nächsten Schritt Routinen zur Behandlung des Zustandsautomaten hinzu. Wir deklarieren eine eigene Enumeration AppConnectionStatus
für die verschiedenen Zustände, in denen sich unsere Anwendung befinden kann. Sie ist der RR::ConnectionStatus
ähnlich, weist aber einen zusätzlichen Zustand für fehlgeschlagene Verbindungen auf.
Fügen Sie der Klassendeklaration die folgenden Member und Funktionen hinzu:
namespace HolographicApp
{
// Our application's possible states:
enum class AppConnectionStatus
{
Disconnected,
CreatingSession,
StartingSession,
Connecting,
Connected,
// error state:
ConnectionFailed,
};
class HolographicAppMain
{
...
// Member functions for state transition handling
void OnConnectionStatusChanged(RR::ConnectionStatus status, RR::Result error);
void SetNewState(AppConnectionStatus state, const char* statusMsg);
void SetNewSession(RR::ApiHandle<RR::RenderingSession> newSession);
void StartModelLoading();
// Members for state handling:
// Model loading:
std::string m_modelURI;
RR::ApiHandle<RR::LoadModelAsync> m_loadModelAsync;
// Connection state machine:
AppConnectionStatus m_currentStatus = AppConnectionStatus::Disconnected;
std::string m_statusMsg;
RR::Result m_connectionResult = RR::Result::Success;
RR::Result m_modelLoadResult = RR::Result::Success;
bool m_isConnected = false;
bool m_sessionStarted = false;
RR::ApiHandle<RR::SessionPropertiesAsync> m_sessionPropertiesAsync;
bool m_modelLoadTriggered = false;
float m_modelLoadingProgress = 0.f;
bool m_modelLoadFinished = false;
double m_timeAtLastRESTCall = 0;
bool m_needsCoordinateSystemUpdate = true;
}
Fügen Sie der CPP-Datei implementierungsseitig diese Funktionstextkörper hinzu:
void HolographicAppMain::StartModelLoading()
{
m_modelLoadingProgress = 0.f;
RR::LoadModelFromSasOptions options;
options.ModelUri = m_modelURI.c_str();
options.Parent = nullptr;
// start the async model loading
m_api->LoadModelFromSasAsync(options,
// completed callback
[this](RR::Status status, RR::ApiHandle<RR::LoadModelResult> result)
{
m_modelLoadResult = RR::StatusToResult(status);
m_modelLoadFinished = true;
if (m_modelLoadResult == RR::Result::Success)
{
RR::Double3 pos = { 0.0, 0.0, -2.0 };
result->GetRoot()->SetPosition(pos);
}
},
// progress update callback
[this](float progress)
{
// progress callback
m_modelLoadingProgress = progress;
m_needsStatusUpdate = true;
});
}
void HolographicAppMain::SetNewState(AppConnectionStatus state, const char* statusMsg)
{
m_currentStatus = state;
m_statusMsg = statusMsg ? statusMsg : "";
// Some log for the VS output panel:
const char* appStatus = nullptr;
switch (state)
{
case AppConnectionStatus::Disconnected: appStatus = "Disconnected"; break;
case AppConnectionStatus::CreatingSession: appStatus = "CreatingSession"; break;
case AppConnectionStatus::StartingSession: appStatus = "StartingSession"; break;
case AppConnectionStatus::Connecting: appStatus = "Connecting"; break;
case AppConnectionStatus::Connected: appStatus = "Connected"; break;
case AppConnectionStatus::ConnectionFailed: appStatus = "ConnectionFailed"; break;
}
char buffer[1024];
sprintf_s(buffer, "Remote Rendering: New status: %s, result: %s\n", appStatus, m_statusMsg.c_str());
OutputDebugStringA(buffer);
}
void HolographicAppMain::SetNewSession(RR::ApiHandle<RR::RenderingSession> newSession)
{
SetNewState(AppConnectionStatus::StartingSession, nullptr);
m_sessionStartingTime = m_timeAtLastRESTCall = m_timer.GetTotalSeconds();
m_session = newSession;
m_api = m_session->Connection();
m_graphicsBinding = m_session->GetGraphicsBinding().as<RR::GraphicsBindingWmrD3d11>();
m_session->ConnectionStatusChanged([this](auto status, auto error)
{
OnConnectionStatusChanged(status, error);
});
};
void HolographicAppMain::OnConnectionStatusChanged(RR::ConnectionStatus status, RR::Result error)
{
const char* asString = RR::ResultToString(error);
m_connectionResult = error;
switch (status)
{
case RR::ConnectionStatus::Connecting:
SetNewState(AppConnectionStatus::Connecting, asString);
break;
case RR::ConnectionStatus::Connected:
if (error == RR::Result::Success)
{
SetNewState(AppConnectionStatus::Connected, asString);
}
else
{
SetNewState(AppConnectionStatus::ConnectionFailed, asString);
}
m_modelLoadTriggered = m_modelLoadFinished = false;
m_isConnected = error == RR::Result::Success;
break;
case RR::ConnectionStatus::Disconnected:
if (error == RR::Result::Success)
{
SetNewState(AppConnectionStatus::Disconnected, asString);
}
else
{
SetNewState(AppConnectionStatus::ConnectionFailed, asString);
}
m_modelLoadTriggered = m_modelLoadFinished = false;
m_isConnected = false;
break;
default:
break;
}
}
Update pro Bild
Der Client muss einmal für jeden Takt der Simulation aktualisiert werden. Außerdem müssen einige weitere Statusupdates durchgeführt werden. Die Funktion HolographicAppMain::Update
bietet einen guten Hook für Updates pro Bild.
Aktualisierung des Zustandsautomats
Sie müssen den Status der Sitzung abrufen und überprüfen, ob sie in den Status Ready
übergegangen ist. Wurde eine Verbindung hergestellt, wird das Laden des Modells über StartModelLoading
gestartet.
Fügen Sie dem Text der Funktion HolographicAppMain::Update
den folgenden Code hinzu:
// Updates the application state once per frame.
HolographicFrame HolographicAppMain::Update()
{
if (m_session != nullptr)
{
// Tick the client to receive messages
m_api->Update();
if (!m_sessionStarted)
{
// Important: To avoid server-side throttling of the requests, we should call GetPropertiesAsync very infrequently:
const double delayBetweenRESTCalls = 10.0;
// query session status periodically until we reach 'session started'
if (m_sessionPropertiesAsync == nullptr && m_timer.GetTotalSeconds() - m_timeAtLastRESTCall > delayBetweenRESTCalls)
{
m_timeAtLastRESTCall = m_timer.GetTotalSeconds();
m_session->GetPropertiesAsync([this](RR::Status status, RR::ApiHandle<RR::RenderingSessionPropertiesResult> propertiesResult)
{
if (status == RR::Status::OK)
{
auto ctx = propertiesResult->GetContext();
if (ctx.Result == RR::Result::Success)
{
auto res = propertiesResult->GetSessionProperties();
switch (res.Status)
{
case RR::RenderingSessionStatus::Ready:
{
// The following ConnectAsync is async, but we'll get notifications via OnConnectionStatusChanged
m_sessionStarted = true;
SetNewState(AppConnectionStatus::Connecting, nullptr);
RR::RendererInitOptions init;
init.IgnoreCertificateValidation = false;
init.RenderMode = RR::ServiceRenderMode::Default;
m_session->ConnectAsync(init, [](RR::Status, RR::ConnectionStatus) {});
}
break;
case RR::RenderingSessionStatus::Error:
SetNewState(AppConnectionStatus::ConnectionFailed, "Session error");
break;
case RR::RenderingSessionStatus::Stopped:
SetNewState(AppConnectionStatus::ConnectionFailed, "Session stopped");
break;
case RR::RenderingSessionStatus::Expired:
SetNewState(AppConnectionStatus::ConnectionFailed, "Session expired");
break;
}
}
else
{
SetNewState(AppConnectionStatus::ConnectionFailed, ctx.ErrorMessage.c_str());
}
}
else
{
SetNewState(AppConnectionStatus::ConnectionFailed, "Failed to retrieve session status");
}
m_sessionPropertiesQueryInProgress = false; // next try
}); }
}
}
if (m_isConnected && !m_modelLoadTriggered)
{
m_modelLoadTriggered = true;
StartModelLoading();
}
}
if (m_needsCoordinateSystemUpdate && m_stationaryReferenceFrame && m_graphicsBinding)
{
// Set the coordinate system once. This must be called again whenever the coordinate system changes.
winrt::com_ptr<ABI::Windows::Perception::Spatial::ISpatialCoordinateSystem> ptr{ m_stationaryReferenceFrame.CoordinateSystem().as<ABI::Windows::Perception::Spatial::ISpatialCoordinateSystem>() };
m_graphicsBinding->UpdateUserCoordinateSystem(ptr.get());
m_needsCoordinateSystemUpdate = false;
}
// Rest of the body:
...
}
Aktualisierung des Koordinatensystems
Sie müssen sich mit dem Renderingdienst auf ein Koordinatensystem einigen, das verwendet werden soll. Für den Zugriff auf das zu verwendende Koordinatensystem benötigen Sie das m_stationaryReferenceFrame
-Element, das am Ende der Funktion HolographicAppMain::OnHolographicDisplayIsAvailableChanged
erstellt wird.
Dieses Koordinatensystem ändert sich in der Regel nicht, sodass dies eine einmalige Initialisierung ist. Sie muss erneut aufgerufen werden, wenn die Anwendung das Koordinatensystem ändert.
Mit dem obigen Code wird das Koordinatensystem einmal innerhalb der Funktion Update
festgelegt, sobald Sie über ein Referenzkoordinatensystem und eine verbundene Sitzung verfügen.
Kameraaktualisierung
Sie müssen die Clippingebenen der Kamera aktualisieren, damit die Serverkamera mit der lokalen Kamera synchronisiert wird. Dieser Schritt kann ganz am Ende der Funktion Update
ausgeführt werden:
...
if (m_isConnected)
{
// Any near/far plane values of your choosing.
constexpr float fNear = 0.1f;
constexpr float fFar = 10.0f;
for (HolographicCameraPose const& cameraPose : prediction.CameraPoses())
{
// Set near and far to the holographic camera as normal
cameraPose.HolographicCamera().SetNearPlaneDistance(fNear);
cameraPose.HolographicCamera().SetFarPlaneDistance(fFar);
}
// The API to inform the server always requires near < far. Depth buffer data will be converted locally to match what is set on the HolographicCamera.
auto settings = m_api->GetCameraSettings();
settings->SetNearAndFarPlane(std::min(fNear, fFar), std::max(fNear, fFar));
settings->SetEnableDepth(true);
}
// The holographic frame will be used to get up-to-date view and projection matrices and
// to present the swap chain.
return holographicFrame;
}
Darstellung
Als letzten Schritt müssen wir das Rendering der Remoteinhalte aufrufen. Sie müssen diesen Aufruf an genau der richtigen Position in der Renderingpipeline ausführen, und zwar nach dem Löschen des Renderziels und dem Festlegen des Viewports. Fügen Sie der Sperre UseHolographicCameraResources
innerhalb von Funktion HolographicAppMain::Render
den folgenden Codeausschnitt hinzu:
...
// Existing clear function:
context->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
// ...
// Existing check to test for valid camera:
bool cameraActive = pCameraResources->AttachViewProjectionBuffer(m_deviceResources);
// Inject remote rendering: as soon as we are connected, start blitting the remote frame.
// We do the blitting after the Clear, and before cube rendering.
if (m_isConnected && cameraActive)
{
m_graphicsBinding->BlitRemoteFrame();
}
...
Ausführen des Beispiels
Das Beispiel sollte jetzt einen Zustand erreicht haben, in dem es sich kompilieren und ausführen lässt.
Wenn das Beispiel ordnungsgemäß ausgeführt wird, zeigt es den rotierenden Würfel direkt vor Ihnen, und nach einiger Zeit für die Sitzungserstellung und das Laden des Modells wird das Engine-Modell an der aktuellen Kopfposition wiedergegeben. Sitzungserstellung und Laden des Modells können mehrere Minuten in Anspruch nehmen. Der aktuelle Status wird nur in den Ausgabebereich von Visual Studio geschrieben. Daher wird empfohlen, das Beispiel aus Visual Studio zu starten.
Achtung
Der Client trennt die Serververbindung, wenn die Taktfunktion einige Sekunden lang nicht aufgerufen wird. Das Auslösen von Haltepunkten kann also sehr leicht zu einem Trennen der Verbindung führen.
Eine ordnungsgemäße Statusanzeige mit einem Textbereich finden Sie in der vorkonfigurierten Version dieses Tutorials auf GitHub.
Nächste Schritte
In diesem Tutorial haben Sie alle erforderlichen Schritte zum Hinzufügen von Remote Rendering zu einem standardmäßigen C++/DirectX11-Beispiel einer Holographic-App kennengelernt. Informationen zum Konvertieren Ihres eigenen Modells finden Sie in der folgenden Schnellstartanleitung: