做个小工具显示UE里地形的高度图和权重图

Posted YakSue

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了做个小工具显示UE里地形的高度图和权重图相关的知识,希望对你有一定的参考价值。

目标

做一个小工具来显示UE里 Landscape(启用编辑层)的高度图和权重图。

要显示高度图和权重图,本身是不难的。不过这里想要不修改引擎源码。所以是在外部(比如项目工程模块,或插件模块)做了事情。

地形高度图和权重图的数据结构

可在 LandscapeComponent.h 中观察到数据结构。

在启用编辑层的情况下,数据在LayersData中,每个编辑层对应一个GUID。

UPROPERTY()
TMap<FGuid, FLandscapeLayerComponentData> LayersData;

每个 FLandscapeLayerComponentData包含一个FHeightmapData和一个FWeightmapData

USTRUCT(NotBlueprintable)
struct FLandscapeLayerComponentData

	GENERATED_USTRUCT_BODY()

	UPROPERTY()
	FHeightmapData HeightmapData;

	UPROPERTY()
	FWeightmapData WeightmapData;

	bool IsInitialized() const  return HeightmapData.Texture != nullptr || WeightmapData.Textures.Num() > 0;  
;

FHeightmapData包含一个贴图:

USTRUCT(NotBlueprintable)
struct FHeightmapData

	GENERATED_USTRUCT_BODY()

	UPROPERTY()
	UTexture2D* Texture;
;

FWeightmapData包含多个贴图(因为可能有很多权重层):

USTRUCT(NotBlueprintable)
struct FWeightmapData

	GENERATED_USTRUCT_BODY()

	UPROPERTY()
	TArray<UTexture2D*> Textures;
	
	UPROPERTY()
	TArray<FWeightmapLayerAllocationInfo> LayerAllocations;

	UPROPERTY(Transient)
	TArray<ULandscapeWeightmapUsage*> TextureUsages;
;

小工具

流程和之前一样,先定义一个UObject。

为了能在Detail面板看到相关的贴图,这里将原来的结构体换成了新的后缀为 _vis 的结构体,并将UProperty加上VisibleAnywhere。然后,提供一个按钮,也就是FindLandscapeTextures()函数,触发后找到世界中的地形,并将其数据复制过来。(目前,只观察第一个找到的地形的第一个Component)

代码如下:

LandscapeTextureWatcher.h

#pragma once

#include "UObject/Object.h"
#include "Landscape/Classes/LandscapeComponent.h"

#include "LandscapeTextureWatcher.generated.h"

USTRUCT(NotBlueprintable)
struct FWeightmapData_vis

	GENERATED_USTRUCT_BODY()

	UPROPERTY(VisibleAnywhere)
	TArray<UTexture2D*> Textures;
	
	UPROPERTY(VisibleAnywhere)
	TArray<FWeightmapLayerAllocationInfo> LayerAllocations;

	UPROPERTY(Transient)
	TArray<ULandscapeWeightmapUsage*> TextureUsages;
;

USTRUCT(NotBlueprintable)
struct FHeightmapData_vis

	GENERATED_USTRUCT_BODY()

	UPROPERTY(VisibleAnywhere)
	UTexture2D* Texture;
;

USTRUCT(NotBlueprintable)
struct FLandscapeLayerComponentData_vis

	GENERATED_USTRUCT_BODY()

	UPROPERTY(VisibleAnywhere)
	FHeightmapData_vis HeightmapData;

	UPROPERTY(VisibleAnywhere)
	FWeightmapData_vis WeightmapData;

	bool IsInitialized() const  return HeightmapData.Texture != nullptr || WeightmapData.Textures.Num() > 0;  
;

UCLASS(Blueprintable)
class ULandscapeTextureWatcher : public UObject

	GENERATED_UCLASS_BODY()
public:
	
	//按钮,点击后就把地形的纹理数据拿来
	UFUNCTION(CallInEditor)
	void FindLandscapeTextures();

	//要观察的地形纹理数据:
	UPROPERTY(VisibleAnywhere)
	TMap<FGuid, FLandscapeLayerComponentData_vis> LayersData;
;

LandscapeTextureWatcher.cpp

#include "LandscapeTextureWatcher.h"
#include "Kismet/GameplayStatics.h"
#include "Editor.h"
#include "LandscapeInfo.h"
#include "Landscape/Classes/Landscape.h"

ULandscapeTextureWatcher::ULandscapeTextureWatcher(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)


void ULandscapeTextureWatcher::FindLandscapeTextures()

	//得到World
	const UWorld* World = GEditor->GetEditorWorldContext().World();

	//找地形:
	TArray<AActor*> OutActors;
	UGameplayStatics::GetAllActorsOfClass(World, ALandscape::StaticClass(), OutActors);
	//没找到则直接返回
	if (OutActors.Num() == 0)
		return;
	//找到则用第一个Landscape:
	ALandscape* Landscape = (ALandscape*)OutActors[0];

	if (Landscape->LandscapeComponents.Num() == 0)
		return;
	//使用第一个Component:
	ULandscapeComponent* LandscapeComponent = Landscape->LandscapeComponents[0];

	//遍历所有layer,填入数据
	LandscapeComponent->ForEachLayer([&](const FGuid& LayerGuid, FLandscapeLayerComponentData& LayerData)
	
		FLandscapeLayerComponentData_vis data;
		data.HeightmapData.Texture = LayerData.HeightmapData.Texture;
		data.WeightmapData.Textures = LayerData.WeightmapData.Textures;
		LayersData.Add(LayerGuid, data);
	);

然后,仿照之前第(3)步,显示其Detail面板。

效果

开启编辑层功能创建地形,注意只创建一个Component(因为暂时只观察第一个Component)
创建两个编辑层,并且使用的地形材质有五个权重层。
结果如下:

虽然上面看到贴图是黑的,但是因为Alpha通道是0,可以直接打开贴图关闭alpha通道看效果。

高度图:

权重图,可以看出每层各占一个通道:

UE4地形快速入门指南、地形提示和技巧0060bate1

参考技术A

利用虚幻引擎中的地形系统基础知识设置和运行。
《虚幻编辑器地形快速入门指南》将对新建地形、塑造地形,新建地形材质,及在地形上绘制此类材质进行讲解。

虚幻引擎4(UE4)内置的 地形(Landscape) 系统是一个工具合集,可帮助你创建 庞大的户外环境。但在深入讲解创建首个地形前,先来熟悉与地形系统互动时的最常用工具和键盘输入。

用于与地形系统交互的所有工具均可在 模式 下拉菜单 中的 地形 选项下找到。要启用地形工具,点击模式下拉菜单并从菜单中选择对应选项即可。

开始绘制地形时,可能会遇到如基本材质消失或变黑等问题,如下图所示:

发生此类情况是由于开始绘制时,地形上没有绘制图层数据。要解决此问题,需继续绘制地形,操作以生成绘制图层数据。如要填充整个地形,首先选择大型笔刷尺寸,例如8192.0,选取要用作基础的图层,然后在整个地形上绘制一次。此操作可创建绘制图层数据,以便继续进行绘制,而材质不会变黑。

另一个潜在问题是地形上使用的纹理缩放过大或过小。要解决此问题,需要打开地形材质,然后选择 Landscape Coords 节点。选中后,调整 细节 面板中的 映射缩放(Mapping Scale) 并保存材质。编译后,在视口中检查缩放。如对缩放效果不满意,重复上述过程直到获得理想效果。

6 - 地形提示和技巧

以上快速入门教程已对设置和运行地形的基础知识进行了讲解,但对地形工具的介绍却十分浅显。此章节旨在展示部分地形工具的使用提示和技巧,及生成地形所需外部工具。

在虚幻引擎4(UE4)中,可使用 世界场景构成(World Composition) 工具对地形进行简单管理,并创建巨大的世界场景。世界场景构成的设计理念是为简化大型世界场景(尤其是使用地形系统创建的世界场景)的管理。欲了解世界场景构成工具的更多信息,请留意后期更新!

虽然默认地形工具可以满足造型和绘制的所有需求,但有时需要对地形外观和风格进行更深入的调整。可以使用以下软件包实现地形工具无法达到的效果。

为了使地形系统发挥最佳性能,必须遵守一定的技术限制。以下文档旨在提醒您注意这些限制,并为您提供有价值的信息,以便您能够在地形的美观和性能之间达到最佳平衡。

[图片上传中...(Snipaste_2021-05-18_00-06-29.jpg-a75d1d-1621267596125-0)]
刚开始时,地形高度图的有效维度并不总是立即会显示出来。要确定高度图的哪些维度是有效的,以及除此之外哪些维度是最佳的,还需要对地形的底层架构有一个全面的了解。为了创建一个允许使用巨大地形但又能继续高效利用内存和性能的系统,架构隐式地对高度图的维度进行了限制,这意味着某些维度是有效的,而其他维度则无效。在虚幻引擎中先前的地形系统中,没有任何限制(即任何维度都是有效可行的)或限制相当简单(即只允许两个高度图的平方幂)。地形高度图的限制更加复杂和严格。

地形Actor是采用颜色编码,这样更容易说明每个部分发挥什么作用。地形的边缘用黄色突出显示,每个组件的边缘用浅绿色显示,分段边缘(如果设置为2x2分段)用中绿色显示,单独的地形四边形用深绿色显示。

地形分为多个组件,它们是虚幻的基本渲染单元、可视性计算单元和碰撞单元。地形中的组件都具有相同的大小,并且总是呈现为正方形。地形组件的大小是在创建地形时决定的,而选择取决于您希望创建的地形的大小和细节。

各个组件的高度数据存储在单个纹理中。因此,它的大小必须是顶点数的2次幂。沿着两个相邻组件边缘的共享顶点行被复制并存储在每个组件中。因此,考虑每个组件中的四边形数量是有意义的。

下面是一个非常简单的地形(其轮廓为绿色),包含四个组件。每个组件由一个四边形组成。一个组件已经被分离,以显示组件相交处的顶点是如何重复的。

组件可以分为1或4(2x2)个子分段。这些分段是地形LOD计算的基本单元。

使用4(2x2)子分段选项可以得到与使用四倍组件(每个组件只有一个子分段)相同大小的高度图,但是使用更少的组件通常可以获得更好的性能。

每个分段的大小(以顶点数量计)必须是2的幂(最大为256x256)。这样,不同的LOD级别可以存储在纹理的mipmap中。这将导致一个组件中各个方向(x或y)的四边形的数量要么是2的幂减1(如果每个组件有1个分段),要么是2的幂减2(如果每个组件有4个分段)。

下面是一个单独的组件(其轮廓为绿色),包含四个分段。每个分段由9(3x3)个四边形组成。同样,您可以看到这些分段相交的顶点都是重复的。

组件大小与组件总数的选择是一种性能上的权衡。较小的组件大小可实现更快的LOD过渡,也可实现更多地形的遮挡,但是组件越小,需要的组件越多。

每个组件都有一个渲染线程CPU处理成本,每个分段都是一个绘制调用,所以尽量将这些数字保持在最小值。对于最大的地形,Epic建议最多使用1024个组件。

正如您所看到的,地形的维度是基于每个分段中的四边形数量、每个组件中的分段数量以及地形中呈现的组件数量。一旦确定了组件数量和每个组件的分辨率,计算整个地形的维度就变得非常简单。

以下是一些示例场景:

示例1

如果我们从包含64x64个顶点的单个分段组成的组件开始,那么组件的大小就是63x63个四边形。假设我们有一个由10x10个这些组件组成的地形,那么地形中总共有630x630个四边形。现在,如果我们想要导入这样一个地形的高度,我们必须有一个有631x631个顶点的高度图,因为存在的顶点总是比四边形多一行(想象一个1x1四边形 - 它需要4个顶点)。因此631x631是一个有效的地形大小。

示例2

如果某个组件可分成4个子分段,且每个子分段由64x64个顶点组成。这样每个分段就有63x63个四边形,每个组件就有126x126个四边形。如果我们有32x32个这样的组件,我们在每个方向上得到共计126 * 32 = 4032个四边形。因此整个地形将有4033x4033个顶点。

上述例子都以正方形地形为探讨对象。但是,您可以创建非正方形的地形。例如,在第一个例子中,10x10并不特殊。假设每个组件有63个四边形,您可以得到由AxB个组件组成,且总大小为(A 63+1 , B 63+1)个顶点的任意地形。

虚幻引擎4在计算地形高度时,读取的数值范围在-256到255.992之间,使用的精度为16位。然后,这个数值会和你在导入高度图时使用的Z缩放值相乘。假如,如果Z缩放值是1,则最大高度大约是256厘米,而最大深度约是-256厘米。因此,当Z缩放值的默认值为100时,高度值的范围在256米(m)到-256米(m)之间。

为了计算自定义高度,你需要使用一个比例值,把你的自定义高度的范围转换成虚幻引擎使用的-256到256的范围。由于高度范围总计有512个单位(-256到0是256个单位,0到256是256个单位),所以比例是1/512,也就是0.001953125。让我们首先把测量单位换算成厘米,然后乘以这个比例。下面是一个例子:

假设你希望在虚幻引擎中表现夏威夷的最高峰莫纳克亚峰(海拔4207米):

首先让4270乘以100,将高度单位转换为厘米。结果是420700厘米。

为了让事情变得更简单,这里有一些尺寸可以用于在最大化面积的同时最小化地形组件的数量。

图层调试(Layer Debug) 模式允许在视口中将特定图层的权重显示在地形上。您可以在视口的视图(View)菜单下的 地形查看器(Landscape Visualizers) 下启用图层调试模式。
启用图层调试模式后,将显示可用于选择单独颜色信道的单选按钮,且目标图层将包含在列表中。

选择一个信道将对地形应用一个着色器,该着色器显示所选目标图层的信道所覆盖的区域。

以上是关于做个小工具显示UE里地形的高度图和权重图的主要内容,如果未能解决你的问题,请参考以下文章

Houdini for Unity(PDG)/UE4 大地形的实践总结(一)

UE4地形快速入门指南、地形提示和技巧0060bate1

Houdini技术体系 基础管线 :UE4以选择区域的方式对地形做生成和更新 上篇

ue4获取所在地形

UE4 Landscape室外地形0059bate1

虚幻UE4怎么让文本竖排显示?