PhysX3.4文档(14) -- Scene Queries

Scene Queries


Introduction


PhysX 在 PxScene 中提供了一些方法,用于对场景中的Actor和附加形状执行碰撞查询。有三种类型的查询:光线投射、扫描和重叠,每种查询都可以返回单个结果或多个结果。从广义上讲,每个查询遍历包含场景对象的剔除结构,使用 GeometryQuery 函数执行精确测试(请参见几何查询),并累积结果。在精确测试之前或之后可能会发生过滤。

该场景使用两种不同的查询结构,一种用于PxRigidStaticacter,另一种用于PxRigidBodyactor(PxRigidDynamic和PxArticulationLink)。这两种结构可以配置为使用不同的剔除实现,具体取决于所需的速度/空间特性(请参见 PxPruningStructureType)。

Basic queries


Raycasts


PxScene::raycast() 查询将用户定义的光线与整个场景相交。raycast() 查询最简单的用例是查找沿给定光线的最接近的命中,如下所示:

PxScene* scene;
PxVec3 origin = ...;                 // [in] Ray origin
PxVec3 unitDir = ...;                // [in] Normalized ray direction
PxReal maxDistance = ...;            // [in] Raycast max distance
PxRaycastBuffer hit;                 // [out] Raycast results

// Raycast against all static & dynamic objects (no filtering)
// The main result from this call is the closest hit, stored in the 'hit.block' structure
bool status = scene->raycast(origin, unitDir, maxDistance, hit);
if (status)
    applyDamage(hit.block.position, hit.block.normal);

在此代码段中,PxRaycastBuffer 对象用于接收来自光线投射查询的结果。如果命中了 raycast(),则对 raycast() 的调用将返回 true。hit.hadBlock 也设置为 true,如果有命中。光线投射的距离必须在 [0, inf) 范围内。

光线投射结果包括位置、法线、命中距离、形状和演员,以及三角形网格和高度场的具有 UV 坐标的面索引。在使用查询结果之前,请先检查PxHitFlag::ePOSITION,eNORMAL,eDISTANCE,eUV标志,因为在某些情况下它们没有设置。

Sweeps


PxScene::sweep() 查询在几何上类似于 raycast():PxGeometry 形状从具有指定最大长度的方向单位Dir 的指定初始姿势扫描,以查找几何图形与场景对象的影响点。扫描的最大距离必须在 [0, inf) 范围内,并且将被夹紧到文件 PxScene.h 中定义的PX_MAX_SWEEP_DISTANCE。

允许的形状是盒子,球形,胶囊和凸形。

PxSweepBuffer 对象用于接收来自 sweep() 查询的结果:

PxSweepBuffer hit;              // [out] Sweep results
PxGeometry sweepShape = ...;    // [in] swept shape
PxTransform initialPose = ...;  // [in] initial shape pose (at distance=0)
PxVec3 sweepDirection = ...;    // [in] normalized sweep direction
bool status = scene->sweep(sweepShape, initialPose, sweepDirection, sweepDistance, hit);

扫描结果包括位置、法线、命中距离、形状和演员,以及三角形网格和高度场的面索引。

Overlaps


PxScene::overlap() 查询在由指定形状包围的区域中搜索场景中任何重叠的对象。该区域被指定为变换后的方框、球体、胶囊或凸几何图形。

PxOverlapBuffer 对象用于接收来自 overlap() 查询的结果:

PxOverlapBuffer hit;            // [out] Overlap results
PxGeometry overlapShape = ...;  // [in] shape to test for overlaps
PxTransform shapePose = ...;    // [in] initial shape pose (at distance=0)

PxOverlapBuffer hit;
bool status = scene->overlap(overlapShape, shapePose, hit);

重叠结果仅包括演员/形状和面部索引,因为没有单一的交点。

Touching and blocking hits


对于具有多个结果的查询,我们会区分触摸和阻止命中。命中是触及还是阻止由用户实现的过滤逻辑进行选择。直观地说,阻挡命中会阻止光线投射或沿其路径扫荡的进一步进展,并且会记录触摸命中,但允许射线或扫描继续进行。因此,多次命中查询将返回最接近的阻止命中(如果存在),以及任何更接近的触摸命中。如果没有阻止命中,则将返回所有触摸命中。

有关详细信息,请参阅筛选部分。

Query modes


Closest hit


所有三种查询类型的默认操作模式均为"最接近的命中"。该查询查找所有阻塞命中,选择距离最小的命中,并在 PxHitBuffer::block 成员中报告它。

  • 对于 overlap() 查询,选择任意阻止命中作为报告的阻止命中(对于所有 overlap() 命中,距离被视为零)。

Any hit


所有三种查询类型都可以在"任意命中"模式下运行。这是对查询系统的性能提示,指示无需查找最接近的命中 - 遇到的任何命中都可以。此模式最常用于布尔阻塞/非阻塞查询。性能改进可能是 3 倍或更多,具体取决于方案。要激活此模式,请使用PxQueryFlag::eANY_HIT过滤器数据标志并将其设置为PxQueryFilterData对象,例如:

PxQueryFilterData fd;
fd.flags |= PxQueryFlag::eANY_HIT; // note the OR with the default value
bool status = scene->raycast(origin, unitDir, maxDistance, hit,
                             PxHitFlags(PxHitFlag::eDEFAULT), fdAny);

Multiple hits


所有三种查询类型(光线投射、重叠、扫描)还可以报告场景中对象的多次命中。

  • 要为光线投射激活此模式,请使用 PxRaycastBuffer 构造函数,其中包含用户提供的用于触摸命中的缓冲区。
  • 在此模式下,所有命中默认为"触摸"类型,并记录在PxRaycastBuffer::touchs数组中。

例如:

PxScene* scene;
PxVec3 origin = ...;                 // [in] Ray origin
PxVec3 unitDir = ...;                // [in] Normalized ray direction
PxReal maxDistance = ...;            // [in] Raycast max distance

const PxU32 bufferSize = 256;        // [in] size of 'hitBuffer'
PxRaycastHit hitBuffer[bufferSize];  // [out] User provided buffer for results
PxRaycastBuffer buf(hitBuffer, bufferSize); // [out] Blocking and touching hits stored here

// Raycast against all static & dynamic objects (no filtering)
// The main result from this call are all hits along the ray, stored in 'hitBuffer'
scene->raycast(origin, unitDir, maxDistance, buf);
for (PxU32 i = 0; i < buf.nbTouches; i++)
    animateLeaves(buf.touches[i]);

相同的机制用于重叠(使用PxOverlapBuffer与PxOverlapHit[])和扫描(PxSweepBuffer与PxSweepHit[])。

Multiple hits with blocking hit


在上面多次点击的片段中,我们只期望触及点击。如果遇到阻塞命中和触摸命中,则会在 PxHitBuffer::块成员中报告,并且触摸缓冲区将仅包含更接近的触摸命中。这种组合在诸如子弹穿过窗户(在途中打破它们)或树叶(使它们沙沙作响)直到它们撞到阻挡物体(混凝土墙)的情况下很有用:

// same initialization code as in the snippet for multiple hits
bool hadBlockingHit = scene->raycast(origin, unitDir, maxDistance, buf);
if (hadBlockingHit)
    drawWallDecal(buf.block);
for (PxU32 i = 0; i < buf.nbTouches; i++)
{
    assert(buf.touches[i].distance <= buf.block.distance);
    animateLeaves(buf.touches[i]);
}
  • 默认情况下,当提供触摸缓冲区时,假定命中是触摸,并且筛选器回调应返回 PxQueryHitType::eBLOCK 以表示命中正在阻塞。有关详细信息,请参阅筛选。

  • 对于 overlap() 查询,即使遇到阻止命中并设置了 PxQueryFlag::eNO_BLOCK 标志,也会记录所有触摸命中。

Filtering


过滤控制如何从场景查询结果中排除形状以及如何报告结果。所有三种查询类型都支持以下筛选参数:

  • PxQueryFilterData结构,包含PxQueryFlags和PxFilterData
  • 一个可选的 PxQueryFilterCallback

PxQueryFlag::eSTATIC, PxQueryFlag::eDYNAMIC


PxQueryFlag::eSTATIC 和 PxQueryFlag::eDYNAMIC 标志控制查询是否应包含来自静态和/或动态查询结构的形状。这是过滤掉所有静态/动态形状的最有效方法。例如,将力应用于区域中所有动力学的爆炸效果可以使用球形重叠查询,并且只有PxQueryFlag::eDYNAMIC标志才能排除所有静态,因为力不能应用于静态对象。默认情况下,统计信息和动态都包含在查询结果中。

例如:

PxScene* scene;
PxVec3 origin = ...;                 // [in] Ray origin
PxVec3 unitDir = ...;                // [in] Normalized ray direction
PxReal maxDistance = ...;            // [in] Raycast max distance
PxRaycastBuffer hit;                 // [out] Raycast results

// [in] Define filter for static objects only
PxQueryFilterData filterData(PxQueryFlag::eSTATIC);

// Raycast against static objects only
// The main result from this call is the boolean 'status'
bool status = scene->raycast(origin, unitDir, maxDistance, hit, PxHitFlag::eDEFAULT, filterData);

PxQueryFlag::ePREFILTER, PxQueryFlag::ePOSTFILTER


场景查询分三个阶段执行:宽相、中相和窄相。

  • 宽相位遍历全局场景空间分区结构,以找到中窄相位的候选项。
  • 中相遍历三角形网格和高度场内部剔除结构,以在宽相报告的网格中找到三角形的较小子集。
  • 窄相位执行精确的相交测试(光线投射()查询的光线测试,以及扫描()和重叠()查询的精确扫描形状测试或重叠测试)。

若要在查询中实现自定义筛选,请使用所需的筛选逻辑设置 PxQueryFlag::ePREFILTER 和/或 PxQueryFlag::ePOSTFILTER 标志和子类 PxQueryFilterCallback。

  • 预滤波发生在中相和窄相之前,并允许在可能昂贵的精确碰撞测试之前有效地丢弃形状。对于三角形网格、高度场、凸面和大多数扫描,这些测试比仅涉及简单形状(如球体、胶囊和盒子)的光线投射和重叠测试更昂贵。
  • 后过滤发生在窄相测试之后,因此可以使用测试结果(例如PxRaycastHit.position)来确定是否应丢弃命中。这些结果可以通过命中输入参数访问后过滤回调(PxQueryFilterCallback::p ostFilter)。例如,使用static_cast<PxRaycastHit&>(hit),访问特定于光线投射查询的数据,以及类似的重叠(PxOverlapHit)和扫描(PxSweepHit)。

筛选回调的实现返回 PxQueryHitType 结果。

  • eNONE indicates that the hit should be discarded.
  • eBLOCK indicates that the hit is blocking.
  • eTOUCH indicates that the hit is touching.

每当使用非零 PxHitCallback::nbTouches 和 PxHitCallback::touchs 参数调用 raycast()、sweep() 或 overlap() 查询时,将报告 eTOUCH 类型命中(触摸距离< = 块距离)与最接近的 eBLOCK 类型命中数不进一步。例如,若要记录光线投射查询中的所有命中,请始终返回 eTOUCH。

从筛选器回调返回 eTOUCH 要求命中缓冲区查询参数具有非零 ::touchs 数组,否则 PhysX 将在检查的构建中生成错误并丢弃任何触摸命中。

eBLOCK 不应从重叠()的用户筛选器返回。这样做将导致未定义的行为,并将发出警告。如果设置了 PxQueryFlag::eNO_BLOCK 标志,则 eBLOCK 将自动转换为电子触控,并禁止显示警告。

PxQueryFlag::eANY_HIT


使用此标志可强制查询将第一次遇到的命中(可能不是最接近的)报告为阻止命中。性能可能快三倍以上,具体取决于方案。对于与附近相交对象的长光线投射/扫描,或与多个相交对象重叠,可以预期最佳增益。

Also see PxHitFlag::eMESH_ANY

PxQueryFlag::eNO_BLOCK


如果要覆盖从筛选器返回到 eTOUCH 的 eBLOCK 值,或者在预期没有阻止命中的情况下(在本例中,此标志用作性能提示),请使用此标志。然后,无论过滤器回调返回值如何,所有命中都将报告为触摸。使用此标志时,提供给查询的命中回调/缓冲区对象需要具有非零 PxHitBuffer::touchs 缓冲区。只有在接触命中缓冲区溢出的情况下,才应期望显著的性能提升。

此标志将覆盖预筛选器函数和后筛选器函数的返回值,因此以前作为阻塞返回的命中将作为触摸返回。

PxFilterData fixed function filtering


PxFilterData提供了一个快速的固定功能滤波器,PxFilterData是内置滤波方程使用的4 * 32位位掩码。每个形状都有一个位掩码(通过 PxShape::setQueryFilterData()设置),查询也有一个位掩码。

批处理查询和未批处理查询以不同的方式使用查询数据(有关批处理查询,请参见下文)。对于未打入的查询,将应用以下规则:

  • 如果查询的位掩码全部为零,则自定义筛选和交集测试将照常进行。
  • 否则,如果查询的位掩码和形状的位掩码的按位 AND 值为零,则跳过该形状

或者换句话说:

PxU32 keep = (query.word0 & object.word0)
           | (query.word1 & object.word1)
           | (query.word2 & object.word2)
           | (query.word3 & object.word3);

此硬编码公式可以提供简单的筛选,同时避免筛选回调的函数调用开销。例如,若要模拟 PhysX 2 活动组的行为,请按如下方式定义组:

enum ActiveGroup
{
    GROUP1    = (1<<0),
    GROUP2    = (1<<1),
    GROUP3    = (1<<2),
    GROUP4    = (1<<3),
    ...
};

创建形状时,可以将它们分配给组,例如 GROUP1:

PxShape* shape;                      // Previously created shape

PxFilterData filterData;
filterData.word0 = GROUP1;
shape->setQueryFilterData(filterData);

或多个组,例如 GROUP1 和 GROUP3:

PxShape* shape;                      // Previously created shape

PxFilterData filterData;
filterData.word0 = GROUP1|GROUP3;
shape->setQueryFilterData(filterData);

执行场景查询时,选择查询处于活动状态的组(例如 GROUP2 和 GROUP3),如下所示:

PxScene* scene;
PxVec3 origin = ...;                 // [in] Ray origin
PxVec3 unitDir = ...;                // [in] Normalized ray direction
PxReal maxDistance = ...;            // [in] Raycast max distance
PxRaycastBuffer hit;                 // [out] Raycast results

// [in] Define what parts of PxRaycastHit we're interested in
const PxHitFlags outputFlags = PxHitFlag::eDISTANCE | PxHitFlag::ePOSITION | PxHitFlag::eNORMAL;

// [in] Raycast against GROUP2 and GROUP3
PxQueryFilterData filterData = PxQueryFilterData();
filterData.data.word0 = GROUP2|GROUP3;

bool status = scene->raycast(origin, unitDir, maxDistance, hit, outputFlags, filterData);

User defined hit callbacks for unbounded results


查询有时可能会返回非常多的结果(例如,具有非常大的对象或对象密度较高的区域中的查询),并且保留足够大的内存缓冲区的开销可能过高。类 PxRaycastCallback、PxSweepCallback 和 PxOverlapCallback 为此类方案提供了基于回调的高效解决方案。例如,具有PxRaycastCallback回调的raycast查询将通过多个虚拟PxHitCallback::p rocessTouches()回调返回所有触摸命中:

struct UserCallback : PxRaycastCallback
{
    UserData data;
    virtual PxAgain processTouches(const PxRaycastHit* buffer, PxU32 nbHits)
        // This callback can be issued multiple times and can be used
        // to process an unbounded number of touching hits.
        // Each reported touching hit in buffer is guaranteed to be closer than
        // the final block hit after the query has fully executed.
    {
        for (PxU32 i = 0; i < nbHits; i++)
            animateLeaves(buffer[i], data);
    }
    virtual void finalizeQuery()
    {
        drawWallDecal(this->block, data);
    }
};

PxScene* scene;
PxVec3 origin = ...;                 // [in] Ray origin
PxVec3 unitDir = ...;                // [in] Normalized ray direction
PxReal maxDistance = ...;            // [in] Raycast max distance

UserCallback cb; cb.data = ...;
scene->raycast(origin, unitDir, maxDistance, cb); // see UserCallback::processTouches

在此代码片段中,raycast() 查询可能会多次调用 processTouches,所有触摸命中都已裁剪为全局最接近的阻塞命中。

  • 请注意,如果所有 eTOUCH 结果都不适合提供的触摸缓冲区,并且还发现了阻止命中,则查询的开销可能高达两倍。
  • 另请参阅 PxQueryFlag::eNO_BLOCK

Batched queries


PhysX 支持通过 PxBatchQuery 接口对场景查询进行批处理。使用此 API 可以简化多线程实现。

PhysX 版本 3.4 中已弃用批处理查询功能。

  • PxBatchQuery 接口便于同时批处理和执行多个查询。PxBatchQuery 缓冲光线投射、重叠和扫描查询,直到调用 PxBatchQuery::execute()。
  • 使用 PxScene::createBatchQuery(const PxBatchQueryDesc& desc) 创建一个 PxBatchQuery 对象。
  • 硬编码的筛选公式不用于批处理查询。取而代之的是两个过滤器着色器,分别在(PxBatchQueryPreFilterShader)和(PxBatchQueryPostFilterShader)之后运行精确的每个形状碰撞测试。请参阅 PxBatchQueryDesc::p reFilterShader 和 PxBatchQueryDesc::p ostFilterShader。
  • BatchQueryFilterData::filterShaderData 将通过 constantBlock 参数进行复制并传递到过滤器着色器。
  • 结果将写入 PxBatchQueryDesc 中的用户定义缓冲区 PxBatchQueryMemory,其顺序与查询在 PxBatchQuery 对象中排队的顺序相同。
  • 所使用的每种查询类型(光线投射、重叠、扫描)的结果和命中缓冲区是单独指定的。
  • 可以在每次批处理查询执行调用之前更改这些缓冲区。SDK 将为具有 NULL 结果的批处理查询生成警告,或者针对相应查询类型(光线投射、重叠或扫描)命中缓冲区。

Volume Caching


PxVolumeCache 提供了一种加速场景查询的机制。此类为指定卷中的对象实现缓存,并提供类似于 PxScene 的 API,用于执行光线投射、重叠和扫描。当在同一个模拟框架内或在以后的帧中多次查询同一局部空间区域内的对象时,PxVolumeCache 可以提供性能提升。

PhysX 版本 3.4 中已弃用卷缓存功能。

PxVolumeCache的一些预期用例是:

  • 一个粒子系统,具有从空间定位云中对每个粒子执行的许多射线照射。
  • 多个短程字符控制器光线投射在角色周围的同一区域内。
  • 跨多个帧缓存查询结果,可以使用前一帧上的较大卷填充缓存(可能沿预期的移动方向拉伸),然后使用较小的卷进行查询。

缓存具有最大容量,在 PxScene::createVolumeCache() 中分别为动态和静态对象指定。

出于多线程访问的目的,缓存上的任何操作都算作对场景的读取调用。

Filling the Cache


要填充缓存,请调用 PxVolumeCache::fill()。这将在场景中查询与几何图形定义的体积重叠的对象,并将结果转换并存储在内部缓冲区中,直至达到静态和动态对象的最大大小。cacheVolume 仅支持 PxBoxGeometry、PxSphereGeometry 和 PxCapsuleGeometry。调用将始终重新填充静态和动态内部缓存,即使新卷完全位于以前的缓存卷中也是如此。它返回类型为 PxVolumeCache::FillStatus 的结果。

如果场景查询子系统自上次填充以来已更新,则针对缓存的后续查询(光线投射、重叠、扫描、forEach)将使用相同的卷自动重新填充缓存。对于静态和动态,更新状态是独立跟踪的,因此查询可能只重新填充动态的缓存,同时重用静态的有效缓存结果。如果任何填充或重新填充的尝试失败,则缓存无效,任何后续查询都将尝试填充缓存。

Querying the Cache


PxVolumeCache 提供了一个用于光线投射、扫描和重叠的 API,类似于场景查询 API。签名的主要区别在于 PxVolumeCache 查询不支持单对象缓存。查询结果通过 PxVolumeCache::Iterator::shapes() 回调报告,查询可能会多次调用回调以提供多批结果。

  • Raycasts、重叠和针对有效缓存的扫描将仅返回与缓存卷重叠的结果,但保证返回所有此类卷。
  • 针对无效缓存的光线投射、重叠和扫描将回退到场景查询。在这种情况下,可能会返回不与缓存卷重叠的结果。

由于缓存会在场景已更改的任何查询上自动重新填充,因此这两个条件可确保对完全位于缓存卷内的缓存的查询将始终返回与查询场景完全相同的形状。如果查询不完全位于缓存卷内(并且缓存有效),则只会返回与缓存卷重叠的那些形状。如果对从未调用过 fill() 的缓存发出查询,则会报告错误。

缓存还提供了一种低级 forEach() 机制,用于循环访问缓存的对象。如果在从未调用过 fill() 的缓存上执行 forEach(),则它将返回而不报告错误。如果缓存无效,forEach() 将直接从场景中检索与缓存卷重叠的形状。此过程涉及临时缓冲区的分配,如果分配失败,forEach() 将发出错误消息并返回。

This code snippet shows how to use PxVolumeCache:

PxScene* scene;
PxVec3 poi = ...;                    // point of interest
PxVec3 origin = ...;                 // [in] Ray origin
PxVec3 unitDir = ...;                // [in] Normalized ray direction
PxReal maxDistance = ...;            // [in] Raycast max distance
PxRaycastBuffer hit;                 // [out] Raycast results
const PxU32 maxStatics = 32, maxDynamics = 8;

// persistent cache, valid until invalidated by object movement,
// insertion or deletion
PxVolumeCache* cache = scene->createVolumeCache(maxStatics, maxDynamics);
cache->setMaxNbStaticShapes(64); cache->setMaxNbDynamicShapes(16);

// fill the cache using a box geometry centered around the point of interest
cache->fill(PxBoxGeometry(PxVec3(1.0f)), PxTransform(position));

...

// Perform multiple raycast queries using the cache
PxRaycastBuffer hit;
const bool status = cache->raycast(origin, unitDir, maxDistance, hit);

// low level iterator for stored actor/shape pairs
struct UserIterator : PxVolumeCache::Iterator
{
    UserData userData;
    virtual void shapes(PxU32 count, const PxActorShape* actorShapePairs)
    {
        for (PxU32 i = 0; i < count; i++)
           doSomething(actorShapePairs[i].actor, actorShapePairs[i].shape, userData);
    }
}   iter;

// invoke UserIterator::shapes() callback for all actor/shape pairs in the cache
cache->forEach(iter);

Single Object Caching


加速场景查询的另一种特殊情况机制是使用 PxQueryCache 的单对象缓存。

  • 此缓存可以为任何操作模式下的光线投射和扫描查询提供额外的加速和内存节省。
  • 缓存对象定义应首先测试哪个形状。对于具有高时间一致性的查询,这可以显著提高性能。捕获这种一致性的一个好策略是简单地用上一帧的 eBLOCK 结果(最后一个阻塞形状)填充给定查询的缓存对象。
  • 请注意,使用过去的触摸命中(用eTOUCH标志记录)进行缓存可能是不正确的,因为它将被解释为阻止并覆盖任何过滤。

例如,AI 可见性查询很有可能会为多个帧返回相同的视线阻塞形状。将光线投射查询与正确填充的 PxQueryCache 对象结合使用将允许 PhysX 在遍历内部空间分区结构之前测试单个形状,并且在"缓存命中"的情况下,可以完全绕过遍历。例如:

PxScene* scene;
PxVec3 origin = ...;                 // [in] Ray origin
PxVec3 unitDir = ...;                // [in] Normalized ray direction
PxReal maxDistance = ...;            // [in] Raycast max distance
PxRaycastBuffer hit;                 // [out] Raycast results

// Per-raycast persistent cache, valid from one frame to the next
static PxQueryCache persistentCache;

// Define cache for current frame:
// - if there was a hit in the previous frame, use the cache.
// - otherwise do not (PhysX requires given cache has a valid shape pointer)
const PxQueryCache* cache = persistentCache.shape ? &persistentCache : NULL;

// Perform a raycast query using the cache
const bool status = scene->raycast(origin, unitDir, maxDistance, hit,
                                   PxHitFlags(PxHitFlag::eDEFAULT),
                                   PxQueryFilterData(), NULL, cache);
if(status)
{
    // We hit a shape. Cache it for next frame.
    persistentCache.shape = hit.block.shape;
    persistentCache.faceIndex = hit.block.faceIndex;
}
else
{
    // We did not hit anything. Reset the cache for next frame.
    persistentCache = PxQueryCache();
}

缓存在查找最接近的阻止命中或使用eANY_HIT标志的查询中也很有用。在这种情况下,首先测试以前最近的对象可以使 PhysX 尽早缩短查询距离,从而减少总窄相位碰撞测试,并提前退出遍历。

PhysX 不检测陈旧的指针,因此在删除形状时,应用程序负责缓存对象的有效性。

重叠不支持单次命中阻止缓存。

PxPruningStructureType


PhysX SDK 提供了不同的修剪结构,用于加速场景查询。本段描述了它们之间的差异。

Generalities


场景查询系统使用两种不同的加速结构,即分层网格和 AABB 树。

网格在 O(n) 时间内快速构建,查询在 O(1) 和 O(N) 时间之间执行,具体取决于对象在空间中的均匀分布,当所有对象都聚集成在同一网格单元中时,病理最坏情况下的性能为 O(N)。

树在 O(n log(n)) 时间内构建,但具有单个结果的查询通常在 O(log(n)) 时间内运行。返回多个结果的查询将遍历更多树,最坏的情况是在 O(n) 时间内返回场景中所有对象的查询。当对象位置更改时,如果相同的拓扑维护时间过长,并且在病理情况下,查询性能可能会降低到 O(n) 时间,则树容易受到退化的影响。

必须根据要添加或删除的对象或由于位置或几何形状的变化而进行的对象AABB更新来不断修改加速结构。为了最大限度地降低成本,修改将尽可能长时间地推迟。因此,添加或删除对象或更新AABB发生在摊销的恒定时间内,修改成本推迟到更改"提交"。这发生在下一个后续查询或下一个 fetchResults() 或下一个 fetchQueries() 时。若要强制立即提交,请调用 PxScene::flushQueryUpdates() 函数。

提交过程的确切细节取决于 PxSceneDesc 中指定的 staticStructure 和 dynamicStructure 的值。

为避免因插入到内部场景查询数据结构中而触发自动调整大小,请提前预留空间。请参阅 PxSceneDesc::maxNbStaticShapes 和 PxSceneDesc::maxNbDynamicShapes。

PxPruningStructureType::eNONE


加速度结构类似于分层网格。提交更改需要完全重建。如果您希望很少或从不更新此结构中的对象,这是一个不错的选择。

PxPruningStructureType::eSTATIC_AABB_TREE


加速度结构是一棵树。提交更改需要完全重建。通常不建议这样做,但如果场景中的静态Actor是在初始化时创建的,并且此后不进行修改,则它可能是 staticStructure 的不错选择。如果经常添加或删除静态几何图形,则默认eDYNAMIC_AABB_TREE设置通常是更好的选择,尽管它的内存占用量高于eSTATIC_AABB_TREE。

PxPruningStructureType::eDYNAMIC_AABB_TREE


在这种情况下,将同时使用树和网格,并且每个查询都搜索树和网格。

该树最初是由第一个提交构建的。构建树后,提交更改将按如下方式进行:: * 根据它所包含对象的更新和删除,对树进行重新拟合。*添加的对象入到网格中。此类添加或移除网格中当前对象,或更改网格中对象的 AABB,都会导致重新生成该对象。

此外,在 fetchResults() 期间,在由 PxScene 的 dynamicTreeRebuildRateHint 属性控制的多个帧上,以增量方式构建一个新树。生成开始时,它将包括当前树和网格中的所有对象。当它完成时,在一些帧之后,新树将根据自构建开始以来的任何AABB更改或删除进行重新拟合,然后替换当前树。自生成开始以来添加的任何对象都将保留在网格中。

要强制立即完全重建,请致电PxScene::forceDynamicTreeRebuild()。这在以下情况下可能很有用:

  • 缓慢的重建速率通常是可取的,但偶尔大量添加的对象会在网格中产生高占用率,特别是如果添加是局部的,以便仅对少数网格单元施加压力。
  • 您正在远距离移动许多对象,因为改装可能会显著降低当前树的质量

PxSceneQueryUpdateMode


可以定义在 PxScene::fetchResults 中完成哪些与场景查询相关的工作。

默认情况下,fetchResults 将在模拟期间同步更改的边界,并在修剪器中更新场景查询边界,这项工作是强制性的。其他工作可以是可选的,基于PxSceneQueryUpdateMode:

  • eCOMMIT_ENABLED_BUILD_ENABLED允许在 fetchResults 期间执行新的 AABB 树构建步骤,此外,还会在应用任何更改的位置调用 pruner 提交。在提交期间,PhysX 会重新调整动态场景查询树,如果构建了新树并且构建完成,则该树将与当前 AABB 树交换。
  • eCOMMIT_DISABLED_BUILD_ENABLED允许在 fetchResults 期间执行新的 AABB 树构建步骤。不调用 Pruner 提交,这意味着在 fetchResults 之后的第一个场景查询期间将进行 refit,或者可能由 PxScene::flushSceneQueryUpdates() 方法强制进行。
  • eCOMMIT_DISABLED_BUILD_DISABLED不会执行进一步的场景查询工作。场景查询更新需要手动调用,请参阅 PxScene::sceneQueriesUpdate。建议在 fetch 之后立即调用 PxScene::sceneQueriesUpdateResults,因为修剪结构未更新。

PxPruningStructure


提供对预计算的修剪结构的访问,该结构用于加速针对新添加的Actor的场景查询。

可以为PxScene::addActors提供修剪结构。然后,Actor 场景查询形状将直接合并到场景 AABB 树中,而无需 AABB 树重新计算:

// Create pruning structure from given actors.
PxPruningStructure* ps = PxPhysics::createPruningStructure(&actors[0], (PxU32)actors.size());
// Add actors into a scene together with the precomputed pruning structure.
PxScene::addActors(*ps);
ps->release();

PxPruningStructure 对象可以与其执行组件一起序列化为集合。

有关 PxPruningStructure 的使用,请参阅代码片段 SnippetPrunerSerialization。

PxPruningStructure的一个典型用例是一个大型世界场景,其中紧密定位的actor块被流式传输。

Merge process


场景查询加速结构的合并过程因PxPruningStructureType而异:* eSTATIC_AABB_TREE - 修剪结构直接合并到场景的AABtree中。这可能会使树失衡,建议在某个时候重新计算静态树。* eDYNAMIC_AABB_TREE - 修剪结构被合并到临时修剪结构中,直到场景的新优化的AABB树被计算出来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值