UE4 顶点着色 学习笔记

2023-12-13 04:21:50
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();
}

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


文章来源:https://blog.csdn.net/qqQQqsadfj/article/details/134710003
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。