网易首页 > 网易号 > 正文 申请入驻

UE5 Chaos物理|物理查询的实现

0
分享至


【USparkle专栏】如果你深怀绝技,爱“搞点研究”,乐于分享也博采众长,我们期待你的加入,让智慧的火花碰撞交织,让知识的传递生生不息!

这是侑虎科技第1977篇文章,感谢作者南京周润发供稿。欢迎转发分享,未经作者授权请勿转载。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)

作者主页:

https://www.zhihu.com/people/xu-chen-71-65

射线检测接口

游戏业务常见的需求就是做各种射线检测/Overlap检测/Sweep检测,比如Character Movement Component。

下面例子是使用Visibility Channel做Line Trace,StaticMesh对其是Block的。




各种Trace,最终都调用到SceneTrace。


使用的TraceChannel,ObjectType等条件,最终都编码成四个4B的Word,下面是TraceChannel的例子。


物理查询,主要就是对AABB Tree的查询。

SceneTrace

ReadLock

要对物理场景做查询,首先加读锁。可以和其他Trace请求同时执行,但和写物理场景操作互斥。



然后获取加速结构Collection,里面有Static AABBTree和Dynamic AABBTree。


最后进到TSpatialAccelerationCollection::RaycastFast。

这里Types就是Buckets,TypeIdx是从0开始的下标,会逐个遍历Buckets,然后对里面的每个AABBTree,调用RaycastFast函数。


注意第一个Bucket存了非常大的AABB,也就是说如果场景内有很多大的物体,是会拖慢射线检测效率的。

TAABBTree::QueryImpl

比较大的函数,做AABBTree的遍历。

AABBTree非叶节点的遍历

经典的BVH检测方式,先检查射线和左右子树AABB是否相交,然后根据相交情况,不断对左右子树做遍历。加速方式为SIMD的应用,以及非递归的遍历AABBTree。


示意图如下:


SIMD版本的Slab算法值得一看,判断射线与AABB是否相交,并计算射线在AABB内经过的距离。全程无if...else...。


里面的VectorMax和VectorSwizzle的组合比较巧妙,用于在三个数中取最大的。

Slab算法简介

Slab算法(Slab Method)是计算机图形学和物理引擎中用于检测射线(Ray)与轴对齐包围盒(AABB)是否相交的最经典、最高效的算法。

它的核心思想非常简单直观:将3D的AABB盒子看作是三组无限大的“平板对”(Slabs)的交集。

假设射线的起点为P,方向为Dir,把射线写成时间t的方程:


射线进入每个“平板对”,都会有进入时间t0和离开时间t1,如果要与AABB发生重叠,三组t0和t1必定有都重叠的部分。

示意图如下:


处理叶节点

对于一个Leave节点,射线检测会执行RaycastFastSimd。


每个Leave节点默认最多有8个Element,它们没有位置关系,因此要逐个对这些Element做射线检测了。

对于Leave中的一个Element,计算和射线是否相交,会经过三道判断。第一道是检查Shape的AABB和射线是否相交,第二道是检查Shape的Channel和QueryChannel是否是Overlap或者Block关系,第三道是真正检查射线和Sphere、Box、凸包等几何体是否相交。前两道是轻量的,算BroadPhase,第三道很重,是NarrowPhase。



把Channel Response检查放到中间也是挺有意思的。按照正常人类思考逻辑,应该是射线碰到一个Geometry后,再判断这个Geometry对射线Channel响应是Block还是Overlap,但判断射线和Shape相交的操作比较重,尤其是面对凸包。因此从代码效率逻辑出发,就变成了不管Shape和射线是否真的相交,先判断Geometry对Channel的响应,如果是Ignore,就直接不管了。具体做法如下:

第一道,射线和AABB Bounds检查是否相交。

同样的,用SIMD判断射线和AABB是否相交,不再赘述。

第二道,射线检测用的Channel和Geometry的ResponseChannel & BlockChannel是否有重叠。

ResponseChannel & BlockChannel是两个int32,本身是每个Geometry的属性,但AABBTree为了效率,在Payload里都缓存了一份,为UnionQueryFilterData和UnionSimFilterData,CachedUniqueIdx则是所属UObject的UniqueIdx缓存。

具体的Channel Response过滤方法。

PrePreQueryFilter


首先从Word0中获取射线检测使用的Channel,然后检查Geometry自身的Word1和Word2,分别是BlockingChannel和OverlapChannel,做bit and操作,看是否有交集。


注意这里的函数名是PrePreQueryFilter,说明后面还有PreQueryFilter流程。

射线和各种几何体相交判断

第三道,真正的射线和几何体相交检测。


PreQueryFilter

几何检测前,先执行一些自定义逻辑的Filter做过滤,判断是否要做对Shape的射线检测,可以做游戏业务相关的操作。注意到例子中的射线检测是按照LineTrace BlockChannel做的,但目前OverlapChannel的Shape也还没被排除,排除操作就是在PreQueryFilter阶段做的,实际会调用FCollisionQueryFilterCallback::PreFilterBaseImp函数。

首先对比查询的Channel,和Shape的OverlapChannel & BlockingChannel,判断这次查询是Touch还是Block,称为CollisionQueryHitType。

如果是LineTraceSingle,那么只关心BlockingChannel,TouchChannel的Shape可以忽略。


查询的Touch/Block和Shape的ResponseChannel比对通过后,再判断IgnoreActors和IgnoreComponents是否需要过滤,LineTrace时通常会设置IgnoreActors。


FCollisionQueryFilterCallback::PreFilter检查通过后,正式进入射线和几何体相交计算。

世界空间坐标和Geometry局部空间坐标

此时射线的起点和方向都是世界坐标,在检测前先变换到Geometry的坐标空间,后面计算起来会更加方便。比如对Box检测时,直接用AABB射线检测算法即可。


射线和不同几何体相交计算

这里不仅要判断是否相交,还要得到射线接触的位置,初次接触几何体的距离,以及接触点的法线。每个几何体都实现了Raycast函数,观察不同几何体的实现。

TSphere

球形判断是最简单的。


令直线起点为P,方向为dir,直线方程为:


设圆心为C,半径为R,直线和圆相交,需要满足以下方程:


这是关于t的一元二次方程,用公式求出t即可。

有了时间t,就能快速求出交点坐标和交点法线了。


Tbox

最终执行的AABB,计算开销比球体高些。


对于三组Slab,都分别计算进出时间t0、t1,然后取交集,得到时间t,再得到交点。法线总是XYZ三个方向,计算t0属于哪个面即可。


FCapsule


Capsule可以想象成,到线段最短距离为r的点,形成的距离场,线段作为Capsule的轴心。


那么判断射线和胶囊体是否相交,计算这个轴心线段到射线的最短距离即可,具体计算上,需要找出一对距离最近的点。

真正困难的地方在于,线段上的最近点,位于线段中间。直接贴一个已有算法,核心是找出两个平面的相交线,理解起来需要点时间,但计算步骤并不多。

参考:

https://johnmayhk.wordpress.com/2012/02/02/shortest-distance-between-two-skew-lines/


如上所示,最短距离就是BD在MN上投影的长度,而MN同时垂直AB和CD,另外ABXCD得到的向量也同时垂直AB和CD,因此MN与(ABXCD)平行。由此可得到最短距离。

有了距离,就能解出两个点了。

UE也实现了这个算法,封装于SegmentDistToSegmentSafe函数,主要步骤如下:


当射线与Capsule相交后,还需要进一步求交点和normal。

分两种情况:

1. 与上下两个半球相交,normal从球心出发

2. 与中间圆柱体相交,计算圆柱体的normal

FConvex凸包

最复杂,使用了GJK算法,不再展开


和Shape发生碰撞后怎么办?

当前的Hit类型是ChaosInterface::FLocationHit,先填充Hit的这几个属性:


之后再给个机会执行FCollisionQueryFilterCallback::PostFilter,如处理bDiscardInitialOverlaps过滤等。

然后把Time作为Distance记下,表示射线已经打了多远,对后续Shape做射线检测时,将从最新的Distance开始,继续往后打射线。

回到AABB子树的遍历,如果要判断多个子树,但射线在之前已经Block了,就不用判断之后的子树了。


FHitResult的填充

FHitResult展开后有如下属性,其中大部分在之前射线发生碰撞时已经可以得到,只有黄线画出的几个需要看下如何获取。


PhysMat

命中点的物理材质,物理材质可以理解为类似渲染用的材质,只不过描述的是物体表面的摩擦力、密度等属性。当前已经有了FaceIndex,而Geometry中记录了FaceIndex和PhysicsMaterial的对应关系,因此可以得到PhysicsMaterial。


BoneName

命中点所属骨骼的名称。

对于示例中的StaticMesh,是没有骨骼的,因此是None,只有对于SkeletalMesh,这个BoneName才有意义。


观察SkeletalMesh的PhysicsAsset,多个骨骼上会挂接RigidBody,它们有各自对应的BodySetup,BoneName就存在BodySetup里。射线检测时,可以根据命中的GeometryParticle获取到BodyInstance,再获取到BodySetup,最终得到BoneName。


HitItem & ElementIndex

都属于一些Extra数据,不太重要。

HitItem是BodyInstance在整个PhysicsAsset中的序号,针对SkeletalMesh的情况。ElementIndex是Trace中Shape在Geometry的Shape数组中下标。对于示例中的StaticMeshComponent,它们都是0。


完整的LineTrace流程如下:


Overlap检测

第二种碰撞检测是Overlap检测,比LineTrace更复杂些,会判断一个体积内有没有和其他物体相交。这里以Box碰撞体为例,需要传入Box的位置和大小,区别于之前的LineTrace使用TraceChannel,这里使用了ObjectTypes,查询Box范围内重叠的其他Actor。但对于物理引擎而言,TraceChannel和ObjectTypes本质都是一样的,都是CollisionChannel。



首先,把查询的Geometry计算一个AABB包围盒,传入AABBTree做BroadPhase的检测。


AABBTree的左右子树Overlap

对左右子树分别做AABB的碰撞检测。



Leaf节点的处理

TAABBTreeLeafArray.OverlapFast

对Leaf中每个Payload Geometry,同样先做AABB碰撞检测。用了SIMD技术,四个Payload为一组。当AABB检测通过后,依然先执行PrePreFilter,做Channel之类的判断,再针对具体的Geometry Shape形状,做Narrow Phase的Overlap检测。


Shape可以有Sphere、Box、Capsule、凸包等各种几何形状,但它们都属于凸包。要判断两个凸包是否Overlap,就要用GJK算法,UE的实现是GJKIntersection函数,自然也是SIMD实现。


GJK算法

关于GJK算法的详细介绍,可以看这里:

看似简单的复杂问题,奇怪而优雅的解决方式(GJK算法)

https://www.bilibili.com/video/BV16P4y1q7GZ/?spm_id_from=333.337.search-card.all.click&vd_source=43d3ce94d2854f1bf3d667631d4a0e98

总体过程是不断通过Support函数,在两个凸包上找一对点,计算闵可夫斯基差,然后让形成的三角形包含原点。GJK算法有点复杂,不展开了,但不同Shape的Support函数实现可以看下,具体就是每个Shape的SupportCore函数。


所谓Support函数,就是给定一个方向,找到Shape在这个方向上最远的一个点,下图就是两个例子。

简单起见只看SupportCore普通版本,SIMD版本思想类似。

TBox

等价于对一个AABB求Support点。不难理解,AABB在某个方向上的最远点必定是8个顶点之一,因此只要依次判断三个轴,做三次方向二分即可,2^3 = 8。



TSphere

最简单,球的中心加上半径*方向即可。



FCapsule

看似复杂,实则和Sphere一样简单。Capsule的Support点必定出现在两个半球上,因此首先根据Direction方向确定是哪个半球,然后类似Sphere处理即可。



FConvex

需要遍历所有凸包的顶点,与方向做点乘,找到最远的点。


这里实现也比较有意思,寻找凸包的Support点,理论上用启发式的“爬山法”更快,即从上个Support点往后找邻接点,不用遍历所有顶点,而且UE是保存了边的连接关系的:


那为什么不用爬山法?个人猜测是以下几个原因:

  • 直接遍历Vertices数组,CPU Cache更友好,在顶点数量不多(也是大部分情况)情况下可能更快。

  • 爬山法需要记录上一次的Support点序号,但碰撞检测可能多个线程并行,这样就需要为每个线程都记录一份上次的Support点了,实现有点麻烦,而且得保证线程安全。

如果要得到Overlap深度,就要使用GJKPenetration算法, 结合了GJK与EPA,这里不展开了。

Sweep检测



最复杂的情况,以Box Sweep为示例,会得到Sweep路径上发生重叠的物体。

BroadPhase

类似Overlap,但把Sweep经过的整个路径,作为AABB,查询与左右子树的碰撞情况。



NarrowPhase

对于Leaf节点中的每个Geometry,同样经过PrePreFilter,AABB检测,PreFilter。

对Geometry中每个Shape的Sweep检测,最后依然使用GJK算法。


如果是TriangleMesh复杂碰撞怎么办?


首先,Shape就变成了FTriangleMeshImplicitObject,类似Mesh,没有凸包的限制,可以表示任何形状。因此,类似GJK这种精妙的几何碰撞算法都不再适用,只能回归最原始的三角形相交判断。


Raycast & Overlap & Sweep

BroadPhase相同,使用AABBTree。

NarrowPhase不使用GJK算法了,而是每个TriangleMesh自己有一个BVH树,用BVH的碰撞检测方式。BVH子节点存储了三角形,在检测子节点内部碰撞时,采样逐三角形检测方式。

比如这个例子,BVH树有379个节点,模型总共有5760个面,还是非常复杂的。

Tips

综合上述碰撞检测流程,可以对物理场景优化做一些指导:

  • 游戏中物体都设置专门的Simple Collision,避免查询复杂碰撞

  • Shape的碰撞检测效率排序:Sphere > Capsule > Box >> Convex >>>> Triangle Mesh,尽量用效率高的Shape做近似

  • 避免太长的LineTrace,以及范围太大的Overlap和Sweep

  • LineTrace查询的开销最低,玩法上尽量用它

文末,再次感谢南京周润发的分享, 作者主页:https://www.zhihu.com/people/xu-chen-71-65, 如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群: 793972859 )。

近期精彩回顾



特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
阴法唐中将带队入驻河北,省委书记程维高各种阻挠:我要去上诉

阴法唐中将带队入驻河北,省委书记程维高各种阻挠:我要去上诉

史海任我行
2025-08-31 16:07:04
人民日报刊文:让群众真切体会到清风正气在身边

人民日报刊文:让群众真切体会到清风正气在身边

澎湃新闻
2026-10-29 16:39:04
外国网友:中国是第一个迫使姆巴佩公开道歉的!卡塔尔+法国都失败了

外国网友:中国是第一个迫使姆巴佩公开道歉的!卡塔尔+法国都失败了

念洲
2026-10-28 13:28:45
沙明梅开二度,U15世界杯中国队3-2逆转战胜刚果(布)

沙明梅开二度,U15世界杯中国队3-2逆转战胜刚果(布)

懂球帝
2026-10-29 01:12:07
官方:墨菲、霍金斯和布雷塞尔退出斯诺克国锦赛

官方:墨菲、霍金斯和布雷塞尔退出斯诺克国锦赛

懂球帝
2026-10-29 20:17:44
知名经济学家:居民不花钱,其实是因为已经进入了极限状态

知名经济学家:居民不花钱,其实是因为已经进入了极限状态

午夜搭车a
2026-10-22 01:32:16
10月29日,大家期盼已久的2026退休养老金,究竟涨还是不涨?

10月29日,大家期盼已久的2026退休养老金,究竟涨还是不涨?

北海史记
2026-10-29 17:59:08
53岁木村拓哉新照外貌巨变!

53岁木村拓哉新照外貌巨变!

阿废冷眼观察所
2026-10-29 15:04:35
哈登、库里、德罗赞三大老将总得分差距越拉越大!

哈登、库里、德罗赞三大老将总得分差距越拉越大!

衔春信
2026-10-29 18:30:29
C罗社媒庆祝:感谢全世界的球迷,史上最佳金球奖也属于你们

C罗社媒庆祝:感谢全世界的球迷,史上最佳金球奖也属于你们

懂球帝
2026-10-29 21:40:47
男子拆外卖盒被割伤左手缝6针,费用约1.5万元,涉事商家:外卖盒出餐时完好

男子拆外卖盒被割伤左手缝6针,费用约1.5万元,涉事商家:外卖盒出餐时完好

齐鲁壹点
2026-10-28 17:16:57
太荒唐了!已婚女子和男网友约会,发生关系后累的睡着,被惊醒后拒绝给男网友钱,女子追悔莫及

太荒唐了!已婚女子和男网友约会,发生关系后累的睡着,被惊醒后拒绝给男网友钱,女子追悔莫及

丫头舫
2026-10-26 15:48:41
她大概是全地球最“恨”马云的女人,那年支付宝抽奖拿下一个亿,她乐坏了,立马辞掉国企工作满世界飞

她大概是全地球最“恨”马云的女人,那年支付宝抽奖拿下一个亿,她乐坏了,立马辞掉国企工作满世界飞

背包旅行
2026-10-27 12:12:27
噩耗传来:被中国隐藏40年的巨星陨落!

噩耗传来:被中国隐藏40年的巨星陨落!

华人星光
2026-10-27 12:20:10
也能治癌!华西医院全球首创试验,八成患者肿瘤停止生长!

也能治癌!华西医院全球首创试验,八成患者肿瘤停止生长!

健身狂人
2026-10-29 00:31:08
吴彦祖老婆走红毯,网友直呼:太辣眼!“疑似”跟黄多多拿错了衣服

吴彦祖老婆走红毯,网友直呼:太辣眼!“疑似”跟黄多多拿错了衣服

深度解析热点
2026-10-27 06:54:35
北京老太住养女家20年,临终给养女2万的卡,养女取钱时却傻眼了

北京老太住养女家20年,临终给养女2万的卡,养女取钱时却傻眼了

牛魔王与芭蕉扇
2025-03-19 15:55:23
最惨降级队?海牛半套阵容被哄抢!泰山欲“走马换马”?国安蹲边路小妖

最惨降级队?海牛半套阵容被哄抢!泰山欲“走马换马”?国安蹲边路小妖

刀锋体育
2026-10-29 10:56:37
23岁抗癌博主“奇迹夏夏”去世:5年历经截肢、25次化疗;确诊后遭父母放弃,独自攒钱治病,选好讣告照片,临终前父母出现送她最后一程

23岁抗癌博主“奇迹夏夏”去世:5年历经截肢、25次化疗;确诊后遭父母放弃,独自攒钱治病,选好讣告照片,临终前父母出现送她最后一程

台州交通广播
2026-10-29 02:41:51
普京:某些国家仍拥有殖民思维,用最卑劣的手段牟利,一个更加公正的多极世界正成为现实,不可逆转

普京:某些国家仍拥有殖民思维,用最卑劣的手段牟利,一个更加公正的多极世界正成为现实,不可逆转

每日经济新闻
2026-10-29 18:06:02
2026-10-29 22:11:00
侑虎科技UWA incentive-icons
侑虎科技UWA
游戏/VR性能优化平台
1637文章数 989关注度
往期回顾 全部

科技要闻

MacBook终于能触屏了:22999元起

头条要闻

女子父母给平台投600万成"董事" 对方称上市可拿上亿

头条要闻

女子父母给平台投600万成"董事" 对方称上市可拿上亿

体育要闻

超级黑马!球队不败 29岁的他得分榜前三

娱乐要闻

白百何被曝和二婚丈夫离婚 和男生逛街

财经要闻

两大娱乐巨头,谈崩了

汽车要闻

标配宁德时代电池 智己LS8 NEO纯电预售24.98万起

态度原创

旅游
健康
房产
公开课
军事航空

旅游要闻

山风掠过老王山,在贵州寻一段不被打扰的时光!

查不出猝死风险?可能是项目没做对

房产要闻

12.1亿!三亚挂出重磅宅地!

公开课

李玫瑾:为什么性格比能力更重要?

军事要闻

万斯致信伊朗安全高官试图重启谈判

无障碍浏览 进入关怀版