CullDistanceVolume源码笔记

Posted by SlothSimon's Skytree on November 8, 2017

Introduction

CullDistanceVolume的用法是设置多个CullDistanceSizePair,在其内部的物体根据自身大小决定在多近的距离内才显示出来,超出该距离就不绘制在玩家画面上。

CullDistanceSizePair可以当做一个极简的Map成员来看待,比如Size=100的物体,可以查到其Distance=1000,那么玩家距离该物体1000米内才能看见它,超出1000米就不见了。形状复杂的物体,其Size由其边界Bound决定,如何计算在此就不展开了。

这样做的好处是,灵活地节约性能

譬如一把Size=100的小椅子距离你有10000UU,那根本没必要渲染它,反正玩家也不会注意,或者根本看不见,并不影响玩家的游玩;但一座远景的大山,Size=8000,作为设计师你希望让玩家在很远的地方就能看见,那么就可以针对这个Size设置CullDistance。

当然了,有人会问那山上有座Size=100的小宝塔,我也希望玩家在很远的地方就能看见呢?那就得设置宝塔的PrimitiveComponent,其中有属性MaxDrawDistanceAllowCullDistanceVolume,勾选后者,然后MaxDrawDistance设为较大的值,就可以实现这个功能。

(若不勾选AllowCullDistanceVolume则取MaxDrawDistanceCullDistanceVolume中较小的值)

Source Code

CullDistanceVolume的继承结构是这样,Actor-->Brush-->Volume-->CullDistanceVolume

诸多属性中只有两个是新定义的,而方法也只有两个是在游戏运行过程中会用到的:

UCLASS(hidecategories=(Advanced, Attachment, Collision, Volume))
class ACullDistanceVolume
	: public AVolume
{
	GENERATED_UCLASS_BODY()
	
	/**
	 * Array of size and cull distance pairs. The code will calculate the sphere diameter of a primitive's BB and look for a best
	 * fit in this array to determine which cull distance to use.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=CullDistanceVolume)
	TArray<struct FCullDistanceSizePair> CullDistances;

	/**
	 * Whether the volume is currently enabled or not.
	 */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=CullDistanceVolume)
	uint32 bEnabled:1;
	
	public:
	
	... // Editor functions
	

	/**
	 * Returns whether the passed in primitive can be affected by cull distance volumes.
	 *
	 * @param	PrimitiveComponent	Component to test
	 * @return	true if tested component can be affected, false otherwise
	 */
	static bool CanBeAffectedByVolumes( UPrimitiveComponent* PrimitiveComponent );

	/** 
	  * Get the set of primitives and new max draw distances defined by this volume. 
	  * Presumes only primitives that can be affected by volumes are being passed in.
	  */
	void GetPrimitiveMaxDrawDistances(TMap<UPrimitiveComponent*,float>& OutCullDistances);

两个属性很简单,不介绍了。两个方法则都是在UpdateCullDistanceVolume()时使用到。

CanBeAffectedByVolumes()

返回True的条件为

组件有非空Owner && 组件是Static && 组件允许CullDistanceVolume && 组件可见 && 组件不是模板 && 组件Owner所在World不为空 && 组件所在Scene是当前的Scene

GetPrimitiveMaxDrawDistances()

这里主要是处理多个地方设置了Distance后,究竟该以哪个Distance为最终的CullDistance

  1. 找到CullDistances中最接近该物体大小的成员,然后取其Distance赋给CurrentCullDistance
  2. 若该物体CullDistance > 0,则重设物体CullDistance=Min(CullDistance, CurrentCullDistance);否则,直接设为CurrentCullDistance

和UE3的对比

相比UE3,UE4优化了一些细节。

  • 只在编辑器用到的函数通过宏来控制是否编译
  • World变量在UE3中是通过全局变量UWorld * GWorld来存储和获得使用,CanBeAffectedByVolumes()并没有判断GWorld是否为空;但在UE4中是用UWorldProxy GWorld来存储,而获取则是通过Actor->GetWorld()来实现。
    • 猜测这样的改进是因为,UE4中游戏时(不考虑PIE),有可能同时存在多个World,譬如UT4中的即时回放就是通过新建另一个World来实现的。
  • UE3中Actor有属性bNoDelete,在CanBeAffectedByVolumes()中与物体是否Static取或,但在UE4中没有该属性。
  • UE3中GetPrimitiveMaxDrawDistances()是对所有UPrimitiveComponent迭代,然后在OutCullDistances中查找是否有该组件;而UE4则直接迭代OutCullDistances中所有组件,降低了复杂度。

Review

考虑到这两个函数的用法都是和UWolrd::UpdateCullDistanceVolume()有关,下节就学习一下这个函数的内容。