空間クエリ

空間クエリは、リモート レンダリング サービスに対して、どのオブジェクトが領域内に配置されているかを確認するための操作です。 空間クエリは、ユーザーが指しているオブジェクトを特定するなど、対話処理を実装するために頻繁に使用されます。

すべての空間クエリは、サーバーで評価されます。 したがって、これらのクエリは非同期操作であり、結果が届くまでネットワーク待ち時間に応じた遅延が発生します。

レイ キャスト

"レイ キャスト" は空間クエリです。ここで、指定の位置から開始し特定の方向を指すレイを交差するオブジェクトが、ランタイムによってチェックされます。 あまりに遠くにあるオブジェクトを検索しないようにするために、レイの最大距離も最適化のために与えられます。 各フレームで数百のレイ キャストを実行することは、サーバー側で計算上可能ですが、各クエリによってネットワーク トラフィックも生成されるため、フレームあたりのクエリ数はできるだけ少なく保つ必要があります。

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
            }
        }
    });
}

3 種類のヒット コレクション モードがあります。

  • Closest: このモードでは、最も近いヒットだけが報告されます。
  • Any: レイが何かにヒット "するかどうか" を把握したいだけで、正確には何がヒットしたのかは気にしない場合は、このモードをお勧めします。 このクエリはかなりの低コストで評価できますが、アプリケーションはごくわずかです。
  • All: このモードでは、レイに沿ったすべてのヒットが、距離で並べ替えられて報告されます。 最初のヒット以外にも必要な場合を除き、このモードは使用しないでください。 MaxHits オプションを使用して、報告されるヒット数を制限します。

レイ キャストの検討対象から除外するオブジェクトを選択するには、HierarchicalStateOverrideComponent コンポーネントを使用できます。

ヒット結果

レイ キャスト クエリの結果は、ヒットの配列です。 ヒットしたオブジェクトがなかった場合、配列は空になります。

ヒットには次のプロパティがあります。

  • HitEntity: どのエンティティがヒットしたか。
  • SubPartId: どの submeshMeshComponent でヒットしたか。 MeshComponent.UsedMaterials にインデックスを作成し、その時点で素材を検索するために使用できます。
  • HitPosition: レイがオブジェクトと交差するワールド空間の位置。
  • HitNormal: 交差の位置にあるメッシュのワールド空間表面法線。
  • DistanceToHit: レイの開始位置からヒットまでの距離。
  • HitType: レイによってヒットしたもの: TriangleFrontFaceTriangleBackFace、または Point。 既定では、ARR は両面をレンダリングするため、ユーザーに表示される三角形は必ずしも正面に向いているとは限りません。 コード内で TriangleFrontFaceTriangleBackFace とを区別する場合、最初に正しい方向の顔でモデルが作成されていることを確認します。

空間クエリ

"空間クエリ" を使用すると、ユーザー定義のボリュームと交差する MeshComponents がランタイムによってチェックできます。 個々のチェックが個々の三角形ではなく、シーン内の各メッシュ パーツの境界に基づいて実行されるため、このチェックはパフォーマンスが高くなります。 最適化のため、ヒットするメッシュ コンポーネントの最大数を指定できます。
このようなクエリはクライアント側で手動で実行できますが、大規模なシーンでは、サーバーでこれを計算した方が桁違いに高速である場合があります。

次のコード例は、軸平行境界ボックス (AABB) に対してクエリを実行する方法を示しています。 クエリのバリアントでは、方向付けられた境界ボックス ボリューム (SpatialQueryObbAsync) と球体ボリューム (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
                }
            }
        });
}

API ドキュメント

次のステップ