【UE4】Bounds 详解

本文使用 UE 4.26 ActionRPG 示例项目,对 Bounds 进行学习和总结。

一、Bounds 是什么 / 有什么用

在这里插入图片描述

  任何的游戏引擎,最终呈现出在屏幕上的效果,都是经过和很多变换和计算从三维世界空间,转成屏幕像素空间才能够被玩家看到的,这个过程中,摄像机的位置,FOV,物体的位置,透明度,深度值等等信息,都能影响物体在最终屏幕上的位置和大小,如果每个物体的每个点都要计算,计算量太过庞大,所以每个物体先进行可见性剔除,对于完全不可见的物体,就可以在流程中很早地舍弃掉,减少很多计算。
  Bounds 就是用于进行可见性剔除的,对于 Bounds 都不在摄像机的视锥体范围内的物体,直接就不画了。
  UE 中 Bounds 全都是 AABB(Axis-Aligned Bounding Box)的,即轴平行包围盒,类型是 FBoxSphereBounds。不管是实时计算的,还是固定(Fixed)的,都会因为旋转而变化,使得始终与世界的 XYZ 轴平行。
在这里插入图片描述

  上图中可以看到,蓝色的长方体线框就是实际用于计算的 AABB Bounds,黄色的球形线框是用于显示这个物体是否被剔除的,如果看不到黄色线框,物体就被剔除了。所以可以看到,在镜头转动至看不到蓝色线框的时候,黄色线框也就消失了,物体的 Mesh 也就不绘制了。关于 Bounds,最重要的就是上边这个 GIF 了(武器没有消失是因为和主角是单独的两个 Actor,用不同的 Bounds 计算剔除)。

二、Bounds 的一些设置和代码

2.1 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 上的配置(这些参数都是运行时可调的!可以很方便地看到效果!!):
在这里插入图片描述

1. Bounds Scale - Bounds 倍率

在这里插入图片描述

  • 所在类: UPrimitiveComponent
  • 用途:
    在 CapsuleComponent 上就可以配,但是实际是不生效的,在 SkeletalMeshComponent 上设置才生效,就是字面意思,Bounds 的倍率,下图是设置成 4 的效果:
    在这里插入图片描述
2. Use Attach Parent Bounds - 使用父节点 Bounds

在这里插入图片描述

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

在这里插入图片描述

  上图左右两个 BP 位移的区别就是 Mesh 上左边勾选了 Component Use Fixed Skel Bounds,右边没有勾,可以看到区分有两个:

  1. 顾名思义,使用了 Fixed Bounds,Boungs 不会根据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 在镜头转至特定角度后直接消失的情况。
7. Consider All Bodies for Bounds - 考虑所有部位

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 特效,稍微改了一下):
请添加图片描述

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

  解决方案就是像 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 的时候,角色博了一个动画,同时还附带了一个镜头动画,镜头中角色头没了,或者上半身没了,或者下肢没了等,都是这个问题。

四、参考资料

猜你喜欢

转载自blog.csdn.net/Bob__yuan/article/details/119813418