精华内容
下载资源
问答
  • Advanced Foliage Shader 4.01 - 强化植物Shader的专用插件;Advanced Foliage Shader 4.01 - 强化植物Shader的专用插件;
  • Unreal engine 4 Foliage mega pack
  • 这个扩展显示了新英格兰地区的现场树叶地图 支持语言:English
  • 之后,还有问题是大世界里,Foliage分布在多个关卡,默认Palette会显示已加载 Level的所有FoliageType,但美术有时需要能看到 CurrentLevel 的Foliage,这样才知道自己刷的对不对,所以我们就需要给这个window加一些...

    目录:第九艺术-目录

    开放世界使用 ProceduralSpawner 生成植被的话,有一个主要的问题是,它默认只生成在 PersistentLevel里,这对于一般使用 StreamLevel 的大世界肯定是不行的,远处的植被是可以放到子关卡里随着距离做加载卸载的,而由于之前说的,笔刷工具才能读取 UISettings获取PaintTool上的是否绘制在当前 Level的设置,所以为了让ProceduralSpawner 也可以,又不得不修改引擎了,只是修改Level的话,其实很简单,就是下面的第一步,但由此会带来一些额外的问题,因此需要一步步解决,另外有些也不是问题,只是对原来流程的操作的优化。

    Procedural Spawner能 Spawn到当前CurrentLevel

    FoliageType.h 里添加属性

    	UPROPERTY(EditAnywhere, Category = Custom)
    	uint8 bForcePlaceInCurrentLevel : 1;

    修改逻辑在 FoliageEdMode.cpp,改一行代码就行了

    static void SpawnFoliageInstance(UWorld* InWorld, const UFoliageType* Settings, const FFoliageUISettings* UISettings, const TArray<FFoliageInstance>& PlacedInstances, bool InRebuildFoliageTree)
    {
    	SCOPE_CYCLE_COUNTER(STAT_FoliageSpawnInstance);
    ​
    	TMap<ULevel*, TSet<const FFoliageInstance*>> PerLevelPlacedInstances;
    ​
    	//MODIFICATION START
    	//if (UISettings != nullptr && UISettings->GetIsInSpawnInCurrentLevelMode())
    	if ((UISettings != nullptr && UISettings->GetIsInSpawnInCurrentLevelMode()) || (Settings != nullptr && Settings->bForcePlaceInCurrentLevel))
    	//MODIFICATION  END	
            {
    		TSet<const FFoliageInstance*>& LevelInstances = PerLevelPlacedInstances.FindOrAdd(InWorld->GetCurrentLevel());
    ​
    		for (const FFoliageInstance& PlacedInstance : PlacedInstances)
    		{
    			LevelInstances.Add(&PlacedInstance);
    		}
    	}
    	else
    	{
    		for (const FFoliageInstance& PlacedInstance : PlacedInstances)
    		{
    			TSet<const FFoliageInstance*>& LevelInstances = PerLevelPlacedInstances.FindOrAdd(PlacedInstance.BaseComponent->GetComponentLevel());
    			LevelInstances.Add(&PlacedInstance);
    		}
    	}

    改了之后,第一个问题就是左边 FoliagePalette显示的 Count 不是想要的,它默认显示的 PlacedNumber,就是笔刷刷上去的数量,它显示的数量是Mesh的 PlacedInstanceCount

    这里,我在FEdModeFoliage::PopulateFoliageMeshList 里面改掉了

    PlacedInstanceCount = MeshPair.Value->Instances.Num()

    另外一个问题是虽然显示了PlacedInstanceCount ,但它默认其实只显示 CurrentLevel的,美术在刷植被时,也需要看到 总的当前已加载所有Level的Count,但默认只在Tooltip里两个都显示,这个可以在 FFoliagePaletteItemModel::GetInstanceCountText 里面改掉,同时显示 InstanceCountCurrentLevel 和 InstanceCountTotal。

    return FText::Format(LOCTEXT("","{0}({1})"), FText::AsNumber(InstanceCountCurrentLevel), FText::AsNumber(InstanceCountTotal));

    之后,还有问题是大世界里,Foliage分布在多个关卡,默认Palette会显示已加载 Level的所有FoliageType,但美术有时需要能看到 CurrentLevel 的Foliage,这样才知道自己刷的对不对,所以我们就需要给这个window加一些filter

    开始是喜闻乐见的 加按钮环节

    Slate上加CheckBox

    FoliageEdit/Private/SFoliageEdit.cpp

     					+ SVerticalBox::Slot()
    					.Padding(StandardPadding)
    					.AutoHeight()
    					[
    						SNew(SHorizontalBox)
    						.Visibility(EVisibility::Visible)
    ​
    						+ SHorizontalBox::Slot()
    						.Padding(StandardPadding)
    						.AutoWidth()
    						.VAlign(VAlign_Center)
    						[
    							SNew(SWrapBox)
    							.UseAllottedWidth(true)
    ​
    							+ SWrapBox::Slot()
    							[
    								SNew(SBox)
    								.MinDesiredWidth(91)
    								[
    									SNew(SCheckBox)
    									.OnCheckStateChanged(this, &SFoliageEdit::OnCheckStateChanged_OnlyShowCurrentLevelMeshList)
    									.IsChecked(this, &SFoliageEdit::GetCheckState_OnlyShowCurrentLevelMeshList)
    									[
    										SNew(STextBlock)
    										.Text(LOCTEXT("OnlyShowCurrentLevelMeshList", "OnlyShowCurrentLevelMeshList"))
    										.Font(StandardFont)
    									]
    								]
    							]
    						]
    					]

    用到的响应和回调声明在头文件里

    FoliageEdit/Private/SFoliageEdit.h

    void OnCheckStateChanged_OnlyShowCurrentLevelMeshList(ECheckBoxState InState);
    ​
    ECheckBoxState GetCheckState_OnlyShowCurrentLevelMeshList() const;

    然后是实现

    FoliageEdit/Private/SFoliageEdit.cpp

    void SFoliageEdit::OnCheckStateChanged_OnlyShowCurrentLevelMeshList(ECheckBoxState InState)
    {
    	FoliageEditMode->UISettings.SetOnlyShowCurrentLevelMeshList(InState == ECheckBoxState::Checked ? true : false);
    	FoliageEditMode->PopulateFoliageMeshList();
    }
    ​
    ECheckBoxState SFoliageEdit::GetCheckState_OnlyShowCurrentLevelMeshList() const
    {
    	return FoliageEditMode->UISettings.GetOnlyShowCurrentLevelMeshList() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
    }

    修改按钮状态会调用 FoliageEditMode->PopulateFoliageMeshList 更新 window

    需要在UISettings 里面加一个标记,用于 EdMode查询

    FFoliageUISettings()
     {...
       		//MODIFICATION BEGIN
                    ,bOnlyShowCurrentLevelMeshList(false)
    		//MODIFICATION  END  
     }
     bool GetOnlyShowCurrentLevelMeshList() const { return bOnlyShowCurrentLevelMeshList; }
     void SetOnlyShowCurrentLevelMeshList(bool InbOnlyShowCurrentLevelMeshList) { bOnlyShowCurrentLevelMeshList = InbOnlyShowCurrentLevelMeshList; }

    好了按钮和调用都加好了,之后在 FEdModeFoliage::PopulateFoliageMeshList() 里update的时候,获取 UISteeings,判断一下是否是 CurrentLevel

    	for (int32 LevelIdx = 0; LevelIdx < NumLevels; ++LevelIdx)
    	{
    		ULevel* Level = World->GetLevel(LevelIdx);
    		if (Level && Level->bIsVisible)
    		{
    			//MODIFICATION  BEGIN
    			if(UISettings.bOnlyShowCurrentLevelMeshList && Level != World->GetCurrentLevel())
    				continue;
    			//MODIFICATION  END
    ​
    			AInstancedFoliageActor* IFA = AInstancedFoliageActor::GetInstancedFoliageActorForLevel(Level);
    			if (IFA)
    			{
    				for (auto& MeshPair : IFA->FoliageInfos)
    				{
    					if (!CanPaint(MeshPair.Key, CurrentLevel))
    					{
    						continue;
    					}
    ​
    					int32 ElementIdx = FoliageMeshList.IndexOfByPredicate([&](const FFoliageMeshUIInfoPtr& Item)
    					{
    						return Item->Settings == MeshPair.Key;
    					});
    ​
    					if (ElementIdx == INDEX_NONE)
    					{
    						ElementIdx = FoliageMeshList.Add(MakeShareable(new FFoliageMeshUIInfo(MeshPair.Key)));
    					}
    ​
    					//MODIFICATION  BEGIN
    					int32 PlacedInstanceCount = MeshPair.Value->Instances.Num();//MeshPair.Value->GetPlacedInstanceCount();
    					//MODIFICATION  END
    					FoliageMeshList[ElementIdx]->InstanceCountTotal += PlacedInstanceCount;
    ​
    					if (Level == World->GetCurrentLevel())
    					{
    						FoliageMeshList[ElementIdx]->InstanceCountCurrentLevel += PlacedInstanceCount;
    					}
    				}
    			}
    		}
    	}

    显示的问题解决了,还有select的问题需要解决一下,虽然默认显示CurrentLevel的 CountNum,但select默认是选择所有已加载Level的Instance,所有我们一旦勾选只显示当前Level,select也需要改成只选择 CurrentLevel

    改起来也简单,在 FEdModeFoliage::SelectInstances 里,如果勾选了 bOnlyShowCurrentLevelMeshList 就判断一下是不是当前Level,另外只是选择时才会判断,取消勾选依然需要清除所有的

    void FEdModeFoliage::SelectInstances(UWorld* InWorld, const UFoliageType* Settings, bool bSelect)
    {
    	for (FFoliageInfoIterator It(InWorld, Settings); It; ++It)
    	{
    		FFoliageInfo* FoliageInfo = (*It);
    		AInstancedFoliageActor* IFA = It.GetActor();
    ​
    		//MODIFICATION BEGIN
    		UWorld* World = GEditor->GetEditorWorldContext().World();
    		ULevel* CurrentLevel = World->GetCurrentLevel();
    		if(bSelect && UISettings.bOnlyShowCurrentLevelMeshList && IFA && IFA->GetOuter()!= CurrentLevel)
    			continue;
    		//MODIFICATION END
    ​
    		FoliageInfo->SelectInstances(IFA, bSelect);
    	}
    }

    好了,在修改允许Spawner生成在子关卡之后,需要用到的其他修改就这些了。

    展开全文
  • Drupal foliage

    2019-12-28 23:15:07
    Drupal foliage
  • FoliageTypeObject.RefreshInstance 更新所有FoliageType引用,虚幻引擎内部对于数据更新的处理有些还是很有意思,值得细细研究的,比如Foliage系统的另一个地方 FoliageType在编辑器下修改之后,是会更新一个自己的...

    目录:第九艺术-目录

    Version:UnrealEngine 4.22.3

    做开放大世界的话,由于面积非常大,用ProceduralFoliageSpawner 来生成植被是比较高效的选择,在实际使用的过程中,美术同事有个需求是希望 Spawner中配置的各个 FoliageType有一些关联,比如有的小灌木就只会生长在某种大树旁边,但原生的 ProceduralFoliageSpawner 内各个FoliageType是完全不相关的,因此生成之后,美术要调整的工作量很大,因此我需要允许美术配置各个Folaige之间的亲疏关系

    正好借着做这个需求的机会,先来熟悉一下原本的逻辑

    配置如图

    以上几个主要属性都会在下面Spawner的整体计算流程中介绍到

    1. 当 点击Spawner的 模拟按钮Resimulate 之后,调用到 FProceduralFoliageComponentDetails::OnResimulateClicked,会直接调用到 UProceduralFoliageComponent::ExecuteSimulation 组件开始i模拟
    2. UProceduralFoliageComponent::ExecuteSimulation
      1. 根据配置 的TileSize,以及spawner的volume大小,计算填满volume需要xy多少个tile作为TileLayout
      2. 之后就调用到 UProceduralFoliageSpawner::Simulate,这里Simulate是为了计算出 NumUniqueTiles个的Tile样本
      3. 使用计算出来的排列组合出来最终需求的 Layout的tile拼接,这里我起个名字叫 CompositeTile,方便下面介绍
    3. UProceduralFoliageSpawner::Simulate
      1. FoliageTypeObject.RefreshInstance 更新所有FoliageType引用,虚幻引擎内部对于数据更新的处理有些还是很有意思,值得细细研究的,比如Foliage系统的另一个地方 FoliageType在编辑器下修改之后,是会更新一个自己的GUID,其他Load等操作用到的时候,会检查这个GUID是否为最新的,不是才会更新一下,类似这样,去看看的话,对理解它的各种机制很有帮助
      2. 用Future调用到 Tile的Simulate,注意这里只会出来NumUniqueTiles个Tile
        1. InitSimulation初始化随机种子
        2. UProceduralFoliageTile::RunSimulation 走两次,一次先生成不考虑ShaderRadius的 FoliageType,第二遍生成允许GrowInShade的FoliageType,内部调用配置的MaxSteps 调用 StepSimulation 分步模拟
        3. StepSimulation 内部根据配置的 Ages,Seeds,SimulateStep多次步进生成多种形态的Foliage
    4. CompositeTile,上一步得到需要的Tile之后,就开始排列组合了
      1. 循环tileLayout需要的tile个数,每个从NumUniqueTiles中随机出来一个作为baseTile,然后如果配置了 TileOverlap,还会随机出来 右边的,上边的和右上的,然后把 Overlap到的相交区域的Instances拷贝到base上
    5. 最终从生成的Tiles里拿到所有的满足各种条件的Instance
    6. 拿到之后,就走到 基础的和笔刷工具类似的主流程了,逐个生成Instance,调用 FEdModeFoliage::AddInstances,循环遍历所有 Instances,调用每个Instance的 FEdModeFoliage::AddInstancesImp
    7. Spawner 生成的Instance的 PlacementMode是 EFoliagePlacementMode::Procedural,这里有个官方的 Todo,之后为了修改成 多线程了,调用了 CalculatePotentialInstances_ThreadSafe,但目前还不是并行的
    8. FEdModeFoliage::CalculatePotentialInstances_ThreadSafe,目前来说和笔刷生成的是类似的
      1. FoliageTrace 找到地块
      2. VertexMaskCheck
      3. LandscapeLayerCheck
      4. 满足条件的Instance加到要Spawn的数组
    9. 逐个生成 Instance

    如上流程可以看到,做为对比,在笔刷工具做这个功能的时候,刷的每种FoliageType都已经可以知道当前地块上有哪些已经放好的 Instance,所以可以直接做距离检查,但是 用Spawner来做,中间不光是每种 FoliageType还是每个Tile,计算过程都是并行的,我生成一个Instance的时候,并不知道其他正在生成的Folaige信息,它们都还没有种到地上,从而没有办法做亲疏关系检查

    所以我们需要找到一个地方得等所有并行和随机的过程都走完之后,所有结果都确定了,再做一次全面的筛查

    分析之后,可以发现最终 CompositeTile的时候是个合适的机会,所有Instance,以及Tile合并都已经执行完,结果都确定了,此时可以遍历一遍所有Instance,检查一下亲疏关系

    实现代码如下:

    FoliageType里

    添加配置结构体,模型和距离

    USTRUCT()
    struct FRelatedFoliageTypeEntry
    {
    	GENERATED_USTRUCT_BODY()
    ​
    	UPROPERTY(EditAnywhere,Category = RelatedFoliage)
    		UStaticMesh* FoliageMesh;
    ​
    	UPROPERTY(EditAnywhere,Category = RelatedFoliage)
    		float Radius;
    };

    添加属性配置

    	UPROPERTY(Category = Procedural, EditAnywhere, meta = (Subcategory = "Clustering"))
    	uint8 bMeetAllCloseToRequirment : 1;
    ​
    	UPROPERTY(Category = Procedural, EditAnywhere, meta = (Subcategory = "Clustering"))
    	TArray<FRelatedFoliageTypeEntry> ProceduralCloseToFoliages;
    ​
    	UPROPERTY(Category = Procedural, EditAnywhere, meta = (Subcategory = "Clustering"))
    	TArray<FRelatedFoliageTypeEntry> ProceduralFarFromFoliages;

    然后在UProceduralFoliageComponent::ExecuteSimulation 方法里CompositeTexture的最后,Extract之前添加和调用方法 UProceduralFoliageTile::CheckFoliageRelatedRestrict,如下是方法实现

    void UProceduralFoliageTile::CheckFoliageRelatedRestrict()
    {
    	for (FProceduralFoliageInstance* Instance : InstancesSet)
    	{
    		if (!Instance->Type)
    			continue;
    ​
    		bool bSurvive = true;
    		bool bFoundOne = false;
    		for (int i = 0; i < Instance->Type->ProceduralCloseToFoliages.Num(); ++i)
    		{
    			if(!Instance->Type->ProceduralCloseToFoliages[i].FoliageMesh || Instance->Type->ProceduralCloseToFoliages[i].Radius <= 0)
    				continue;
    ​
    			float Radius = Instance->Type->ProceduralCloseToFoliages[i].Radius;
    			FVector2D Location(Instance->Location);
    			FVector2D Offset(Radius, Radius);
    			FBox2D AABB(Location - Offset, Location + Offset);
    			TArray<FProceduralFoliageInstance*> AreaOverlapInstances;
    			Broadphase.GetInstancesInBox(AABB, AreaOverlapInstances);
    ​
    			bool bFoundInCloseFoliageArea = false;
    			for (FProceduralFoliageInstance* Overlap : AreaOverlapInstances)
    			{
    				if (Overlap->Type && Overlap->Type->Mesh == Instance->Type->ProceduralCloseToFoliages[i].FoliageMesh)
    				{
    					bFoundInCloseFoliageArea = true;
    					break;
    				}
    			}
    			if (Instance->Type->bMeetAllCloseToRequirment)
    			{
    				if (!bFoundInCloseFoliageArea)
    				{
    					bSurvive = false;
    					break;
    				}
    			}
    			else
    			{
    				if (bFoundInCloseFoliageArea)
    				{
    					bFoundOne = true;
    					bSurvive = true;
    					break;
    				}
    			}
    		}
    ​
    		if (bSurvive && !Instance->Type->bMeetAllCloseToRequirment && Instance->Type->ProceduralCloseToFoliages.Num() && !bFoundOne)
    			bSurvive = false;
    ​
    		if (bSurvive)
    		{
    			for (int i = 0; i < Instance->Type->ProceduralFarFromFoliages.Num(); ++i)
    			{
    				if (!Instance->Type->ProceduralFarFromFoliages[i].FoliageMesh || Instance->Type->ProceduralFarFromFoliages[i].Radius <= 0)
    					continue;
    ​
    				float Radius = Instance->Type->ProceduralFarFromFoliages[i].Radius;
    				FVector2D Location(Instance->Location);
    				FVector2D Offset(Radius, Radius);
    				FBox2D AABB(Location - Offset, Location + Offset);
    				TArray<FProceduralFoliageInstance*> AreaOverlapInstances;
    				Broadphase.GetInstancesInBox(AABB, AreaOverlapInstances);
    ​
    				bool bFoundInCloseFoliageArea = false;
    				for (FProceduralFoliageInstance* Overlap : AreaOverlapInstances)
    				{
    					if (Overlap->Type && Overlap->Type->Mesh == Instance->Type->ProceduralFarFromFoliages[i].FoliageMesh)
    					{
    						bFoundInCloseFoliageArea = true;
    						break;
    					}
    				}
    				if (bFoundInCloseFoliageArea)
    				{
    					bSurvive = false;
    					break;
    				}
    			}
    		}
    ​
    		if(!bSurvive)
    		{
    			MarkPendingRemoval(Instance);
    		}
    	}
    ​
    	FlushPendingRemovals();
    }

    逻辑就是检查了各个Instance配置的相关性,用到了 Tile的 FProceduralFoliageBroadphase 它是所有Instance的四叉树结构的管理工具,Tile的增删改查instance都会通过它来完成,包括Spawner内配置的 CollisionRadius/ShadeRadius的距离剔除等等

     

    展开全文
  • 目录:第九艺术-目录 Version:UnrealEngine 4.22.3 ...因此就有了也在笔刷中实现上一篇文章功能的需求,也要在笔刷中允许配置各种Foliage的远近亲疏关系,借同样的需求,熟悉一下默认笔刷的流程。 ...

    目录:第九艺术-目录

    Version:UnrealEngine 4.22.3

    做开放大世界的话,初始可以用ProceduralFoliageSpawner 生成,由于都是随机生成的,之后一般都还会需要很多的细节调整,那就得用到默认的笔刷工具来生成植被了。因此就有了也在笔刷中实现上一篇文章功能的需求,也要在笔刷中允许配置各种Foliage的远近亲疏关系,借同样的需求,熟悉一下默认笔刷的流程。

    在这里选择Foliage之后,点击要刷的表面。

    1. 调用到 FEdModeFoliage::StartFoliageBrushTrace,开始spawn 满足 UISettings和FoliageType里配置的Instances,这是和 Spawner 一个比较大的区别,用笔刷是可以获取到ActiveTool等Editor信息的,但spawner由于是在之外生成的,就得不到这些信息,之后做一些拓展的时候,会需要考虑到这些区别
    2. 之后调用到 FEdModeFoliage::ApplyBrush,这里会计算你操作工具的压力大小,作为最终生成Instance数量的系数,当然鼠标就是 1 了,然后判断如果是 Paint 工具,就开始生成
    3. 调用到 FEdModeFoliage::AddInstancesForBrush,根据Brush大小,构建这次生成的Buckets,主要用于LandscapeLayer的权重检查,会先查看一下已有的bucket里有多少
    4. 拿到之后,就走到 笔刷和Spawner工具类似的主流程了,调用FEdModeFoliage::AddInstancesImp,逐个生成Instance
    5. 相比Spawner会调用线程安全的 CalculatePotentialInstances_ThreadSafe,笔刷不会多线程,所以直接调用CalculatePotentialInstances
    6. FEdModeFoliage::CalculatePotentialInstances,和spawner类似
      1. FoliageTrace 找到地块
      2. VertexMaskCheck
      3. LandscapeLayerCheck
      4. 满足条件的Instance加到要Spawn的数组
    7. 逐个生成 Instance

    如上流程可以看到,之前也说过,在笔刷工具做这个功能的时候,最大的优势就是刷的每种FoliageType都已经可以知道当前地块上有哪些已经放好的 Instance,所以可以直接做距离检查,那就很简单了,只要再最后做各种检查的地方加一个远近亲疏关系的检查就可以了

    实现代码如下:

    FoliageType里

    添加配置结构体,模型和距离,这里可以新加一个,也可以用spawner的

    USTRUCT()
    struct FRelatedFoliageTypeEntry
    {
    	GENERATED_USTRUCT_BODY()
    ​
    	UPROPERTY(EditAnywhere,Category = RelatedFoliage)
    		UStaticMesh* FoliageMesh;
    ​
    	UPROPERTY(EditAnywhere,Category = RelatedFoliage)
    		float Radius;
    };

    添加属性配置

    	UPROPERTY(EditAnywhere, Category = Custom)
    	TArray<FRelatedFoliageTypeEntry> CloseToFoliages;
    ​
    	UPROPERTY(EditAnywhere, Category = Custom)
    	TArray<FRelatedFoliageTypeEntry> FarFromFoliages;

    添加 距离检查的方法 RelatedFoliageTypesCheck,逻辑很简单

    static bool CheckForOverlappingSphere(AInstancedFoliageActor* IFA, const UStaticMesh* Mesh, const FSphere& Sphere)
    {
    	if (IFA)
    	{
    		TArray<const UFoliageType*> MeshFoliageTypes;
    		IFA->GetAllFoliageTypesForSource(Mesh, MeshFoliageTypes);
    		for (int i = 0; i < MeshFoliageTypes.Num(); ++i)
    		{
    			if (CheckForOverlappingSphere(IFA, MeshFoliageTypes[i], Sphere))
    				return true;
    		}
    	}
    ​
    	return false;
    }
    ​
    bool RelatedFoliageTypesCheck(const FHitResult& Hit, const UFoliageType* Settings)
    {
    	if (!Settings)
    		return false;
    ​
    	if (Settings->CloseToFoliages.Num() == 0 && Settings->FarFromFoliages.Num() == 0)
    		return true;
    ​
    	AInstancedFoliageActor* IFA = AInstancedFoliageActor::GetInstancedFoliageActorForLevel(Hit.Component->GetComponentLevel(),false);
    	if (!IFA)
    	{
    		if (Settings->CloseToFoliages.Num())
    			return false;
    ​
    		return true;
    	}
    ​
    	for (int i = 0; i < Settings->CloseToFoliages.Num(); ++i)
    	{
    		if (Settings->CloseToFoliages[i].FoliageMesh && Settings->GetSource() != Settings->CloseToFoliages[i].FoliageMesh && Settings->CloseToFoliages[i].Radius > 0)
    		{	
    			if (!CheckForOverlappingSphere(IFA, Settings->CloseToFoliages[i].FoliageMesh, FSphere(Hit.Location, Settings->CloseToFoliages[i].Radius)))
    				return false;
    		}
    	}
    	for (int i = 0; i < Settings->FarFromFoliages.Num(); ++i)
    	{
    		if (Settings->FarFromFoliages[i].FoliageMesh && Settings->GetSource() != Settings->FarFromFoliages[i].FoliageMesh && Settings->FarFromFoliages[i].Radius > 0)
    		{
    			if (CheckForOverlappingSphere(IFA, Settings->FarFromFoliages[i].FoliageMesh, FSphere(Hit.Location, Settings->FarFromFoliages[i].Radius)))
    				return false;
    		}
    	}
    	return true;
    }

    最后就是调用了,在最后 CalculatePotentialInstances 和 CalculatePotentialInstances_ThreadSafe 里面都加上这个判断

    类似

    const bool bValidInstance = CheckLocationForPotentialInstance_ThreadSafe(Settings, Hit.ImpactPoint, Hit.ImpactNormal)
    	&& VertexMaskCheck(Hit, Settings)
    	&& LandscapeLayerCheck(Hit, Settings, LocalCache, HitWeight)
    	&& RelatedFoliageTypesCheck(Hit, Settings)//多加了这一句
    展开全文
  • Foliage篇-UE4笔刷功能拓展

    千次阅读 2020-01-26 15:30:55
    UE_LOG(LogFoliage, Error, TEXT("Cann't read Foliage Settings Mask Texture %s."),*(Settings->PlaceMaskTexture->GetName())); } } //MODIFICATION END TArray<FPotentialInstance> ...

    目录:第九艺术-目录

    基础逻辑都和前面类似,这里就不再细说了,分享一些可能会有帮助的工具,需要的话尽管拿去~

    反向LandscapeLayer检查:

    InvertLandscapeLayerCheck

    FoliageType.h 里添加属性

     UPROPERTY(EditAnywhere, AdvancedDisplay, Category = Placement, meta = (ReapplyCondition = "ReapplyLandscapeLayers"))
    uint32 bExcluedeLandscapeLayers : 1;

    然后修改原本的LandscapeLayer逻辑,这里的这种写法主要是为了尽量少改原本的逻辑,之后好升版本,另外就是注意一下 OutHitWeight 也需要反向一下,这样之后才能正确的分到对应的 Bucket里

    bool LandscapeLayerCheck(const FHitResult& Hit, const UFoliageType* Settings, FEdModeFoliage::LandscapeLayerCacheData& LandscapeLayersCache, float& OutHitWeight)
    {
    //MODIFICATION START
    	if (Settings->bExcluedeLandscapeLayers)
    	{
    		OutHitWeight = 1.f;
    		if (LandscapeLayersValid(Settings) && GetMaxHitWeight(Hit.ImpactPoint, Hit.Component.Get(), Settings, &LandscapeLayersCache, OutHitWeight))
    		{
    			const float WeightNeeded = FMath::Min(Settings->MinimumLayerWeight, FMath::FRand());
    			if (OutHitWeight > FMath::Max(SMALL_NUMBER, WeightNeeded))
    			{
    				return false;
    			}
    			OutHitWeight = 1 - OutHitWeight;//反向bucket
    		}
    ​
    		return true;
    	}
    //MODIFICATION END
    ​
    	OutHitWeight = 1.f;
    	if (LandscapeLayersValid(Settings) && GetMaxHitWeight(Hit.ImpactPoint, Hit.Component.Get(), Settings, &LandscapeLayersCache, OutHitWeight))
    	{
    		// Reject instance randomly in proportion to weight
    		if (FilterByWeight(OutHitWeight, Settings))
    			return false;
    	}
    ​
    	return true;
    }

    基于Mask生成植被:

    这个需求主要是为了相比原来的landscape layer更自由一点控制,随意由美术自己调节种植区域

    FoliageType.h 里添加属性

     UPROPERTY(EditAnywhere, Category = MaskSpawn)
     uint8 bEnableMaskTexture : 1;
    ​
     UPROPERTY(EditAnywhere, Category = MaskSpawn)
     uint8 SampleIndex = 0;
    ​
     UPROPERTY(EditAnywhere, Category = MaskSpawn)
     float SampleThreshold = 0.f;
    ​
     UPROPERTY(EditAnywhere, Category = MaskSpawn)
     class UTexture2D* PlaceMaskTexture;

    基本思路是 在 FEdModeFoliage::AddInstancesImp 读取texture,传入Calculate方法,这里需要修改 CalculatePotentialInstances 和 CalculatePotentialInstances_ThreadSafe 的接口

    读取:

    //MODIFICATION START
     	const FColor * FormatedMaskData = nullptr;
    	if (Settings->bEnableMaskTexture && Settings->PlaceMaskTexture)
    	{
    		FormatedMaskData = static_cast<const FColor*>(Settings->PlaceMaskTexture->PlatformData->Mips[0].BulkData.LockReadOnly());
    		if (!FormatedMaskData)
    		{
    			UE_LOG(LogFoliage, Error, TEXT("Cann't  read Foliage Settings Mask Texture %s."),*(Settings->PlaceMaskTexture->GetName()));
    		}
    	}
    //MODIFICATION END
    	TArray<FPotentialInstance> PotentialInstanceBuckets[NUM_INSTANCE_BUCKETS];
    	if (DesiredInstances[0].PlacementMode == EFoliagePlacementMode::Manual)
    	{
    		CalculatePotentialInstances(InWorld, Settings, DesiredInstances, PotentialInstanceBuckets, LandscapeLayerCachesPtr, UISettings, OverrideGeometryFilter, FormatedMaskData);//#ifdef GAMES_ENGINE_MODIFY
    	}
    	else
    	{
    		//@TODO: actual threaded part coming, need parts of this refactor sooner for content team
    		CalculatePotentialInstances_ThreadSafe(InWorld, Settings, &DesiredInstances, PotentialInstanceBuckets, nullptr, 0, DesiredInstances.Num() - 1, OverrideGeometryFilter, FormatedMaskData);//#ifdef GAMES_ENGINE_MODIFY
                  ......
    	}
    ​
    //MODIFICATION START
    	if (Settings->bEnableMaskTexture && Settings->PlaceMaskTexture && FormatedMaskData)
    	{
    		Settings->PlaceMaskTexture->PlatformData->Mips[0].BulkData.Unlock();
    	}
     //MODIFICATION END

    Calculate里上文加了 距离相关性的check,这里再加 Mask的检查

    			const bool bValidInstance = CheckLocationForPotentialInstance_ThreadSafe(Settings, Hit.ImpactPoint, Hit.ImpactNormal)
    				&& VertexMaskCheck(Hit, Settings)
    				&& LandscapeLayerCheck(Hit, Settings, LocalCache, HitWeight)
    				//MODIFICATION START
    				&& RelatedFoliageTypesCheck(Hit, Settings)
    				&& PlaceMaskFoliageTypeCheck(Hit, Settings, FormatMaskColor);
    			        //MODIFICATION END

    具体Check逻辑,这里我只做了threshold的01判断,其实也可以做些过渡,根据mask值,设置 Weight权重

    bool PlaceMaskFoliageTypeCheck(const FHitResult& Hit, const UFoliageType* Settings ,const FColor* FormatMaskColor)
    {
    	if (!Settings)
    		return false;
    ​
    	if (Settings->PlaceMaskTexture && FormatMaskColor)
    	{
    		if (ALandscape * HitLandscape = Cast<ALandscape>(Hit.Actor))
    		{
    			FIntRect LandscapeRect = HitLandscape->GetBoundingRect();
    			FVector MidPoint = FVector(LandscapeRect.Min, 0.f) + FVector(LandscapeRect.Size(), 0.f)*0.5f;
    ​
    			FVector LandscapeCenter = HitLandscape->GetTransform().TransformPosition(MidPoint); //直接 FVector2D LocOffset = FVector2D(Hit.Location.X - Center.X, Hit.Location.Y - Center.Y); 因为没有考虑本身landscape的rotation和scale
    			//FVector LandscapeExtent = FVector(LandscapeRect.Size(), 0.f)*HitLandscape->GetActorScale()*0.5f;
    ​
    			int32 TextureX = Settings->PlaceMaskTexture->GetSizeX();
    			int32 TextureY = Settings->PlaceMaskTexture->GetSizeY();
    			FVector SampleLoc = (Hit.Location - LandscapeCenter) / (FVector(LandscapeRect.Size(), 0.f)*HitLandscape->GetActorScale());
    			int32 SampleX = FMath::Clamp(FMath::TruncToInt((SampleLoc.X + 0.5) * TextureX), 0, TextureX - 1);
    			int32 SampleY = FMath::Clamp(FMath::TruncToInt((SampleLoc.Y + 0.5) * TextureY), 0, TextureY - 1);
    ​
    			FColor PixelColor = FormatMaskColor[SampleY*TextureX + SampleX];
    			
    			switch (Settings->SampleIndex)
    			{
    			case  0:
    				return PixelColor.R > Settings->SampleThreshold;
    			case 1:
    				return PixelColor.G > Settings->SampleThreshold;
    			case 2:
    				return PixelColor.B > Settings->SampleThreshold;
    			case 3:
    				return PixelColor.A > Settings->SampleThreshold;
    			default:
    				return PixelColor.R > Settings->SampleThreshold;
    			}
    		}
    	}
    ​
    	return true;
    }

     

     

    展开全文
  • Advanced Foliage Shader

    2013-11-13 12:49:29
    Advanced Foliage Shader
  • UE4添加植被Foliage Type

    千次阅读 2019-09-30 10:27:43
    UE4提供了一种批量添加地面植被类型的方式Foliage Type。在编辑器内容窗口添加一个Foliage Type,命名为Grass。 双击打开Grass,在Mesh中添加需要批量制造的物体并保存。 打开UE4的植被编辑器,添加刚创建的...
  • ue4植被foliage漂浮问题

    千次阅读 2018-03-24 22:14:36
    foliage.OffGroundThreshold 5 也没有任何效果(但是这个给了我提示,虽然设置任意大的值没有效果,但是这个肯定在ue内容有值让超出的植物不在起作用,然后结合下面的功能才最终发现了更稳妥的解决方法) 我使用的解决...
  • 更好的叶子 纤细的Better Foliage端口,适用于1.16及更高版本。 可以通过资源包禁用此mod的大多数元素。 由EERussianguy创建。 由Paint_Ninja委托。 该模块已获得MIT许可。
  • 语言:English 此扩展程序显示了新英格兰地区的实时叶子地图
  • foliage recognition

    2014-09-30 15:32:09
    foliage recognition 叶片识别
  • D3D11_Foliage.rar

    2017-02-21 22:42:26
    D3D11_Foliage.rar
  • 树叶森林蛇 效果器+森林+叶子上的蛇游戏 效应器:状态和逻辑 森林:在dom中渲染 叶子:造型 播放: :
  • 茉莉酸甲酯诱导普通棉抗虫性对棉铃虫解毒力和其它生理特征的影响,杨世勇,吴慧慧,用植物激素茉莉酸甲酯 (MeJA) 处理后一周的皖棉28F1 叶片饲喂3龄棉铃虫幼虫,探讨诱导抗虫性对棉铃虫发育时间、蛹重、相对生长率...
  • Let's Render Some Foliage

    2013-12-31 13:12:11
    可以说是精简版的SpeedTree,远处的树是根据Impostor画出来的,可以画大规模的森林...This blog series discusses some ideas and issues around rendering foliage. Why? I’m on Intel’s Game Technology Developme
  • <div><p>I've got the most up-to-date version of the overviewer (fresh git ...<p>It mostly generates grey grass and foliage</p><p>该提问来源于开源项目:overviewer/Minecraft-Overviewer</p></div>
  • 按照官方教程 https://docs.unrealengine.com/latest/CHN/Engine/OpenWorldTools/ProceduralFoliage/QuickStart/1/index.html ...但是在Add New里怎么也找不到Procedural Foliage Spawner 解决方案: https://an...
  • Foliage Powerpoint Theme

    2010-08-18 16:33:07
    Foliage Powerpoint Theme.zip
  • 这个gem描述如何来渲染植物和树。这其中描述的方法所画的植物仍旧是非常粗糙的,但是其中描述的一些优化方法以及思想倒是 值得借鉴。目前比较大型游戏里面对于叶子的渲染都是用alpha test来做,就是一大片叶子其实...
  • 背景 在上篇中,实现了使用Houdini在UE4里根据地形过程生成植被的最基本的原型。并且支持把植被在UE4里Bake成使用的HierarchicalInstancedStaticMeshComponent的...开发效率方面,这个方案并不支持UE4的Foliage Mod...
  • 构建光照时 Foliage警告光照贴图太大,如图: 解决办法,看下图:
  • UE4 Material 101学习笔记——30-37 植物叶片(透光/mask/面片隐藏/法线调整/AO/渐隐/世界空间色彩/随风舞动) Lec30 叶子透光 Foliage Translucency Lec31 叶子贴图打包 Foliage Texture Packing 1 合并粗糙度/透明...
  • 着色模型简介和实现(下)

    千次阅读 2020-10-08 15:38:49
    Two Sided Foliage Two Sided Foliage用于渲染较薄的次表面散射材质,例如树叶、花瓣等。它可以模拟光线穿过材质的效果,比Subsurface model更真实。 Base Color Metallic Specular Roughness Emissive Color ...
  • Foliage for Landscapes1 寻找参考/资产下载2 遮罩&Grass节点尝试&四个问题3 问题解决3.1 密度管理3.2 过渡(TAA模拟透明度变化)3.3 草的多样性 本系列学习资料来源,Ben Cloward的油管空间,B站的搬运...
  • 【Android】蓝牙开发——经典蓝牙(附Demo源码)

    千次阅读 多人点赞 2019-08-27 09:33:53
    目录 一、前言 二、经典蓝牙的介绍 三、经典蓝牙项目实战 四、Demo案例源码地址: ...去年毕业进入公司以来,工作内容主要和蓝牙打交道,几个月的学习和实践让我这个Android蓝牙小白逐渐成长起来。...
  • By DAVE GRAM, Associated Press Writer Sat Oct 20, 4:39 PM ET EAST MONTPELIER, Vt. - Every fall, Marilyn Krom tries to make a trip to Vermont to see its famously beautiful fall foliage.

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 832
精华内容 332
关键字:

foliage