Condividi tramite


Query spaziali

Le query spaziali sono operazioni che consentono di chiedere al servizio di rendering remoto quali oggetti si trovano in un'area. Le query spaziali vengono spesso usate per implementare interazioni, ad esempio capire a quale oggetto stia puntando un utente.

Tutte le query spaziali vengono valutate nel server. Di conseguenza, le query sono operazioni asincrone e i risultati arrivano con un ritardo che dipende dalla latenza di rete.

Raycast

Un ray cast è una query spaziale in cui il runtime controlla quali oggetti intersecano un raggio, a partire da una determinata posizione e puntando a una determinata direzione. Come ottimizzazione, viene anche fornita una distanza massima del raggio, in modo da non cercare oggetti troppo lontani. Anche se l'esecuzione di centinaia di cast di raggi ogni fotogramma è fattibile sul lato server, ogni query genera anche traffico di rete, quindi il numero di query per fotogramma deve essere mantenuto il più basso possibile.

async void CastRay(RenderingSession session)
{
    // trace a line from the origin into the +z direction, over 10 units of distance.
    RayCast rayCast = new RayCast(new Double3(0, 0, 0), new Double3(0, 0, 1), 10);

    // only return the closest hit
    rayCast.HitCollection = HitCollectionPolicy.ClosestHit;

    RayCastQueryResult result = await session.Connection.RayCastQueryAsync(rayCast);
    RayCastHit[] hits = result.Hits;
    if (hits.Length > 0)
    {
        var hitObject = hits[0].HitObject;
        var hitPosition = hits[0].HitPosition;
        var hitNormal = hits[0].HitNormal;
        var hitType = hits[0].HitType;
        // do something with the hit information
    }
}
void CastRay(ApiHandle<RenderingSession> session)
{
    // trace a line from the origin into the +z direction, over 10 units of distance.
    RayCast rayCast;
    rayCast.StartPos = {0, 0, 0};
    rayCast.EndPos = {0, 0, 10};

    // only return the closest hit
    rayCast.HitCollection = HitCollectionPolicy::ClosestHit;

    session->Connection()->RayCastQueryAsync(rayCast, [](Status status, ApiHandle<RayCastQueryResult> result)
    {
        if (status == Status::OK)
        {
            std::vector<RayCastHit> hits;
            result->GetHits(hits);

            if (hits.size() > 0)
            {
                auto hitObject = hits[0].HitObject;
                auto hitPosition = hits[0].HitPosition;
                auto hitNormal = hits[0].HitNormal;
                auto hitType = hits[0].HitType;

                // do something with the hit information
            }
        }
    });
}

Sono disponibili tre modalità di raccolta delle collisioni (hit):

  • Closest: in questa modalità viene segnalato solo l'hit più vicino.
  • Any: preferisce questa modalità quando tutto quello che vuoi sapere è se un raggio avrebbe colpito qualsiasi cosa, ma non importa cosa è stato colpito esattamente. La valutazione di questa query può essere notevolmente più economica, ma ha anche poche applicazioni.
  • All: in questa modalità vengono segnalati tutti i riscontri lungo il raggio, ordinati in base alla distanza. Non usare questa modalità a meno che non siano realmente necessarie altre collisioni oltre la prima. Limitare il numero di collisioni segnalate con l'opzione MaxHits.

Per escludere in modo selettivo gli oggetti da considerare per i raycast è possibile usare il componente HierarchicalStateOverrideComponent.

Risultati

Il risultato di una query raycast è una matrice di collisioni. Se non è stato colpito alcun oggetto, la matrice è vuota.

Una collisione ha le proprietà seguenti:

  • HitEntity: quale entità è stata raggiunta.
  • SubPartId: quale sottomesh è stato raggiunto in un MeshComponent. Si può usare per l'indicizzazione in MeshComponent.UsedMaterials e per cercare il materiale in quel punto.
  • HitPosition: posizione dello spazio globale in cui il raggio intersecò l'oggetto.
  • HitNormal: normale della superficie spaziale globale della mesh nella posizione dell'intersezione.
  • DistanceToHit: distanza dalla posizione iniziale del raggio all'hit.
  • HitType: ciò che viene colpito dal raggio: TriangleFrontFaceo TriangleBackFacePoint. Per impostazione predefinita, ARR esegue il rendering del doppio lato , in modo che i triangoli visualizzati dall'utente non siano necessariamente frontali. Se si vuole distinguere tra TriangleFrontFace e TriangleBackFace nel codice, assicurarsi che i modelli vengano creati prima con indicazioni corrette sul viso.

Query spaziali

Una query spaziale consente al runtime di verificare quale MeshComponents interseca con un volume definito dall'utente. Questo controllo è efficiente perché il controllo individuale viene eseguito in base ai limiti di ogni parte della mesh nella scena, non su un singolo triangolo. Come ottimizzazione, è possibile fornire un numero massimo di componenti hit mesh.
Anche se una query di questo tipo può essere eseguita manualmente sul lato client, per le scene di grandi dimensioni può essere più veloce per il server di calcolo.

Nel codice di esempio seguente viene illustrato come eseguire query su un rettangolo di selezione allineato all'asse .AABB. Le varianti della query consentono anche volumi di rettangoli di selezione orientati (SpatialQueryObbAsync) e volumi sphere (SpatialQuerySphereAsync).

async void QueryAABB(RenderingSession session)
{
    // Query all mesh components in a 2x2x2m cube.
    SpatialQueryAabb query = new SpatialQueryAabb();
    query.Bounds = new Microsoft.Azure.RemoteRendering.Bounds(new Double3(-1, -1, -1), new Double3(1, 1, 1));
    query.MaxResults = 100;

    SpatialQueryResult result = await session.Connection.SpatialQueryAabbAsync(query);
    foreach (MeshComponent meshComponent in result.Overlaps)
    {
        Entity owner = meshComponent.Owner;
        // do something with the hit MeshComponent / Entity
    }
}
void QueryAABB(ApiHandle<RenderingSession> session)
{
    // Query all mesh components in a 2x2x2m cube.
    SpatialQueryAabb query;
    query.Bounds.Min = {-1, -1, -1};
    query.Bounds.Max = {1, 1, 1};
    query.MaxResults = 100;

    session->Connection()->SpatialQueryAabbAsync(query, [](Status status, ApiHandle<SpatialQueryResult> result)
        {
            if (status == Status::OK)
            {
                std::vector<ApiHandle<MeshComponent>> overlaps;
                result->GetOverlaps(overlaps);

                for (ApiHandle<MeshComponent> meshComponent : overlaps)
                {
                    ApiHandle<Entity> owner = meshComponent->GetOwner();
                    // do something with the hit MeshComponent / Entity
                }
            }
        });
}

Documentazione sull'API

Passaggi successivi