acecase 发表于 2022-5-30 21:34

【UE4】Bounds 详解

History:

[*]2021.11.10 - 更新黄色球和蓝色框用途,Mesh 中的 Bounds 选项以及Fixed和缩放的关系
[*]2021.12.18 - Actor AttachToParent
<hr/>用 UE 4.26 ActionRPG 示例项目,对 Bounds 进行学习和总结。一、Bounds 是什么 / 有什么用



开门见山:
Bounds 是用于进行 可见性剔除(蓝色框)以及LOD计算(黄色球) 的。
[*]可见性剔除
任何的游戏引擎,最终呈现出在屏幕上的效果,都是经过和很多变换和计算从三维世界空间,转成屏幕像素空间才能够被玩家看到的,这个过程中,摄像机的位置,FOV,物体的位置,透明度,深度值等等信息,都能影响物体在最终屏幕上的位置和大小,如果每个物体的每个点都要计算,计算量太过庞大,所以每个物体利用八个顶点组成的长方体 Bounds 进行可见性剔除,对于完全不可见的物体,就可以在流程中很早地舍弃掉,减少很多计算。
UE 中用于可见性剔除的 Bounds 全都是 AABB(Axis-Aligned Bounding Box)的,即轴平行包围盒,类型是 FBoxSphereBounds。不管是实时计算的,还是固定(Fixed)的,都会因为旋转而变化,使得始终与世界的 XYZ 轴平行。



蓝色框用于可见性剔除

上图中可以看到,蓝色的长方体线框就是实际用于计算的 AABB Bounds,如果蓝色长方体的八个顶点都不在视窗内,物体就被剔除了(随之那个黄色的球体框也看不见了)。所以可以看到,在镜头转动至看不到蓝色线框的时候,黄色线框也就消失了,物体的 Mesh 也就不绘制了。关于 Bounds 的可见性剔除,最重要的就是上边这个 GIF 了(武器没有消失是因为和主角是单独的两个 Actor,用不同的 Bounds 计算剔除)。
2. Mesh LOD 计算
可以看到,上图中 Bounds 除了蓝色的长方体方框,还有与之对应的黄色球体框,它的目的,是用于计算物体在屏幕内的大小(Scale)的,这个 Scale 会用于美术人员制作的 Mesh LOD。
这是因为 Mesh LOD 的计算和配置其实不是真的 Distance,而是 Screen Size,屏幕占比,比如屏占比 1.0 到 0.5 都是 LOD0,即最高等级 LOD,0.5 到 0.3 是 LOD1,即粗糙一点的 LOD,以此类推。所以黄色框的是球形,不管摄像机从哪个角度看,距离一定,Screen Size 就一定。



黄色球体框用于 LOD 计算

二、Bounds 的一些设置和代码

2.1 Bounds 的显示命令


[*]预览模式下



Viewport 中预览模式下查看 Bounds


[*]运行时
Play之后,在命令行窗口输入Show Bounds即可显示所有 Bounds。
一个 Actor 如果没有 Mesh,空的 Actor,是没有 Bounds 的,一个带有 Mesh 的默认 Character(Mesh 是空的),也是没有 bounds 的。


可以看到在没有运行时,选中 Actor,是有蓝色框和黄色框的,但是实际运行起来是看不到的,这个因为预览时默认是有一个 Sphere 的,所以有 Bounds。
如果给 MeshComponent 添加一个 SkeletalMesh,比如一把剑,Bounds 就有了:


这里也可以看出来,Bounds 和真正可见的部分,不一定是吻合的,大部分情况下 Bounds 是能完全包住可见部分的,这样就没问题(如果Bounds 太大,会导致不管在场景中怎么转摄像机,都不能剔除这个物体,增加无用计算量);但是如果 Bounds 小了,就会出现,我认为能看到的东西,转了一点点镜头突然就看不到了的情况,这个在 ( 三、Bounds 相关的一些问题)中记录。
2.2 Bounds 的设置

主要记录 SkeletalMeshComponent 上的配置,以及 Component 上的配置(这些参数都是运行时可调的!可以很方便地看到效果!!):



Bounds 相关配置

1. Bounds Scale - Bounds 倍率



[*]所在类: UPrimitiveComponent
[*]用途:在 CapsuleComponent 上就可以配,但是实际是不生效的,在 SkeletalMeshComponent 上设置才生效,就是字面意思,Bounds 的倍率,下图是设置成 4 的效果:


2. Use Attach Parent Bounds - 使用父节点 Bounds



[*]所在类: USceneComponent
[*]用途:
如果勾选了则这个 Component 的 Bounds 不自己计算,而是直接使用他 Parent 的,从注释也可以看到,这个选项如果选了,可以大幅提升一个很多个 Component 在一个 Parent 下的效率。
最常见的就是,一个主角,头、上身、胳膊、下半身、腿、分别是单独的 SkeletalMeshComponent,都挂在一个总的 MeshComponent 下,勾选和不勾选的对比如下(其中左图是所有 Mesh 都没有选择使用 Parent,右图五个子 Mesh 以及主 Mesh 都选择了使用 Parent,可以看到减少了很大的计算量,但是由于主 Mesh 是空的,所有 Bounds 大小不太对):


如果主 Mesh 也勾选了 UseAttachParent,那么会使用 RootComponent 的 Bounds,一般来说 RootComponent 是胶囊体,即使用胶囊体的 Bounds。
特殊:

[*]角色的武器一般都是挂在人身上的(武器Actor AttachToParent到角色身上),不管怎么切换挂点(背上,手上),都可以其实 Parent 都是角色的 RootComponent。如果武器的 RootComponent 选择了 UseAttachParent,那么武器的 Bounds 直接就使用角色的了(相当于是两个 Actor 用一个 Bounds,和上面说的一个 Actor 上的各个Mesh不一样)



武器没有 UseAttachParent



武器使用 UseAttachParent

3. Include Component Location Into Bounds


在 USkeletalMeshComponent::CalcBounds 中,会判断是否勾选,如果勾选了,则会根据 ComponentLocation重新计算 Bounds:
FBoxSphereBounds NewBounds = CalcMeshBound(RootBoneOffset, bHasValidBodies, LocalToWorld);
if (bIncludeComponentLocationIntoBounds)
{
    const FVector ComponentLocation = GetComponentLocation();
    NewBounds = NewBounds + FBoxSphereBounds(ComponentLocation, FVector(1.0f), 1.0f);
}
把所有 Bounds 都关了,只剩头的,下图左右分别是没有勾选 Include Component Location Into Bounds 以及勾选的效果:


4. Use Bounds from Master Pose omponent


代码里 USkinnedMeshComponent::CalcMeshBound(...):
// Use MasterPoseComponent's PhysicsAsset if told to
else if (MasterPoseComponentInst && bCanUsePhysicsAsset && bUseBoundsFromMasterPoseComponent)
{
    NewBounds = MasterPoseComponentInst->Bounds;
}
目前没用过,没有效果演示。
5. Skip Bounds Update When Interpolating


使用的地方,即插值就不更新了:
void USkeletalMeshComponent::FinalizeAnimationUpdate()
{
    // ...
    // update bounds
    if(bSkipBoundsUpdateWhenInterpolating)
    {
      if(AnimEvaluationContext.bDoEvaluation)
      {
            // Cached local bounds are now out of date
            InvalidateCachedBounds();
            UpdateBounds();
      }
    }
    else
    {
      // Cached local bounds are now out of date
      InvalidateCachedBounds();
      UpdateBounds();
    }
}
6. Component Use FixedSkel Bounds - 使用固定 Bounds



Component Use FixedSkel Bounds

上图左右两个 BP 位移的区别就是 Mesh 上左边勾选了 Component Use Fixed Skel Bounds,右边没有勾,可以看到区分有两个:
1. 顾名思义,使用了 Fixed Bounds,Bounds 不会根据Mesh Pose 的变化而更新,减少了不少的计算量
2. 由于使用的是 Fixed,所以默认回比自动计算的大,为了把 Mesh 包住(用的就是默认 Mesh 的 Bounds)


实际在角色 Mesh 的界面中,原始的 Bounds 是比角色大一圈的(如上边 GIF 左边的),并不贴合(可以通过左侧的 Asset Details 中 Bounds 的设置调整,Positive 就是 XYZ 正方向的延展,单位是 cm,UE 里正常的长度单位都是厘米),这个需要使用 Fixed Bounds 才生效(但是要注意这个改小了,就可能会出现 Mesh 没有被 Bounds 包住的情况)。


注意:
1. 使用了 Fixed,且 Bounds 没有把 Mesh 完整包裹住(比如播了一个 Mesh 动画),就会出现文章开头那样,Mesh 在镜头转至特定角度后直接消失的情况。
2. Fix 不 Fix 和Scale 没有关系,只是是不是会实时计算大小,Mesh 调整大小,Bounds 不管Fix 与否,都会随之变大


7. Consider All Bodies for Bounds - 考虑所有部位
目前没用过。
2.3 Mesh 中的 Bounds 选项



在 Mesh 的界面左侧,LOD 设置中可以设置 LOD Info,比如 LOD0 设的 Screen Size 是 1.0(一般都是) ,LOD1 的 Screen Size 设的 0.3,LOD2 设的 0.15, LOD3 设的 0.1,意思就是:
1.0 ~ 0.3,使用 LOD0;
0.3 ~ 0.15,使用 LOD1;
0.15 ~ 0.1,使用 LOD2;
0.1 ~ 0,使用 LOD3。


左上角会实时显示当前的 Current Screen Size,美术人员可以参照这个来制作和调整 LOD 参数(这些参数在 Mesh Info,Basic 里显示)。
如果想在 Mesh 的编辑界面看 Bounds,右上边三个选项,其中:

[*]Bound - 表示是否显示 Bounds
[*]In-game Bounds - 表示是否使用这个 Mesh 进行 Bounds 的计算,不勾则使用的是 Bones 即骨骼进行 Bounds 的计算(因为一个骨骼可以适配多个 Mesh)。
[*]Fixed Bounds - 即上文所说的是否使用 Fixed Bounds,和实际在角色 BP 中是一样的效果。
所以如果实际 BP 中使用的是这个 Mesh,则美术人员做 LOD 时需要勾选 In-game Bounds,如果实际使用角色时,使用 Fixed Bounds,美术人员制作 LOD 时也需要勾选,否则制作出的 LOD 实际使用效果就不对了。
2.4 Bounds 相关的代码、计算方式

所有 Bounds 都是 AABB 轴平行包围盒,所以像第一张图中看到的那样,计算部分代码为:


实际用于剔除的代码详见:USkinnedMeshComponent::CalcMeshBound,通过一次断点可以看到堆栈:



[*]轴平行
因为需要始终轴平行,所以可能看起来 Mesh 没有动,但是 Bounds 是变了的,比如默认 Place Actors 中的球(这是因为使用和局部坐标系轴平行的 Bounds 转到与世界坐标系轴平行的 Bounds):


用 Box 看的更清楚:


三、Bounds 相关的一些问题

由于 Bounds 直接影响物体是否可见,所以如果 Bounds 设置的有问题,很可能导致在物体应该可见的情况下,却看不到。
3.1 Bounds 过小时的问题

Bounds 过小,可能是因为距离太远,整个 Bounds 在屏幕中就是一个点了,这时候整个物体就看不见了(这种情况,很少见,且看不见了也并没有什么问题)。
还有一种需要注意的,就是 Bounds XYZ 的一个值特效小的时候(比如 Z 很小,就是 Bounds 很扁),如果镜头的正方向,正好与 Bounds 的 XY 平面平行,会由于计算进行精度误差(Bounds 的长方体在屏幕中就是一根线),导致整个物体看不见了。如下图所示(使用的时候 ActionRPG 的 P_Skill_03 特效,稍微改了一下):



Bounds 过小 / 过薄

出现这个问题的一个原因是,ParticleSystem 的特效,在更新 MeshRotation 的时候,Bounds 是不更新的(不知道是不是引擎的 Bug,还是设计如此),所以如果使用的 Mesh 是一个片状的形状的话(如上图),那么默认 Bounds 就是很扁的,当 Mesh 旋转成上图所示效果,Bounds 是完全包不住 Mesh 的:



Cascade 中旋转 Mesh, Bounds 不刷新

解决方案就是像 ActionRPG 一样,使用 Fixed Bounds:



[*]Tips:特效的 Bounds 是整个特效的设置,不是某一个发射器的,想从发射器的设置 Details 回到整个特效的设置,不需要关了重新打开,鼠标点击最右侧空白黑色处即可~~~
3.2 Bounds 过大的问题

Bounds 大不会出现上述稍微动一下镜头瞬间看不见了的 Bug,但是会增加计算量,比如粒子特效,发射出的粒子是随机的,Bounds 的计算就会根据速度等信息自动计算,但是这样计算出来的 Bounds 可能会很大(如下图,粒子实际飞不了那么远),而且一直在变化,也会有计算量,也可以用一个适当大小的 Fixed Bounds 解决。


3.3 Mesh 没有被 Bounds 包住的情况

在勾选了 Fixed Bounds 之后,很有可能在 Mesh 播放一些动画的时候 Mesh 从 Bounds 里出去了。就像文章一开头的 GIF 一样,出现镜头转动,Mesh 突然消失的情况。
这种问题也常见于头、上肢、下肢为分别不用的 Mesh Component 的时候,角色博了一个动画,同时还附带了一个镜头动画,镜头中角色头没了,或者上半身没了,或者下肢没了等,都是这个问题。

Ylisar 发表于 2022-5-30 21:38

写的真不错,关于Bounds和Fixed Bounds的几个点都讲的很透,点赞一波[赞]
页: [1]
查看完整版本: 【UE4】Bounds 详解