Öğretici: Uzaktan İşlemeyi HoloLens Holographic Uygulamasıyla Tümleştirme

Bu öğreticide şunları öğreneceksiniz:

  • HoloLens'e dağıtılabilir bir Holographic Uygulaması oluşturmak için Visual Studio'yu kullanma
  • Yerel işlemeyi uzaktan işlenmiş içerikle birleştirmek için gerekli kod parçacıklarını ve proje ayarlarını ekleyin

Bu öğretici, yerel işlemeyi Azure Remote Rendering ile birleştirmek için gerekli bitleri yerel Holographic App bir örneğe eklemeye odaklanır. Bu uygulamadaki tek durum geri bildirimi türü Visual Studio'nun içindeki hata ayıklama çıkış panelidir, bu nedenle örneği Visual Studio'nun içinden başlatmanızı öneririz. Sıfırdan dinamik bir metin paneli oluşturmak çok fazla kodlama gerektirdiğinden, uygulama içi geri bildirim eklemek bu örneğin kapsamının dışındadır. İyi bir başlangıç noktası, GitHub'da Uzaktan İletişim Oynatıcı örnek projesinin bir parçası olan sınıfıdırStatusDisplay. Aslında, bu öğreticinin önceden canned sürümü bu sınıfın yerel bir kopyasını kullanır.

İpucu

ARR örnekleri deposu, kullanıma hazır bir Visual Studio projesi olarak bu öğreticinin sonucunu içerir. Ayrıca kullanıcı arabirimi sınıfı StatusDisplayaracılığıyla uygun hata ve durum raporlama ile zenginleştirilmiştir. Öğreticinin içinde, tüm ARR'ye özgü eklemelerin kapsamı ile #ifdef USE_REMOTE_RENDERING / #endifbelirlenmiştir, bu nedenle Uzaktan İşleme eklemelerini tanımlamak kolaydır.

Önkoşullar

Bu öğretici için şunları yapmanız gerekir:

Yeni holografik uygulama örneği oluşturma

İlk adım olarak, Uzaktan İşleme tümleştirmesinin temelini oluşturan bir hisse senedi örneği oluştururuz. Visual Studio'yu açın ve "Yeni proje oluştur"u seçin ve "Holographic DirectX 11 Uygulaması (Evrensel Windows) (C++/WinRT)" ifadesini arayın

Yeni proje oluşturma

İ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ıklama / ARM64" olarak değiştirin. Artık bunu derleyip bağlı bir HoloLens 2 cihazına dağıtabilmeniz gerekir. HoloLens'te çalıştırırsanız, önünde dönen bir küp görmeniz gerekir.

NuGet aracılığıyla Uzaktan İşleme bağımlılıkları ekleme

Uzaktan İşleme özelliklerini eklemenin ilk adımı, istemci tarafı bağımlılıklarını eklemektir. İlgili bağımlılıklar NuGet paketi olarak kullanılabilir. Çözüm Gezgini projeye sağ tıklayın ve bağlam menüsünden "NuGet Paketlerini Yönet..." öğesini seçin.

İstenilen iletişim kutusunda "Microsoft.Azure.RemoteRendering.Cpp" adlı NuGet paketine göz atın:

NuGet paketine göz atın

ögesini seçin ve ardından "Yükle" düğmesine basarak projeye ekleyin.

NuGet paketi, Uzaktan İşleme bağımlılıklarını projeye ekler. Özellikle:

  • İstemci kitaplığına (RemoteRenderingClient.lib) karşı bağlantı.
  • .dll bağımlılıklarını ayarlayın.
  • Ekleme dizinine doğru yolu ayarlayın.

Proje hazırlığı

Mevcut projede küçük değişiklikler yapmamız gerekiyor. Bu değişiklikler hafiftir, ancak bunlar olmadan Uzaktan İşleme çalışmaz.

DirectX cihazında çoklu iş parçacığı korumasını etkinleştirme

Cihazda DirectX11 çoklu iş parçacığı koruması etkinleştirilmelidir. Bunu değiştirmek için dosya DeviceResources.cpp "Common" klasöründe açın ve işlevin DeviceResources::CreateDeviceResources()sonuna aşağıdaki kodu ekleyin:

// 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ılmadan, bağlantı sorguları sonunda zaman aşımlarıyla sonuçlanır. Etkinleştirmek için çözüm gezginindeki öğeye package.appxmanifest çift tıklayın. Sonraki kullanıcı arabiriminde Özellikler sekmesine gidin ve şunları seçin:

  • İnternet (İstemci ve Sunucu)
  • İnternet (İstemci)

Ağ özellikleri

Uzaktan İşlemeyi Tümleştirme

Artık proje hazır olduğuna göre kodla başlayabiliriz. Başlatma, başlatmayı kaldırma ve işleme için gerekli tüm kancalara sahip olduğundan, uygulamaya iyi bir giriş noktası sınıfıdır HolographicAppMain(Dosya HolographicAppMain.h/cpp).

Dahil Eder

Gerekli eklemeleri ekleyerek başlıyoruz. Aşağıdaki eklemeleri HolographicAppMain.h dosyasına ekleyin:

#include <AzureRemoteRendering.h>

... ve dosya HolographicAppMain.cpp için şu ek include yönergeler:

#include <AzureRemoteRendering.inl>
#include <RemoteRenderingExtensions.h>
#include <windows.perception.spatial.h>

Kodun basitliği için, holographicAppMain.h dosyasının üst kısmında yönergelerinden sonra aşağıdaki ad alanı kısayolunu include tanımlarız:

namespace RR = Microsoft::Azure::RemoteRendering;

Bu kısayol kullanışlıdır, bu nedenle her yerde tam ad alanını yazmamız gerekmez, ancak yine de ARR'ye özgü veri yapılarını tanıyabiliriz. Elbette yönergesini using namespace... de kullanabiliriz.

Uzaktan İşleme başlatma

Uygulamanın ömrü boyunca oturum vb. için birkaç nesne tutmamız gerekir. Yaşam süresi, uygulamanın HolographicAppMain nesnesinin ömrüyle çakışır, bu nedenle nesnelerimizi sınıfına HolographicAppMainüye olarak ekleriz. 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 uygulamayı yapmak için iyi bir yer sınıfının HolographicAppMainoluşturucusdur. Orada üç tür başlatma yapmamız gerekir:

  1. Uzaktan İşleme sisteminin tek seferlik başlatılması
  2. İstemci oluşturma (kimlik doğrulaması)
  3. Oturum oluşturma

Bunların hepsini oluşturucuda sıralı olarak yaparız. Ancak, gerçek kullanım örneklerinde bu adımları ayrı ayrı yapmak uygun olabilir.

Dosya HolographicAppMain.cpp 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 makine kodunun geri kalanıyla birlikte sonraki paragrafta uygulayacağımız üye işlevlerini SetNewSession ve SetNewStateöğesini çağırır.

Kimlik bilgilerinin örnekte sabit olarak kodlandığını ve yerinde doldurulması gerektiğini unutmayın (hesap kimliği, hesap anahtarı, hesap etki alanı ve uzaktan işleme etki alanı).

Yok edici gövdesinin sonunda başlatmayı simetrik ve ters sırada yaparı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

Uzaktan İşleme'de oturum oluşturmak ve modeli yüklemek için kullanılan temel işlevler zaman uyumsuz işlevlerdir. Bunu hesaba eklemek için, temelde aşağıdaki durumlardan otomatik olarak geçen basit bir durum makinesine ihtiyacımız vardır:

Başlatma -> Oturum oluşturma -> Oturum başlatma -> Model yükleme (ilerleme durumuyla)

Buna göre, bir sonraki adım olarak sınıfına biraz durum makine işlemesi ekleyeceğiz. Uygulamamızın bulunabileceği çeşitli durumlar için kendi sabit listemizi AppConnectionStatus bildiririz. ile benzerdir RR::ConnectionStatus, ancak başarısız bağlantı için ek bir duruma sahiptir.

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 onay 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 duruma geçirilip geçirmediğini Ready görmemiz gerekiyor. Başarıyla bağlandıysak, sonunda aracılığıyla StartModelLoadingmodel yükleme işlemini başlatmış olacağız.

aşağıdaki kodu işlevinin HolographicAppMain::Updategövdesine 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:
    ...
}

Koordinat sistemi güncelleştirmesi

Kullanılacak koordinat sisteminde işleme hizmetiyle aynı fikirde olmamız gerekir. Kullanmak istediğimiz koordinat sistemine erişmek için işlevinin HolographicAppMain::OnHolographicDisplayIsAvailableChangedsonunda oluşturulan öğesine ihtiyacımız vardırm_stationaryReferenceFrame.

Bu koordinat sistemi genellikle değişmez, bu nedenle bu tek seferlik bir başlatmadır. Uygulamanız koordinat sistemini değiştirirse yeniden çağrılmalıdır.

Yukarıdaki kod, hem bir başvuru koordinat sistemi hem de bağlı bir oturuma sahip olduğumuz anda koordinat sistemini işlev içinde Update bir kez ayarlar.

Kamera güncelleştirmesi

Sunucu kamerasının yerel kamerayla eşitlenmiş durumda 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

Yapılacak son şey, uzak içeriğin işlenmesini çağırmaktır. İşleme hedefi temizlendikten ve görünüm penceresi ayarlandıktan sonra bu çağrıyı işleme işlem hattı içinde tam olarak doğru konumda yapmamız gerekir. aşağıdaki kod parçacığını lock inside işlevine UseHolographicCameraResources HolographicAppMain::Renderekleyin:

        ...
        // 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 derlendiği ve çalıştığı bir durumda olmalıdır.

Örnek düzgün çalıştığında, dönen küpü hemen önünüzde gösterir ve bazı oturum oluşturma ve model yükleme işlemlerinden sonra geçerli baş konumunda bulunan altyapı modelini işler. Oturum oluşturma ve model yükleme işlemi birkaç dakika kadar sürebilir. Geçerli durum yalnızca Visual Studio'nun çıkış paneline yazılır. Bu nedenle, örneğin Visual Studio'dan başlatılması önerilir.

Dikkat

Onay işlevi birkaç saniye boyunca çağrılmadığında istemcinin sunucuyla bağlantısı kesilir. Bu nedenle kesme noktalarının tetiklenmesi uygulamanın bağlantısının kesilmesine çok kolay neden olabilir.

Metin paneliyle düzgün durum görüntüleme için GitHub'da bu öğreticinin önceden canned sürümüne bakın.

Sonraki adımlar

Bu öğreticide, stok Holographic App C++/DirectX11 örneğine Uzaktan İşleme eklemek için gereken tüm adımları öğrendiniz. Kendi modelinizi dönüştürmek için aşağıdaki hızlı başlangıç bölümüne bakın: