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 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 OverrideMapBuildData;
/** Vertex data cached at the time this LOD was painted, if any */
TArray 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 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
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 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 UVertexPainterBPLibrary::GetVertexColorsInLOD(UStaticMeshComponent* StaticMeshComponent, int32 LODIndex) {
TArray 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 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
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
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
接下来就是对每个顶点进行赋值颜色信息,通过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 UVertexPainterBPLibrary::GetSkeletalVertexColorsInLOD(USkeletalMeshComponent* SkeletalMeshComponent, int32 LODIndex) {
TArray 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 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();
}
最后来一个支持谷歌模型的顶点绘制