Öğretici: Remote Rendering Holographic App HoloLens tümleştirin
Bu öğreticide şunları öğreneceksiniz:
- Visual Studio'a dağıtılabilir bir Holographic Uygulaması oluşturmak için HoloLens
- Yerel işlemeyi uzaktan işlenen içerikle birleştirmek için gerekli kod parçacıklarını ve proje ayarlarını ekleyin
Bu öğretici, yerel işlemeyi yerel bir örnekle birleştirmek için gerekli bitleri yerel bir Holographic App Azure Remote Rendering. Bu uygulamanın tek durum geri bildirimi türü, Visual Studio içindeki hata ayıklama çıkış paneli aracılığıyladır, bu nedenle örneği uygulamanın içinden Visual Studio. Sıfırdan dinamik bir metin paneli oluşturmak çok fazla kodlama içerdiğinden, uygulama içinde düzgün geri bildirim eklemek bu örneğin kapsamının dışındadır. İyi bir başlangıç noktası, üzerinde Remoting Player örnek projesinin bir parçası StatusDisplay olan sınıfıdır GitHub. Aslında, bu öğreticinin önceden canned sürümü bu sınıfın yerel bir kopyasını kullanır.
İpucu
ARR örnekleri deposu, bu öğreticinin kullanıma hazır bir Visual Studio proje olarak sonucunu içerir. Ayrıca kullanıcı arabirimi sınıfı aracılığıyla düzgün hata ve durum raporlama ile StatusDisplay zenginleştirilmiştir. Öğreticinin içinde, tüm ARR'ye özgü eklemelerin kapsamına göre genişletilir, bu nedenle ek #ifdef USE_REMOTE_RENDERING / #endif Remote Rendering kolayca tanımlayabilirsiniz.
Önkoşullar
Bu öğretici için şunları gerekir:
- Hesap bilgileri (hesap kimliği, hesap anahtarı, hesap etki alanı, abonelik kimliği). Hesabınız yoksa bir hesap oluşturun.
- Windows SDK 10.0.18362.0 (indirme).
- Visual Studio 2019'un en son sürümü (indirme).
- Visual Studio Gerçeklik için araçlar. Özellikle, aşağıdaki İş Yükü yüklemeleri zorunludur:
- C++ ile masaüstü geliştirme
- Evrensel Windows Platformu (UWP) geliştirme
- için Windows Mixed Reality Uygulama Şablonları Visual Studio (indirme).
Yeni bir Holographic App örneği oluşturma
İlk adım olarak, veri tümleştirmesi için temel alınan bir stok Remote Rendering oluşturuz. Yeni Visual Studio açın ve "Yeni proje oluştur"u seçin ve "Holographic DirectX 11 Uygulaması (Universal Windows) (C++/WinRT)" araması için arama

İstediğiniz proje adını yazın, bir yol seçin ve "Oluştur" düğmesini seçin. Yeni projede yapılandırmayı "Hata Ayıkla / ARM64" olarak değiştir. Artık derleme ve 2 cihaza bağlı bir HoloLens dağıtabilirsiniz. Bu küpü HoloLens önünde dönen bir küp görüyorsanız.
Remote Rendering aracılığıyla yeni bağımlılıklar NuGet
Bu özellikleri Remote Rendering ilk adım istemci tarafı bağımlılıklarını eklemektir. İlgili bağımlılıklar bir NuGet kullanılabilir. Bu Çözüm Gezgini sağ tıklayın ve bağlam menüsünden "NuGet Paketlerini Yönet..." öğesini seçin.
İstendiğinde, "Microsoft.Azure.RemoteRendering.Cpp" adlı NuGet paketine göz atabilirsiniz:

ve paketini seçerek ve ardından "Yükle" düğmesine basarak projeye ekleyin.
NuGet paketi, Remote Rendering bağımlılıklarını projeye ekler. Özellikle:
- İstemci kitaplığına (RemoteRenderingClient.lib) yönelik bağlantı.
- Bağımlılıkları .dll ayarlayın.
- Doğru yolu include dizinine ayarlayın.
Project hazırlığı
Mevcut projede küçük değişikliklere ihtiyacımız var. Bu değişiklikler hafiftir, ancak bunlar Remote Rendering çalışmayamaz.
DirectX cihazında çoklu iş parçacığı korumasını etkinleştirme
Cihazda DirectX11 çoklu iş parçacığı koruması etkin olmalıdır. Bunu değiştirmek için DeviceResources.cpp dosyasını "Common" klasöründe açın ve işlevinin sonuna aşağıdaki kodu DeviceResources::CreateDeviceResources() girin:
// 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);
}
Uygulama bildiriminde ağ özelliklerini etkinleştirme
Dağıtılan uygulama için ağ özellikleri açıkça etkinleştirilmelidir. Bu yapılandırılmazsa, bağlantı sorguları sonunda zaman aşımına neden olur. Etkinleştirmek için çözüm gezgininde package.appxmanifest öğeye çift tıklayın. Sonraki kullanıcı arabiriminde Özellikler sekmesine gidin ve şunları seçin:
- İnternet (İstemci & Sunucusu)
- İnternet (İstemci)

Tümleştirin Remote Rendering
Proje hazırlanmıştır, artık kodla başlayabiliriz. Uygulamaya iyi bir giriş noktası sınıfıdır (HolographicAppMain.h/cpp dosyası) çünkü başlatma, başlatmayı kaldır ve işleme için gerekli tüm kancalara HolographicAppMain sahip olur.
Dahil Eder
Gerekli eklemeleri ekleyerek başlayacağız. HolographicAppMain.h dosyasına aşağıdakileri ekleyin:
#include <AzureRemoteRendering.h>
... ve include HolographicAppMain.cpp dosyasına şu ek yönergeler eklenmiştir:
#include <AzureRemoteRendering.inl>
#include <RemoteRenderingExtensions.h>
#include <windows.perception.spatial.h>
Kodun basit olması için HolographicAppMain.h dosyasının üst kısmında yönergelerinin ardından aşağıdaki ad alanı include kısayolunu tanımlarsınız:
namespace RR = Microsoft::Azure::RemoteRendering;
Bu kısayol, her yerde tam ad alanını yazmamız gerekmaması ama yine de ARR'ye özgü veri yapılarını tanımamız için kullanışlıdır. Elbette yönergesi de using namespace... kullanabiliriz.
Remote Rendering başlatma
Uygulamanın kullanım ömrü boyunca oturum için birkaç nesne tutmamız gerekir. Yaşam süresi uygulama nesnesinin yaşam süresiyle çakıştığı için HolographicAppMain nesnelerimizi sınıfına üye olarak ekleyebilirsiniz. HolographicAppMain Sonraki adım HolographicAppMain.h dosyasına aşağıdaki sınıf üyelerini eklemektir:
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
}
Gerçek uygulama için iyi bir yer sınıfının HolographicAppMain oluşturucusudur. Burada üç tür başlatma yapmak zorundaiz:
- Sistem için bir kez Remote Rendering başlatma
- İstemci oluşturma (kimlik doğrulaması)
- Oturum oluşturma
Bunların hepsini oluşturucuda sırayla yapacağız. Ancak, gerçek kullanım durumlarında bu adımları ayrı ayrı yapmak uygun olabilir.
HolographicAppMain.cpp dosyasında oluşturucu gövdesinin başına aşağıdaki kodu ekleyin:
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:
...
}
Kod, durum makinesi kodunun geri kalanıyla birlikte bir SetNewSession SetNewState sonraki paragrafta uygulayacakları üye işlevlerini ve işlevlerini çağıracak.
Kimlik bilgilerinin örnekte sabit kodlu olduğunu ve yerinde doldurulması gerektiğini unutmayın ( hesapkimliği, hesap anahtarı,hesap etki alanı ve uzaktan işleme etki alanı).
Başlatmayı simetrik ve yıkıcı gövdesinin sonunda ters sırada yapacağız:
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();
}
Durum makinesi
Bu Remote Rendering, oturum oluşturma ve model yükleme için önemli işlevler zaman uyumsuz işlevlerdir. Bunu hesaba etmek için, temelde aşağıdaki durumlardan otomatik olarak geçiş yapılan basit bir durum makinesine ihtiyacımız vardır:
Başlatma -> Oturum oluşturma -> -> Modeli yüklemesi (ilerleme durumuyla) başlat
Buna uygun olarak, sonraki adım olarak sınıfına biraz durum makinesi işlemesi ekleriz. Uygulamamızın içinde yer alan AppConnectionStatus çeşitli durumları için kendi enum'larımızı bildirmektedir. buna benzer, RR::ConnectionStatus ancak başarısız bağlantı için ek bir durumu vardır.
Sınıf bildirimine aşağıdaki üyeleri ve işlevleri ekleyin:
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;
}
.cpp dosyasının uygulama tarafında şu işlev gövdelerini ekleyin:
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;
}
}
Çerçeve başına güncelleştirme
Simülasyon işareti başına istemciyi bir kez güncelleştirmemiz ve bazı ek durum güncelleştirmeleri yapmamız gerekir. İşlev, HolographicAppMain::Update çerçeve başına güncelleştirmeler için iyi bir kanca sağlar.
Durum makinesi güncelleştirmesi
Oturumun durumunu yoklamamız ve durumuna geçişin olup olamay olduğunu Ready görmemiz gerekiyor. Başarıyla bağlandıysanız, son olarak aracılığıyla model yüklemesini StartModelLoading başlatıldık.
İşlevin gövdesine aşağıdaki kodu HolographicAppMain::Update ekleyin:
// 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:
...
}
Sistem güncelleştirmesini koordine etmek
Kullanmak üzere bir koordinat sistemi üzerinde işleme hizmetini kabul etmek gerekir. Kullanmak istediğiniz koordinat sistemine erişmek için işlevinin m_stationaryReferenceFrame sonunda oluşturulan 'a ihtiyacımız HolographicAppMain::OnHolographicDisplayIsAvailableChanged vardır.
Bu koordinat sistemi genellikle değişmez, bu nedenle bu bir kez başlatmadır. Uygulamanız koordinat sistemini değiştirirse yeniden çağrılmalı.
Yukarıdaki kod, hem başvuru koordinat sistemine hem de bağlı oturuma sahip olduğu anda koordinat sistemini işlev Update içinde bir kez ayarlar.
Kamera güncelleştirmesi
Sunucu kamerasının yerel kamerayla eşit tutulması için kamera klibi düzlemlerini güncelleştirmemiz gerekiyor. Bunu işlevin en sonunda Update yapabiliriz:
...
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;
}
İşleme
Son olarak uzak içeriğin işlemesi iptal etmektir. İşleme hedefi temiz olduktan ve görünüm çıtasını ayardikten sonra bu çağrıyı işleme işlem hattında tam doğru konumda yapacağız. Lock inside işlevine aşağıdaki UseHolographicCameraResources kod parçacığını HolographicAppMain::Render girin:
...
// 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();
}
...
Örneği çalıştırma
Örnek artık derlediği ve çalıştır olduğu bir durumda olmalı.
Örnek düzgün şekilde çalıştırılsa, hemen önünde dönen küpü gösterir ve bazı oturum oluşturma ve model yüklemelerinden sonra geçerli baş konumunda bulunan motor modelini işler. Oturum oluşturma ve model yüklemesi birkaç dakika sürebilir. Geçerli durum yalnızca Visual Studio paneline yazılır. Bu nedenle örneği uygulamanın içinden başlatma Visual Studio.
Dikkat
Tick işlevi birkaç saniye çağrılmayca istemcinin sunucu bağlantısı kesiliyor. Bu nedenle kesme noktaları tetiklemek uygulamanın bağlantısının kesilmesine çok kolay neden olabilir.
Metin paneliyle düzgün bir durum görüntüsü için bu öğreticinin önceden canned sürümüne GitHub.
Sonraki adımlar
Bu öğreticide, bir stok Holographic App C++/DirectX11 Remote Rendering eklemek için gereken tüm adımları öğrendinsiniz. Kendi modelinizi dönüştürmek için aşağıdaki hızlı başlangıçlara bakın: