DirectX'te uzamsal eşleme

Not

Bu makale eski WinRT yerel API'leriyle ilgilidir. Yeni yerel uygulama projeleri için OpenXR API'sini kullanmanızı öneririz.

Bu konu başlığında, Evrensel Windows Platformu SDK ile paketlenmiş uzamsal eşleme örnek uygulamasının ayrıntılı bir açıklaması da dahil olmak üzere DirectX uygulamanızda uzamsal eşlemenin nasıl uygulandığı açıklanmaktadır.

Bu konuda HolographicSpatialMapping UWP kod örneğindeki kod kullanılmıştır.

Not

Bu makaledeki kod parçacıkları şu anda C++ holografik proje şablonunda kullanılan C++17 uyumlu C++/WinRT yerine C++/CX kullanımını göstermektedir. Kavramlar C++/WinRT projesi için eşdeğerdir, ancak kodu çevirmeniz gerekir.

Cihaz desteği

Özellik HoloLens (1. nesil) HoloLens 2 Çevreleyici kulaklıklar
Uzamsal eşleme ✔️ ✔️

DirectX geliştirmeye genel bakış

Uzamsal eşleme için yerel uygulama geliştirme, Windows.Perception.Spatial ad alanındaki API'leri kullanır. Bu API'ler, uzamsal eşleme API'lerinin Unity tarafından kullanıma sunulduğu şekilde uzamsal eşleme işlevselliğinin tam denetimini sağlar.

Algı API'leri

Uzamsal eşleme geliştirme için sağlanan birincil türler şunlardır:

  • SpatialSurfaceObserver, uzamsalSurfaceInfo nesneleri biçiminde kullanıcıya yakın uygulama tarafından belirtilen uzay bölgelerindeki yüzeyler hakkında bilgi sağlar.
  • SpatialSurfaceInfo benzersiz bir kimlik, sınırlayıcı hacim ve son değişikliğin zamanı dahil olmak üzere tek bir ekstant uzamsal yüzeyi açıklar. İstek üzerine SpatialSurfaceMesh'i zaman uyumsuz olarak sağlar.
  • SpatialSurfaceMeshOptions , SpatialSurfaceInfo'dan istenen SpatialSurfaceMesh nesnelerini özelleştirmek için kullanılan parametreleri içerir.
  • SpatialSurfaceMesh , tek bir uzamsal yüzey için örgü verilerini temsil eder. Köşe konumları, köşe normalleri ve üçgen dizinleri için veriler SpatialSurfaceMeshBuffer üye nesnelerinde bulunur.
  • SpatialSurfaceMeshBuffer tek bir örgü veri türünü sarmalar.

Bu API'leri kullanarak uygulama geliştirirken temel program akışınız şöyle görünür (aşağıda açıklanan örnek uygulamada gösterildiği gibi):

  • SpatialSurfaceObserver'ınızı ayarlama
    • Kullanıcının uygulamanızın cihazın uzamsal eşleme özelliklerini kullanmasına izin verdiğinden emin olmak için RequestAccessAsync'i çağırın.
    • SpatialSurfaceObserver nesnesinin örneğini oluşturma.
    • Uzamsal yüzeyler hakkında bilgi almak istediğiniz alan bölgelerini belirtmek için SetBoundingVolumes öğesini çağırın. Gelecekte bu işlevi yeniden çağırarak bu bölgeleri değiştirebilirsiniz. Her bölge SpatialBoundingVolume kullanılarak belirtilir.
    • Belirttiğiniz uzay bölgelerindeki uzamsal yüzeyler hakkında her yeni bilgi sağlandığında tetiklenecek Olan ObservedSurfacesChanged olayına kaydolun.
  • Process ObservedSurfacesChanged events
    • Olay işleyicinizde, SpatialSurfaceInfo nesnelerinin haritasını almak için GetObservedSurfaces öğesini çağırın. Bu haritayı kullanarak, kullanıcının ortamında hangi uzamsal yüzeylerin bulunduğuna ilişkin kayıtlarınızı güncelleştirebilirsiniz.
    • Her SpatialSurfaceInfo nesnesi için, seçtiğiniz uzamsal koordinat sisteminde ifade edilen yüzeyin uzamsal uzantılarını belirlemek için TryGetBounds'ı sorguleyebilirsiniz.
    • uzamsal bir yüzey için mesh isteğinde bulunmaya karar verirseniz TryComputeLatestMeshAsync çağrısında bulunabilirsiniz. Üçgenlerin yoğunluğu ve döndürülen örgü verilerinin biçimini belirten seçenekler sağlayabilirsiniz.
  • Ağ alma ve işleme
    • TryComputeLatestMeshAsync çağrısının her biri zaman uyumsuz olarak bir SpatialSurfaceMesh nesnesi döndürür.
    • Bu nesneden, isteğe bağlı olarak ağın üçgen dizinlerine, köşe konumlarına ve köşe normallerine erişmenizi sağlayan içerilen SpatialSurfaceMeshBuffer nesnelerine erişebilirsiniz. Bu veriler, tire işlemek için kullanılan Direct3D 11 API'leriyle doğrudan uyumlu bir biçimde olacaktır.
    • Buradan uygulamanız isteğe bağlı olarak mesh verilerini analiz edebilir veya işleyebilir ve bunları işleme ve fizik ışın yayınları ile çarpışma için kullanabilir.
    • Dikkat edilmesi gereken önemli bir ayrıntı, mesh köşe konumlarına (örneğin, kafesleri işlemek için kullanılan köşe gölgelendiricisinde) bir ölçek uygulayarak bunları arabellekte depolandıkları iyileştirilmiş tamsayı birimlerinden ölçümlere dönüştürmeniz gerektiğidir. VertexPositionScale çağırarak bu ölçeği alabilirsiniz.

Sorun giderme

Uzamsal Eşleme kodu örneği kılavuzu

Holografik Uzamsal Eşleme kod örneği, yüzey çizgilerini yönetme ve işleme altyapısı dahil olmak üzere uygulamanıza yüzey tirelerini yüklemeye başlamak için kullanabileceğiniz kodu içerir.

Şimdi DirectX uygulamanıza yüzey eşleme özelliği ekleme adımlarını inceleyeceğiz. Bu kodu Windows Holographic uygulama şablonu projenize ekleyebilir veya yukarıda belirtilen kod örneğine göz atarak devam edebilirsiniz. Bu kod örneği, Windows Holographic uygulama şablonunu temel alır.

Uygulamanızı spatialPerception özelliğini kullanacak şekilde ayarlama

Uygulamanız uzamsal eşleme özelliğini kullanabilir. Uzamsal ağ, kullanıcının ortamının özel veriler olarak kabul edilebilecek bir gösterimi olduğundan bu gereklidir. Uygulamanızın package.appxmanifest dosyasında bu özelliği bildirin. Aşağıda bir örnek verilmiştir:

<Capabilities>
  <uap2:Capability Name="spatialPerception" />
</Capabilities>

Bu özellik uap2 ad alanından gelir. Bildiriminizde bu ad alanına erişmek için package> öğesine xlmns özniteliği <olarak ekleyin. Aşağıda bir örnek verilmiştir:

<Package
    xmlns="https://schemas.microsoft.com/appx/manifest/foundation/windows10"
    xmlns:mp="https://schemas.microsoft.com/appx/2014/phone/manifest"
    xmlns:uap="https://schemas.microsoft.com/appx/manifest/uap/windows10"
    xmlns:uap2="https://schemas.microsoft.com/appx/manifest/uap/windows10/2"
    IgnorableNamespaces="uap uap2 mp"
    >

Uzamsal eşleme özelliği desteğini denetleme

Windows Mixed Reality uzamsal eşlemeyi desteklemeyen cihazlar da dahil olmak üzere çok çeşitli cihazları destekler. Uygulamanız uzamsal eşlemeyi kullanabiliyorsa veya işlev sağlamak için uzamsal eşlemeyi kullanması gerekiyorsa, kullanmaya çalışmadan önce uzamsal eşlemenin desteklendiğinden emin olunması gerekir. Örneğin, karma gerçeklik uygulamanızın uzamsal eşlemesi gerekiyorsa, kullanıcı uzamsal eşleme olmadan bir cihazda çalıştırmayı denediğinde bu etkiye yönelik bir ileti görüntülemesi gerekir. Ya da uygulamanız kullanıcının ortamı yerine kendi sanal ortamını işleyerek uzamsal eşleme kullanılabilir olduğunda gerçekleşeceklere benzer bir deneyim sağlayabilir. Her durumda, bu API uygulamanızın uzamsal eşleme verilerini ne zaman almayacağını ve uygun şekilde yanıt vermeyeceğinin farkında olmasını sağlar.

Geçerli cihazda uzamsal eşleme desteği olup olmadığını denetlemek için önce UWP sözleşmesinin 4 veya daha yüksek düzeyde olduğundan emin olun ve spatialSurfaceObserver::IsSupported() öğesini çağırın. Holographic Spatial Mapping kod örneği bağlamında bunu şu şekilde yapabilirsiniz. Destek, erişim istemeden hemen önce denetlendi.

SpatialSurfaceObserver::IsSupported() API'sini SDK 15063 sürümünden itibaren kullanabilirsiniz. Gerekirse, bu API'yi kullanmadan önce projenizi platform sürümü 15063'e yeniden hedefleyin.

if (m_surfaceObserver == nullptr)
   {
       using namespace Windows::Foundation::Metadata;
       if (ApiInformation::IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 4))
       {
           if (!SpatialSurfaceObserver::IsSupported())
           {
               // The current system does not have spatial mapping capability.
               // Turn off spatial mapping.
               m_spatialPerceptionAccessRequested = true;
               m_surfaceAccessAllowed = false;
           }
       }

       if (!m_spatialPerceptionAccessRequested)
       {
           /// etc ...

UWP sözleşmesi düzey 4'ten küçük olduğunda uygulama, cihaz uzamsal eşleme yapabiliyormuş gibi devam etmelidir.

Uzamsal eşleme verilerine erişim isteme

Uygulamanızın yüzey gözlemcileri oluşturmaya çalışmadan önce uzamsal eşleme verilerine erişmek için izin istemesi gerekir. Aşağıda Surface Mapping kod örneğimizi temel alan ve bu sayfanın ilerleyen kısımlarında sağlanan diğer ayrıntıları içeren bir örnek verilmiştir:

auto initSurfaceObserverTask = create_task(SpatialSurfaceObserver::RequestAccessAsync());
initSurfaceObserverTask.then([this, coordinateSystem](Windows::Perception::Spatial::SpatialPerceptionAccessStatus status)
{
    if (status == SpatialPerceptionAccessStatus::Allowed)
    {
        // Create a surface observer.
    }
    else
    {
        // Handle spatial mapping unavailable.
    }
}

Yüzey gözlemcisi oluşturma

Windows::P erception::Spatial::Surfaces ad alanı, SpatialCoordinateSystem içinde belirttiğiniz bir veya daha fazla birimi gözlemleyen SpatialSurfaceObserver sınıfını içerir. Surface mesh verilerine gerçek zamanlı olarak erişmek için SpatialSurfaceObserver örneği kullanın.

AppMain.h'den:

// Obtains surface mapping data from the device in real time.
Windows::Perception::Spatial::Surfaces::SpatialSurfaceObserver^     m_surfaceObserver;
Windows::Perception::Spatial::Surfaces::SpatialSurfaceMeshOptions^  m_surfaceMeshOptions;

Önceki bölümde belirtildiği gibi, uygulamanızın kullanabilmesi için uzamsal eşleme verilerine erişim istemeniz gerekir. Bu erişim HoloLens'te otomatik olarak verilir.

// The surface mapping API reads information about the user's environment. The user must
// grant permission to the app to use this capability of the Windows Mixed Reality device.
auto initSurfaceObserverTask = create_task(SpatialSurfaceObserver::RequestAccessAsync());
initSurfaceObserverTask.then([this, coordinateSystem](Windows::Perception::Spatial::SpatialPerceptionAccessStatus status)
{
    if (status == SpatialPerceptionAccessStatus::Allowed)
    {
        // If status is allowed, we can create the surface observer.
        m_surfaceObserver = ref new SpatialSurfaceObserver();

Ardından, yüzey gözlemcisini belirli bir sınırlayıcı hacmi gözlemlemek için yapılandırmanız gerekir. Burada koordinat sisteminin kökeninde ortalanmış 20x20x5 metre olan bir kutu gözlemliyoruz.

// The surface observer can now be configured as needed.

        // In this example, we specify one area to be observed using an axis-aligned
        // bounding box 20 meters in width and 5 meters in height and centered at the
        // origin.
        SpatialBoundingBox aabb =
        {
            { 0.f,  0.f, 0.f },
            {20.f, 20.f, 5.f },
        };

        SpatialBoundingVolume^ bounds = SpatialBoundingVolume::FromBox(coordinateSystem, aabb);
        m_surfaceObserver->SetBoundingVolume(bounds);

Bunun yerine birden çok sınırlayıcı birim ayarlayabilirsiniz.

Bu sahte koddur:

m_surfaceObserver->SetBoundingVolumes(/* iterable collection of bounding volumes*/);

Görünüm frustum veya eksen hizalanmamış bir sınırlayıcı kutu gibi diğer sınırlayıcı şekilleri kullanmak da mümkündür.

Bu sahte koddur:

m_surfaceObserver->SetBoundingVolume(
            SpatialBoundingVolume::FromFrustum(/*SpatialCoordinateSystem*/, /*SpatialBoundingFrustum*/)
            );

Yüzey eşleme verileri kullanılamadığında uygulamanızın farklı bir şey yapması gerekiyorsa SpatialPerceptionAccessStatus'unİzin Verilmeyen durumlara yanıt vermek için kod yazabilirsiniz. Örneğin, bu cihazların uzamsal eşleme için donanımı olmadığından çevreleyici cihazlar takılı bilgisayarlarda buna izin verilmez. Bu cihazlar için, bunun yerine kullanıcının ortamı ve cihaz yapılandırması hakkında bilgi için uzamsal aşamaya güvenmeniz gerekir.

Surface Mesh koleksiyonunu başlatma ve güncelleştirme

Yüzey gözlemcisi başarıyla oluşturulduysa surface mesh koleksiyonumuzu başlatmaya devam edebiliriz. Burada, gözlemlenen geçerli yüzey kümesini hemen almak için çekme modeli API'sini kullanırız:

auto mapContainingSurfaceCollection = m_surfaceObserver->GetObservedSurfaces();
        for (auto& pair : mapContainingSurfaceCollection)
        {
            // Store the ID and metadata for each surface.
            auto const& id = pair->Key;
            auto const& surfaceInfo = pair->Value;
            m_meshCollection->AddOrUpdateSurface(id, surfaceInfo);
        }

Surface mesh verilerini almak için kullanılabilen bir anında iletme modeli de vardır. Uygulamanızı yalnızca seçerseniz çekme modelini kullanacak şekilde tasarlayabilirsiniz. Bu durumda her zaman (örneğin, kare başına bir kez) veya oyun kurulumu sırasında olduğu gibi belirli bir zaman aralığında verileri yoklarsınız. Bu durumda, ihtiyacınız olan yukarıdaki koddur.

Kod örneğimizde her iki modelin de pedagojik amaçlarla kullanımını göstermeyi seçtik. Burada, sistem bir değişikliği her tanıdığında güncel surface mesh verilerini almak için bir olaya abone olacağız.

m_surfaceObserver->ObservedSurfacesChanged += ref new TypedEventHandler<SpatialSurfaceObserver^, Platform::Object^>(
            bind(&HolographicDesktopAppMain::OnSurfacesChanged, this, _1, _2)
            );

Kod örneğimiz de bu olaylara yanıt verecek şekilde yapılandırılmıştır. Bunu nasıl yaptığımıza bakalım.

NOT: Uygulamanızın mesh verilerini işlemesinin en verimli yolu bu olmayabilir. Bu kod netlik için yazılmıştır ve iyileştirilmemiştir.

Surface mesh verileri, Platform::Guids anahtar değerleri kullanılarak SpatialSurfaceInfo nesnelerini depolayan salt okunur bir haritada sağlanır.

IMapView<Guid, SpatialSurfaceInfo^>^ const& surfaceCollection = sender->GetObservedSurfaces();

Bu verileri işlemek için önce koleksiyonumuzda olmayan anahtar değerleri ararız. Verilerin örnek uygulamamızda nasıl depolandığına ilişkin ayrıntılar bu konunun ilerleyen bölümlerinde sunulacaktır.

// Process surface adds and updates.
for (const auto& pair : surfaceCollection)
{
    auto id = pair->Key;
    auto surfaceInfo = pair->Value;

    if (m_meshCollection->HasSurface(id))
    {
        // Update existing surface.
        m_meshCollection->AddOrUpdateSurface(id, surfaceInfo);
    }
    else
    {
        // New surface.
        m_meshCollection->AddOrUpdateSurface(id, surfaceInfo);
    }
}

Ayrıca, yüzey ağı koleksiyonumuzda bulunan ancak artık sistem koleksiyonunda olmayan yüzey çizgilerini de kaldırmamız gerekir. Bunu yapmak için, tire eklemek ve güncelleştirmek için gösterdiğimiz şeyin tam tersi bir şey yapmamız gerekir; uygulamamızın koleksiyonunda döngü oluşturur ve sahip olduğumuz Guid değerinin sistem koleksiyonunda olup olmadığını denetleriz. Sistem koleksiyonunda değilse, bizim koleksiyonumuzdan kaldırırız.

AppMain.cpp dosyasındaki olay işleyicimizden:

m_meshCollection->PruneMeshCollection(surfaceCollection);

RealtimeSurfaceMeshRenderer.cpp dosyasında mesh ayıklama uygulaması:

void RealtimeSurfaceMeshRenderer::PruneMeshCollection(IMapView<Guid, SpatialSurfaceInfo^>^ const& surfaceCollection)
{
    std::lock_guard<std::mutex> guard(m_meshCollectionLock);
    std::vector<Guid> idsToRemove;

    // Remove surfaces that moved out of the culling frustum or no longer exist.
    for (const auto& pair : m_meshCollection)
    {
        const auto& id = pair.first;
        if (!surfaceCollection->HasKey(id))
        {
            idsToRemove.push_back(id);
        }
    }

    for (const auto& id : idsToRemove)
    {
        m_meshCollection.erase(id);
    }
}

Surface mesh veri arabelleklerini alma ve kullanma

Surface mesh bilgilerini almak, bir veri toplamayı çekmek ve güncelleştirmeleri bu koleksiyona işlemek kadar kolaydı. Şimdi verileri nasıl kullanabileceğiniz hakkında ayrıntılı bilgi edineceğiz.

Kod örneğimizde, işleme için yüzey tirelerini kullanmayı seçtik. Bu, gerçek dünya yüzeylerinin arkasındaki hologramları tıkama konusunda yaygın bir senaryodur. Ayrıca, uygulama veya oyun işlevselliği sağlamaya başlamadan önce kullanıcıya odanın hangi alanlarının taranmış olduğunu göstermek için mesh'leri işleyebilir veya bunların işlenmiş sürümlerini işleyebilirsiniz.

Kod örneği, önceki bölümde açıkladığımız olay işleyicisinden surface mesh güncelleştirmeleri aldığında işlemi başlatır. Bu işlevin önemli kod satırı surface mesh'i güncelleştirme çağrısıdır: Bu zamana kadar mesh bilgilerini zaten işledik ve uygun gördüğümüz şekilde kullanmak üzere köşe ve dizin verilerini almak üzereyiz.

RealtimeSurfaceMeshRenderer.cpp dosyasından:

void RealtimeSurfaceMeshRenderer::AddOrUpdateSurface(Guid id, SpatialSurfaceInfo^ newSurface)
{
    auto options = ref new SpatialSurfaceMeshOptions();
    options->IncludeVertexNormals = true;

    auto createMeshTask = create_task(newSurface->TryComputeLatestMeshAsync(1000, options));
    createMeshTask.then([this, id](SpatialSurfaceMesh^ mesh)
    {
        if (mesh != nullptr)
        {
            std::lock_guard<std::mutex> guard(m_meshCollectionLock);
            '''m_meshCollection[id].UpdateSurface(mesh);'''
        }
    }, task_continuation_context::use_current());
}

Örnek kodumuz, SurfaceMesh adlı veri sınıfı ağ veri işleme ve işleme işlemlerini işleyecek şekilde tasarlanmıştır. Bu tireler RealtimeSurfaceMeshRenderer'ın aslında bir haritasını tuttuğu şey. Her birinin geldiği SpatialSurfaceMesh'e bir referansı vardır, bu nedenle mesh köşesine veya dizin arabelleklerine erişmeniz gerektiğinde kullanabilir ya da mesh için bir dönüşüm elde edebilirsiniz. Şimdilik mesh'e güncelleştirme gerekiyor bayrağını kullanıyoruz.

SurfaceMesh.cpp'den:

void SurfaceMesh::UpdateSurface(SpatialSurfaceMesh^ surfaceMesh)
{
    m_surfaceMesh = surfaceMesh;
    m_updateNeeded = true;
}

Mesh'in bir sonraki çiziminde önce bayrağı kontrol eder. Bir güncelleştirme gerekirse, köşe ve dizin arabellekleri GPU'da güncelleştirilir.

void SurfaceMesh::CreateDeviceDependentResources(ID3D11Device* device)
{
    m_indexCount = m_surfaceMesh->TriangleIndices->ElementCount;
    if (m_indexCount < 3)
    {
        // Not enough indices to draw a triangle.
        return;
    }

İlk olarak ham veri arabelleklerini alıyoruz:

Windows::Storage::Streams::IBuffer^ positions = m_surfaceMesh->VertexPositions->Data;
    Windows::Storage::Streams::IBuffer^ normals   = m_surfaceMesh->VertexNormals->Data;
    Windows::Storage::Streams::IBuffer^ indices   = m_surfaceMesh->TriangleIndices->Data;

Ardından HoloLens tarafından sağlanan ağ verileriyle Direct3D cihaz arabellekleri oluşturacağız:

CreateDirectXBuffer(device, D3D11_BIND_VERTEX_BUFFER, positions, m_vertexPositions.GetAddressOf());
    CreateDirectXBuffer(device, D3D11_BIND_VERTEX_BUFFER, normals,   m_vertexNormals.GetAddressOf());
    CreateDirectXBuffer(device, D3D11_BIND_INDEX_BUFFER,  indices,   m_triangleIndices.GetAddressOf());

    // Create a constant buffer to control mesh position.
    CD3D11_BUFFER_DESC constantBufferDesc(sizeof(SurfaceTransforms), D3D11_BIND_CONSTANT_BUFFER);
    DX::ThrowIfFailed(
        device->CreateBuffer(
            &constantBufferDesc,
            nullptr,
            &m_modelTransformBuffer
            )
        );

    m_loadingComplete = true;
}

NOT: Önceki kod parçacığında kullanılan CreateDirectXBuffer yardımcı işlevi için surface eşleme kod örneğine bakın: SurfaceMesh.cpp, GetDataFromIBuffer.h. Artık cihaz kaynağı oluşturma işlemi tamamlandı ve ağ yüklenip güncelleştirme ve işleme için hazır olarak kabul ediliyor.

Yüzey çizgilerini güncelleştirme ve işleme

SurfaceMesh sınıfımızın özel bir güncelleştirme işlevi vardır. Her SpatialSurfaceMesh'in kendi dönüşümü vardır ve örneğimiz, dönüşümü almak için SpatialStationaryReferenceFrame için geçerli koordinat sistemini kullanır. Ardından GPU'da model sabiti arabelleği güncelleştirir.

void SurfaceMesh::UpdateTransform(
    ID3D11DeviceContext* context,
    SpatialCoordinateSystem^ baseCoordinateSystem
    )
{
    if (m_indexCount < 3)
    {
        // Not enough indices to draw a triangle.
        return;
    }

    XMMATRIX transform = XMMatrixIdentity();

    auto tryTransform = m_surfaceMesh->CoordinateSystem->TryGetTransformTo(baseCoordinateSystem);
    if (tryTransform != nullptr)
    {
        transform = XMLoadFloat4x4(&tryTransform->Value);
    }

    XMMATRIX scaleTransform = XMMatrixScalingFromVector(XMLoadFloat3(&m_surfaceMesh->VertexPositionScale));

    XMStoreFloat4x4(
        &m_constantBufferData.vertexWorldTransform,
        XMMatrixTranspose(
            scaleTransform * transform
            )
        );

    // Normals don't need to be translated.
    XMMATRIX normalTransform = transform;
    normalTransform.r[3] = XMVectorSet(0.f, 0.f, 0.f, XMVectorGetW(normalTransform.r[3]));
    XMStoreFloat4x4(
        &m_constantBufferData.normalWorldTransform,
        XMMatrixTranspose(
            normalTransform
        )
        );

    if (!m_loadingComplete)
    {
        return;
    }

    context->UpdateSubresource(
        m_modelTransformBuffer.Get(),
        0,
        NULL,
        &m_constantBufferData,
        0,
        0
        );
}

Yüzey çizgilerini işleme zamanı geldiğinde, koleksiyonu işlemeden önce bazı hazırlık çalışmaları yaparız. Geçerli işleme yapılandırması için gölgelendirici işlem hattını ve giriş derleyicisi aşamasını ayarladık. CameraResources.cpp holografik kamera yardımcı sınıfı şimdiye kadar görünüm/projeksiyon sabit arabelleği ayarlamıştır.

RealtimeSurfaceMeshRenderer::Render'dan:

auto context = m_deviceResources->GetD3DDeviceContext();

context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
context->IASetInputLayout(m_inputLayout.Get());

// Attach our vertex shader.
context->VSSetShader(
    m_vertexShader.Get(),
    nullptr,
    0
    );

// The constant buffer is per-mesh, and will be set as such.

if (depthOnly)
{
    // Explicitly detach the later shader stages.
    context->GSSetShader(nullptr, nullptr, 0);
    context->PSSetShader(nullptr, nullptr, 0);
}
else
{
    if (!m_usingVprtShaders)
    {
        // Attach the passthrough geometry shader.
        context->GSSetShader(
            m_geometryShader.Get(),
            nullptr,
            0
            );
    }

    // Attach our pixel shader.
    context->PSSetShader(
        m_pixelShader.Get(),
        nullptr,
        0
        );
}

Bu yapıldıktan sonra, tirelerimizi döngüye alıp her birine kendi çizmelerini söyleriz. NOT: Bu örnek kod herhangi bir tür frustum culling kullanacak şekilde iyileştirilmemiştir, ancak bu özelliği uygulamanıza eklemeniz gerekir.

std::lock_guard<std::mutex> guard(m_meshCollectionLock);

auto device = m_deviceResources->GetD3DDevice();

// Draw the meshes.
for (auto& pair : m_meshCollection)
{
    auto& id = pair.first;
    auto& surfaceMesh = pair.second;

    surfaceMesh.Draw(device, context, m_usingVprtShaders, isStereo);
}

Köşe ve dizin arabelleği, adım ve model dönüştürme sabit arabelleğinin ayarlanması tek tek tireler tarafından sorumludur. Windows Holographic uygulama şablonundaki dönen küpte olduğu gibi, statik uygulama kullanarak stereoskopik arabellekler oluştururuz.

SurfaceMesh::D raw'dan:

// The vertices are provided in {vertex, normal} format

const auto& vertexStride = m_surfaceMesh->VertexPositions->Stride;
const auto& normalStride = m_surfaceMesh->VertexNormals->Stride;

UINT strides [] = { vertexStride, normalStride };
UINT offsets [] = { 0, 0 };
ID3D11Buffer* buffers [] = { m_vertexPositions.Get(), m_vertexNormals.Get() };

context->IASetVertexBuffers(
    0,
    ARRAYSIZE(buffers),
    buffers,
    strides,
    offsets
    );

const auto& indexFormat = static_cast<DXGI_FORMAT>(m_surfaceMesh->TriangleIndices->Format);

context->IASetIndexBuffer(
    m_triangleIndices.Get(),
    indexFormat,
    0
    );

context->VSSetConstantBuffers(
    0,
    1,
    m_modelTransformBuffer.GetAddressOf()
    );

if (!usingVprtShaders)
{
    context->GSSetConstantBuffers(
        0,
        1,
        m_modelTransformBuffer.GetAddressOf()
        );
}

context->PSSetConstantBuffers(
    0,
    1,
    m_modelTransformBuffer.GetAddressOf()
    );

context->DrawIndexedInstanced(
    m_indexCount,       // Index count per instance.
    isStereo ? 2 : 1,   // Instance count.
    0,                  // Start index location.
    0,                  // Base vertex location.
    0                   // Start instance location.
    );

Surface Eşleme ile işleme seçenekleri

Surface Mapping kod örneği, yalnızca yüzey mesh verilerinin gizli olarak işlenmesi ve yüzey mesh verilerinin ekranda işlenmesi için kod sunar. Seçtiğiniz yol (veya her ikisi) uygulamanıza bağlıdır. Bu belgedeki her iki yapılandırmayı da inceleyeceğiz.

Holografik efekt için tıkanıklık arabelleklerini işleme

Geçerli sanal kamera için işleme hedef görünümünü temizleyerek başlayın.

AppMain.cpp dosyasından:

context->ClearRenderTargetView(pCameraResources->GetBackBufferRenderTargetView(), DirectX::Colors::Transparent);

Bu bir "ön işleme" geçişidir. Burada, ağ işleyiciden yalnızca derinliği işlemesini isteyerek bir oklüzyon arabelleği oluşturacağız. Bu yapılandırmada işleme hedef görünümü eklemiyoruz ve mesh işleyicisi piksel gölgelendiricisi aşamasını nullptr olarak ayarlayarak GPU'nun piksel çizme zahmetine neden olmamasını sağlıyor. Geometri derinlik arabelleğine rasterleştirilecek ve grafik işlem hattı burada duracak.

// Pre-pass rendering: Create occlusion buffer from Surface Mapping data.
context->ClearDepthStencilView(pCameraResources->GetSurfaceDepthStencilView(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

// Set the render target to null, and set the depth target occlusion buffer.
// We will use this same buffer as a shader resource when drawing holograms.
context->OMSetRenderTargets(0, nullptr, pCameraResources->GetSurfaceOcclusionDepthStencilView());

// The first pass is a depth-only pass that generates an occlusion buffer we can use to know which
// hologram pixels are hidden behind surfaces in the environment.
m_meshCollection->Render(pCameraResources->IsRenderingStereoscopic(), true);

Surface Mapping occlusion arabelleğine karşı ekstra derinlik testi ile hologramlar çizebiliriz. Bu kod örneğinde, küpteki pikselleri bir yüzeyin arkasındaysa farklı bir renkle işleriz.

AppMain.cpp dosyasından:

// Hologram rendering pass: Draw holographic content.
context->ClearDepthStencilView(pCameraResources->GetHologramDepthStencilView(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

// Set the render target, and set the depth target drawing buffer.
ID3D11RenderTargetView *const targets[1] = { pCameraResources->GetBackBufferRenderTargetView() };
context->OMSetRenderTargets(1, targets, pCameraResources->GetHologramDepthStencilView());

// Render the scene objects.
// In this example, we draw a special effect that uses the occlusion buffer we generated in the
// Pre-Pass step to render holograms using X-Ray Vision when they are behind physical objects.
m_xrayCubeRenderer->Render(
    pCameraResources->IsRenderingStereoscopic(),
    pCameraResources->GetSurfaceOcclusionShaderResourceView(),
    pCameraResources->GetHologramOcclusionShaderResourceView(),
    pCameraResources->GetDepthTextureSamplerState()
    );

SpecialEffectPixelShader.hlsl'den alınan koda göre:

// Draw boundaries
min16int surfaceSum = GatherDepthLess(envDepthTex, uniSamp, input.pos.xy, pixelDepth, input.idx.x);

if (surfaceSum <= -maxSum)
{
    // The pixel and its neighbors are behind the surface.
    // Return the occluded 'X-ray' color.
    return min16float4(0.67f, 0.f, 0.f, 1.0f);
}
else if (surfaceSum < maxSum)
{
    // The pixel and its neighbors are a mix of in front of and behind the surface.
    // Return the silhouette edge color.
    return min16float4(1.f, 1.f, 1.f, 1.0f);
}
else
{
    // The pixel and its neighbors are all in front of the surface.
    // Return the color of the hologram.
    return min16float4(input.color, 1.0f);
}

Not:GatherDepthLess yordamımız için bkz. Surface Mapping kod örneği: SpecialEffectPixelShader.hlsl.

Ekrana yüzey örgü verilerini işleme

Ayrıca yüzey örgülerini stereo ekran arabelleklerine de çizebiliriz. Aydınlatmalı tam yüzler çizmeyi seçtik, ancak tel çerçeve çizmekte, işlemeden önce tireleri işlemekte, doku haritası uygulamakta vb. serbestsiniz.

Burada kod örneğimiz mesh işleyicisine koleksiyonu çizmesini söyler. Bu kez yalnızca derinlik geçişi belirtmiyoruz, bir piksel gölgelendiricisi ekleyip geçerli sanal kamera için belirttiğimiz hedefleri kullanarak işleme işlem hattını tamamlayacak.

// Spatial Mapping mesh rendering pass: Draw Spatial Mapping mesh over the world.
context->ClearDepthStencilView(pCameraResources->GetSurfaceOcclusionDepthStencilView(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

// Set the render target to the current holographic camera's back buffer, and set the depth buffer.
ID3D11RenderTargetView *const targets[1] = { pCameraResources->GetBackBufferRenderTargetView() };
context->OMSetRenderTargets(1, targets, pCameraResources->GetSurfaceDepthStencilView());

// This drawing pass renders the surface meshes to the stereoscopic display. The user will be
// able to see them while wearing the device.
m_meshCollection->Render(pCameraResources->IsRenderingStereoscopic(), false);

Ayrıca bkz.