UE4 顶点着色 学习笔记

复制代码
void UVertexPainterBPLibrary::PaintVertexColorByIndex(UStaticMeshComponent* StaticMeshComponent, FColor LinearColor, int Index, int LODIndex)
{ 
	if (!StaticMeshComponent || !StaticMeshComponent->GetStaticMesh()) {
		return;
	}
	const int LODNum = StaticMeshComponent->GetStaticMesh()->GetNumLODs();
	StaticMeshComponent->SetLODDataCount(LODNum, LODNum);
	if (!StaticMeshComponent->LODData.IsValidIndex(LODIndex)) {
		return;
	}
	FStaticMeshComponentLODInfo& LODInfo = StaticMeshComponent->LODData[LODIndex];

	const int32 VertexNum = StaticMeshComponent->GetStaticMesh()->GetRenderData()->LODResources[LODIndex].GetNumVertices();

	if (Index < 0 || Index >= VertexNum) {
		return;
	}

	TArray<FColor> VertexColors;
	LODInfo.OverrideVertexColors->GetVertexColors(VertexColors);
	VertexColors[Index] = LinearColor;
	LODInfo.OverrideVertexColors = new FColorVertexBuffer;
	LODInfo.OverrideVertexColors->InitFromColorArray(VertexColors);

	BeginInitResource(LODInfo.OverrideVertexColors);
}

首先区别一下StaticMesh和StaticMeshComponent

StaticMesh是模型本身

而StaticMeshComponent是模型出来的实例

直接修改StaticMesh的内容,所有StaticMeshComponent实例都会产生变化

而修改StaticMeshComponent直会对实例产生影响不会对StaticMesh有任何修改



函数参数

1、要修改顶点着色的StaticMeshCommponent

2、第二个顶点所修改成的颜色

3、顶点的下标,具体修改的是哪一个顶点

4、LOD的下标,根据距离不同启用不同的LOD,对应顶点数量也会发生变化。

总结:

具体哪一个模型的哪一个LOD下的哪一个顶点,修改成什么颜色


if (!StaticMeshComponent || !StaticMeshComponent->GetStaticMesh()) {

return;

}

如果StaticMeshComponent为空,或者StaticMeshComponent对应的StaticMesh为空,则退出函数执行


const int LODNum = StaticMeshComponent->GetStaticMesh()->GetNumLODs();

StaticMeshComponent->SetLODDataCount(LODNum, LODNum);

获取StaticMeshComponent所使用的StaticMesh的LOD数量,将之LOD数量设置为,StaticMeshComponent的LOD上


if (!StaticMeshComponent->LODData.IsValidIndex(LODIndex)) {

return;

}

如果函数参数的LODIndex是无效的,则不执行函数后续


FStaticMeshComponentLODInfo& LODInfo = StaticMeshComponent->LODData[LODIndex];

const int32 VertexNum = StaticMeshComponent->GetStaticMesh()->GetRenderData()->LODResources[LODIndex].GetNumVertices();

获取StaticMeshComponent的LOD信息

复制代码
USTRUCT()
struct FStaticMeshComponentLODInfo
{
	GENERATED_USTRUCT_BODY()

	/** Uniquely identifies this LOD's built map data. */
	FGuid MapBuildDataId;

	/** Used during deserialization to temporarily store legacy lightmap data. */
	FMeshMapBuildData* LegacyMapBuildData;

	/** 
	 * Transient override lightmap data, used by landscape grass.
	 * Be sure to add your component to UMapBuildDataRegistry::CleanupTransientOverrideMapBuildData() for proper cleanup
	 * so that you don't get stale rendering resource references if the underlying MapBuildData is gone (lighting scenario changes, new static lighting build, etc.)
	 */
	TUniquePtr<FMeshMapBuildData> OverrideMapBuildData;

	/** Vertex data cached at the time this LOD was painted, if any */
	TArray<struct FPaintedVertex> PaintedVertices;

	/** Vertex colors to use for this mesh LOD */
	FColorVertexBuffer* OverrideVertexColors;

	/** Information for each section about what range of PreCulledIndexBuffer to use.  If no preculled index data is available, PreCulledSections will be empty. */
	TArray<FPreCulledStaticMeshSection> PreCulledSections;

	FRawStaticIndexBuffer PreCulledIndexBuffer;

	/** 
	 * Owner of this FStaticMeshComponentLODInfo 
	 * Warning, can be NULL for a component created via SpawnActor off of a blueprint default (LODData will be created without a call to SetLODDataCount).
	 */
	class UStaticMeshComponent* OwningComponent;

	/** Default constructor */
	FStaticMeshComponentLODInfo();
	FStaticMeshComponentLODInfo(UStaticMeshComponent* InOwningComponent);
	/** Destructor */
	~FStaticMeshComponentLODInfo();

	/** Delete existing resources */
	void CleanUp();

	/** 
	 * Ensure this LODInfo has a valid MapBuildDataId GUID.
	 * @param LodIndex Index of the LOD this LODInfo represents.
	 * @return true if a new GUID was created, false otherwise.
	 */
	ENGINE_API bool CreateMapBuildDataId(int32 LodIndex);

	/**
	* Enqueues a rendering command to release the vertex colors.
	* The game thread must block until the rendering thread has processed the command before deleting OverrideVertexColors.
	*/
	ENGINE_API void BeginReleaseOverrideVertexColors();

	ENGINE_API void ReleaseOverrideVertexColorsAndBlock();

	void ReleaseResources();

	/** Methods for importing and exporting the painted vertex array to text */
	void ExportText(FString& ValueStr);
	void ImportText(const TCHAR** SourceText);

	/** Serializer. */
	friend FArchive& operator<<(FArchive& Ar,FStaticMeshComponentLODInfo& I);

private:
	/** Purposely hidden */
	FStaticMeshComponentLODInfo &operator=( const FStaticMeshComponentLODInfo &rhs ) { check(0); return *this; }
};

该结构体内有对应LOD的顶点颜色

然后获取对应的顶点个数


if (Index < 0 || Index >= VertexNum) {

return;

}

如果顶点个数不合法,则不执行后续逻辑


TArray<FColor> VertexColors;

LODInfo.OverrideVertexColors->GetVertexColors(VertexColors);

VertexColors[Index] = LinearColor;

LODInfo.OverrideVertexColors = new FColorVertexBuffer;

LODInfo.OverrideVertexColors->InitFromColorArray(VertexColors);

将顶点颜色赋值,然后刷新颜色


BeginInitResource(LODInfo.OverrideVertexColors);

开始初始化资源

记得用这个函数加对应模块

"RenderCore"


总结:就是把LODInfo.OverrideVertexColors重新刷新颜色

最后记住模型加上Vertex Color的材质


复制代码
void UVertexPainterBPLibrary::PaintVertexColorByIndex(UStaticMeshComponent* StaticMeshComponent, FColor LinearColor, int Index, int LODIndex)
{ 
	if (!StaticMeshComponent || !StaticMeshComponent->GetStaticMesh()) {
		return;
	}
	const int LODNum = StaticMeshComponent->GetStaticMesh()->GetNumLODs();
	StaticMeshComponent->SetLODDataCount(LODNum, LODNum);
	if (!StaticMeshComponent->LODData.IsValidIndex(LODIndex)) {
		return;
	}
	FStaticMeshComponentLODInfo& LODInfo = StaticMeshComponent->LODData[LODIndex];

	FStaticMeshLODResourcesArray& LODResourcesArray = StaticMeshComponent->GetStaticMesh()->GetRenderData()->LODResources;
	const int32 VertexNum = LODResourcesArray[LODIndex].GetNumVertices();

	if (Index < 0 || Index >= VertexNum) {
		return;
	}

	TArray<FColor> VertexColors;

	if (LODInfo.OverrideVertexColors) {
		LODInfo.OverrideVertexColors->GetVertexColors(VertexColors);
	}
	else {
		if (LODResourcesArray[LODIndex].bHasColorVertexData) {
			LODResourcesArray[LODIndex].VertexBuffers.ColorVertexBuffer.GetVertexColors(VertexColors);
		}
		else {
			VertexColors.Init(FColor::White, VertexNum);
		}
	}
	VertexColors.SetNum(VertexNum);
	
	VertexColors[Index] = LinearColor;
	LODInfo.OverrideVertexColors = new FColorVertexBuffer;
	LODInfo.OverrideVertexColors->InitFromColorArray(VertexColors);

	BeginInitResource(LODInfo.OverrideVertexColors);
}

增加了一部分初始化LODInfo.OverrideVertexColors信息


复制代码
TArray<FColor> UVertexPainterBPLibrary::GetVertexColorsInLOD(UStaticMeshComponent* StaticMeshComponent, int32 LODIndex) {
	TArray<FColor> VertexColors;
	if (!StaticMeshComponent || !StaticMeshComponent->GetStaticMesh()) {
		return VertexColors;
	}
	const int LODNum = StaticMeshComponent->GetStaticMesh()->GetNumLODs();
	if (LODIndex < 0 || LODIndex >= LODNum) {
		return VertexColors;
	}
	FStaticMeshComponentLODInfo& LODInfo = StaticMeshComponent->LODData[LODIndex];
	FStaticMeshLODResourcesArray& LODResourcesArray = StaticMeshComponent->GetStaticMesh()->GetRenderData()->LODResources;
	const int32 VertexNum = LODResourcesArray[LODIndex].GetNumVertices();
	if (StaticMeshComponent->LODData.IsValidIndex(LODIndex)) {
		if (LODInfo.OverrideVertexColors) {
			LODInfo.OverrideVertexColors->GetVertexColors(VertexColors);
			VertexColors.SetNum(VertexNum);
			return VertexColors;
		}
	}
	{
		if (LODResourcesArray[LODIndex].bHasColorVertexData) {
			LODResourcesArray[LODIndex].VertexBuffers.ColorVertexBuffer.GetVertexColors(VertexColors);
		}
		else {
			VertexColors.Init(FColor::White, VertexNum);
		}
	}
	VertexColors.SetNum(VertexNum);
	return VertexColors;
}

void UVertexPainterBPLibrary::PaintVertexColorOnSphere(UStaticMeshComponent* StaticMeshComponent, FColor LinearColor, float Radius, FVector Position)
{
	if (!StaticMeshComponent || !StaticMeshComponent->GetStaticMesh()) {
		return;
	}
	const int LODNum = StaticMeshComponent->GetStaticMesh()->GetNumLODs();
	StaticMeshComponent->SetLODDataCount(LODNum, LODNum);

	const FTransform& LocalToWorld = StaticMeshComponent->GetComponentTransform();
	int LODIndex = 0;
	for (FStaticMeshComponentLODInfo& LODInfo : StaticMeshComponent->LODData) {
		const FPositionVertexBuffer& PositionVertexBuffer = StaticMeshComponent->GetStaticMesh()->GetRenderData()->
			LODResources[LODIndex].VertexBuffers.PositionVertexBuffer;
		int VertexNum = PositionVertexBuffer.GetNumVertices();
		TArray<FColor> VertexColors = GetVertexColorsInLOD(StaticMeshComponent, LODIndex++);
		for (int i = 0; i < VertexNum; i++) {
			FVector VertexPosition = UKismetMathLibrary::TransformLocation(LocalToWorld, FVector(PositionVertexBuffer.VertexPosition(i)));
			float NormalizedDistance = UKismetMathLibrary::Vector_Distance(VertexPosition, Position)/Radius;
			if (NormalizedDistance <= 1) {
				VertexColors[i] = LinearColor;
			}
		}
		LODInfo.OverrideVertexColors = new FColorVertexBuffer;
		LODInfo.OverrideVertexColors->InitFromColorArray(VertexColors);

		BeginInitResource(LODInfo.OverrideVertexColors);
	}
	StaticMeshComponent->MarkRenderStateDirty();
	StaticMeshComponent->bDisallowMeshPaintPerInstance = true;
}

TArray<FColor> VertexColors;

if (!StaticMeshComponent || !StaticMeshComponent->GetStaticMesh()) {

return VertexColors;

}

const int LODNum = StaticMeshComponent->GetStaticMesh()->GetNumLODs();

if (LODIndex < 0 || LODIndex >= LODNum) {

return VertexColors;

}

前面依然是一个判断违规使用函数的条件

FStaticMeshComponentLODInfo& LODInfo = StaticMeshComponent->LODData[LODIndex];

FStaticMeshLODResourcesArray& LODResourcesArray = StaticMeshComponent->GetStaticMesh()->GetRenderData()->LODResources;

const int32 VertexNum = LODResourcesArray[LODIndex].GetNumVertices();

这里拿到LOD信息,然后获取LOD资源的数组,最后拿到顶点的数量

if (StaticMeshComponent->LODData.IsValidIndex(LODIndex)) {

if (LODInfo.OverrideVertexColors) {

LODInfo.OverrideVertexColors->GetVertexColors(VertexColors);

VertexColors.SetNum(VertexNum);

return VertexColors;

}

}

{

if (LODResourcesArray[LODIndex].bHasColorVertexData) {

LODResourcesArray[LODIndex].VertexBuffers.ColorVertexBuffer.GetVertexColors(VertexColors);

}

else {

VertexColors.Init(FColor::White, VertexNum);

}

}

这里初始化顶点颜色信息,防止获取到空颜色,造成报错


VertexColors.SetNum(VertexNum);

return VertexColors;

最后设置顶点颜色数组的长度,防止多取少取


if (!StaticMeshComponent || !StaticMeshComponent->GetStaticMesh()) {

return;

}

防止错误的输入


const int LODNum = StaticMeshComponent->GetStaticMesh()->GetNumLODs();

StaticMeshComponent->SetLODDataCount(LODNum, LODNum);

从StaticMesh的LOD数量设置到StaticMeshComponent里面去


const FTransform& LocalToWorld = StaticMeshComponent->GetComponentTransform();

int LODIndex = 0;

这里拿到Component在场景的世界变换信息


for (FStaticMeshComponentLODInfo& LODInfo : StaticMeshComponent->LODData) {

const FPositionVertexBuffer& PositionVertexBuffer = StaticMeshComponent->GetStaticMesh()->GetRenderData()->

LODResources[LODIndex].VertexBuffers.PositionVertexBuffer;

int VertexNum = PositionVertexBuffer.GetNumVertices();

TArray<FColor> VertexColors = GetVertexColorsInLOD(StaticMeshComponent, LODIndex++);

for (int i = 0; i < VertexNum; i++) {

FVector VertexPosition = UKismetMathLibrary::TransformLocation(LocalToWorld, FVector(PositionVertexBuffer.VertexPosition(i)));

float NormalizedDistance = UKismetMathLibrary::Vector_Distance(VertexPosition, Position)/Radius;

if (NormalizedDistance <= 1) {

VertexColors[i] = LinearColor;

}

}

LODInfo.OverrideVertexColors = new FColorVertexBuffer;

LODInfo.OverrideVertexColors->InitFromColorArray(VertexColors);

BeginInitResource(LODInfo.OverrideVertexColors);

}

先开始拿到顶点缓冲位置,这个信息是在LODResources里面,LOD里面有Vertex信息,当然顶点的位置信息也包括在里面

FPositionVertexBuffer是继承自VertexBuffer的

顶点数量,顶点位置的信息都在FVertexBuffer里面

接下来TArray<FColor>获取到顶点颜色的所有颜色,通过第一个函数获取,就不赘述

接下来就是对每个顶点进行赋值颜色信息,通过TransformLocation函数,可以将本地的VertexPosition转化为世界场景坐标

float NormalizedDistance = UKismetMathLibrary::Vector_Distance(VertexPosition, Position)/Radius;

if (NormalizedDistance <= 1) {

VertexColors[i] = LinearColor;

}

若顶点距离和着色距离范围内,则进行着色

最后这里都讲过

LODInfo.OverrideVertexColors = new FColorVertexBuffer;

LODInfo.OverrideVertexColors->InitFromColorArray(VertexColors);

BeginInitResource(LODInfo.OverrideVertexColors);


StaticMeshComponent->MarkRenderStateDirty();

StaticMeshComponent->bDisallowMeshPaintPerInstance = true;

防止第二个实例顶点着色无效


复制代码
TArray<FColor> UVertexPainterBPLibrary::GetSkeletalVertexColorsInLOD(USkeletalMeshComponent* SkeletalMeshComponent, int32 LODIndex) {
	TArray<FColor> VertexColors;
	if (!SkeletalMeshComponent || !SkeletalMeshComponent->SkeletalMesh) {
		return VertexColors;
	}
	const int LODNum = SkeletalMeshComponent->SkeletalMesh->GetLODNum();
	if (LODIndex < 0 || LODIndex >= LODNum) {
		return VertexColors;
	}
	FSkelMeshComponentLODInfo& LODInfo = SkeletalMeshComponent->LODInfo[LODIndex];
	FSkeletalMeshLODRenderData& LODResourcesArray = SkeletalMeshComponent->SkeletalMesh->GetResourceForRendering()->LODRenderData[LODIndex];
	const int32 VertexNum = LODResourcesArray.GetNumVertices();
	if (SkeletalMeshComponent->LODInfo.IsValidIndex(LODIndex)) {
		if (LODInfo.OverrideVertexColors) {
			LODInfo.OverrideVertexColors->GetVertexColors(VertexColors);
			VertexColors.SetNum(VertexNum);
			return VertexColors;
		}
	}
	{
		VertexColors.Init(FColor::White, VertexNum);
	}
	VertexColors.SetNum(VertexNum);
	return VertexColors;
}

void UVertexPainterBPLibrary::PaintVertexColorOnSkeletal(USkeletalMeshComponent* SkeletalMeshComponent, FColor LinearColor, float Radius, FVector Position) {
	if (!SkeletalMeshComponent || !SkeletalMeshComponent->SkeletalMesh) {
		return;
	}
	SkeletalMeshComponent->InitLODInfos();

	const FTransform& LocalToWorld = SkeletalMeshComponent->GetComponentTransform();
	int LODIndex = 0;
	for (FSkelMeshComponentLODInfo& LODInfo : SkeletalMeshComponent->LODInfo) {
		FSkeletalMeshLODRenderData& LODRenderData = SkeletalMeshComponent->SkeletalMesh->GetResourceForRendering()->LODRenderData[LODIndex];
		const int VertexNum = LODRenderData.GetNumVertices();
		FSkinWeightVertexBuffer& SkinWeightVertexBuffer = LODRenderData.SkinWeightVertexBuffer;
		TArray<FColor> VertexColors = GetSkeletalVertexColorsInLOD(SkeletalMeshComponent, LODIndex++);
		for (int i = 0; i < VertexNum; i++) {
			FVector VertexPosition = UKismetMathLibrary::TransformLocation(LocalToWorld, FVector(USkeletalMeshComponent::GetSkinnedVertexPosition(SkeletalMeshComponent, i,
				LODRenderData, SkinWeightVertexBuffer))); 
			float NormalizedDistance = UKismetMathLibrary::Vector_Distance(VertexPosition, Position) / Radius;
			if (NormalizedDistance <= 1) {
				VertexColors[i] = LinearColor;
			}
		}
		LODInfo.OverrideVertexColors = new FColorVertexBuffer;
		LODInfo.OverrideVertexColors->InitFromColorArray(VertexColors);

		BeginInitResource(LODInfo.OverrideVertexColors);
	}
	SkeletalMeshComponent->MarkRenderStateDirty();
}

最后来一个支持谷歌模型的顶点绘制


相关推荐
码出钞能力18 分钟前
linux内核模块的查看
linux·运维·服务器
考虑考虑34 分钟前
JDK9中的dropWhile
java·后端·java ee
想躺平的咸鱼干42 分钟前
Volatile解决指令重排和单例模式
java·开发语言·单例模式·线程·并发编程
hqxstudying1 小时前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·1 小时前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
Bug退退退1232 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
小皮侠2 小时前
nginx的使用
java·运维·服务器·前端·git·nginx·github
Zz_waiting.3 小时前
Javaweb - 10.4 ServletConfig 和 ServletContext
java·开发语言·前端·servlet·servletconfig·servletcontext·域对象
全栈凯哥3 小时前
02.SpringBoot常用Utils工具类详解
java·spring boot·后端
兮动人3 小时前
获取终端外网IP地址
java·网络·网络协议·tcp/ip·获取终端外网ip地址