你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

空间查询

空间查询是一种操作,可用来询问远程渲染服务哪些对象位于一个区域中。 空间查询经常用于实现交互,如确定用户正在指向哪个对象。

所有空间查询都是在服务器上进行计算。 因此,查询是异步操作,结果会得到延迟,具体取决于网络延迟。

光线投射

光线转换是空间查询,运行时检查对象与光线相交、从给定位置开始并指向特定方向。 作为一种优化,还给出了最长光线距离,以免搜索太远的对象。 尽管在服务器端执行数百个光线转换在计算上是可行的,但每个查询也会生成网络流量,因此每个帧的查询数应尽可能低。

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

有三种命中收集模式:

  • Closest 在此模式下,只报告最近的命中。
  • Any 当你想要知道 的是光线是否会 击中任何东西时,首选此模式,但并不关心所击中的内容。 此查询的计算成本可能会大大降低,但几乎没有应用程序。
  • All 在此模式下,将报告沿光线的所有命中,按距离排序。 除非确实除了第一次命中还需要更多命中,否则请不要使用这种模式。 使用 MaxHits 选项可以限制报告的命中数。

若要有选择性地从光线投射的目标对象范围内排除对象,可以使用 HierarchicalStateOverrideComponent 组件。

命中结果

光线投射查询的结果是命中数组。 如果未命中任何对象,数组就是空的。

命中有以下属性:

  • HitEntity 命中了哪个 实体
  • SubPartId在 MeshComponent 中命中了哪个子消息。 可用于索引到 MeshComponent.UsedMaterials,并查找此时的材料
  • HitPosition 光线与对象相交的世界空间位置。
  • HitNormal 网格在交集位置处的世界空间表面法线。
  • DistanceToHit 从光线起始位置到命中距离。
  • HitType 光线击中的内容: TriangleFrontFaceTriangleBackFace Point。 默认情况下, ARR 呈现双面 ,以便用户看到的三角形不一定是正面的。 如果要区分 TriangleFrontFaceTriangleBackFace 代码,请首先确保使用正确的人脸方向创作模型。

空间查询

空间查询允许运行时检查哪些 MeshComponent 与用户定义的卷相交。 此检查是高性能的,因为单个检查基于场景中每个网格部件的边界执行,而不是基于单个三角形。 作为优化,可以提供最大的命中网格组件数。
虽然此类查询可以在客户端手动运行,但对于大型场景,服务器计算此查询的速度可能更快。

以下示例代码演示如何针对轴对齐边界框(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 文档

后续步骤