In this post we’re going to see how we can paint vertices during runtime. This post was created with Unreal Engine 4.20 so if you’re using another version you may need to update the code to match Epic’s API for the engine you’re using. It’s worth mentioning that the following code runs in packaged builds as well.

Adding the required Dependencies

Before typing any code go into <MyProject>.Build.cs file and add the RenderCore dependency:

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "RenderCore" }); 1 PublicDependencyModuleNames . AddRange ( new string [ ] { "Core" , "CoreUObject" , "Engine" , "InputCore" , "RenderCore" } ) ;

Creating the required Materials

In order for our code to work, we need to assign the following material in the static meshes we want to paint. As you may have noticed it’s the same material we’ve used in the “Creating Procedural Meshes” post:

Painting vertices at runtime

To paint all the vertices at runtime, I have created a Blueprint function library with the following public function:

PaintSMVertices function signature UFUNCTION(BlueprintCallable, Category = VertexPainting) static void PaintSMVertices(UStaticMeshComponent* SMComp); 1 2 UFUNCTION ( BlueprintCallable , Category = VertexPainting ) static void PaintSMVertices ( UStaticMeshComponent * SMComp ) ;

Here is the code that we need to execute in order to paint the vertices of the mesh:

Vertex painting at runtime void UVertexPaintingFunctionLibrary::PaintSMVertices(UStaticMeshComponent* SMComp) { if (!SMComp) return; //Get the static mesh that we're going to paint UStaticMesh* SM = SMComp->GetStaticMesh(); if (SM) { //Get the vertex buffer from the 1st lod //FPositionVertexBuffer* PositionVertexBuffer = &SM->RenderData->LODResources[0].VertexBuffers.PositionVertexBuffer; //Make sure that we have at least 1 LOD SMComp->SetLODDataCount(1, SMComp->LODData.Num()); FStaticMeshComponentLODInfo* LODInfo = &SMComp->LODData[0]; //We're going to modify the 1st LOD only //Empty the painted vertices and assign a new color vertex buffer which will contain the new colors for each vertex LODInfo->PaintedVertices.Empty(); LODInfo->OverrideVertexColors = new FColorVertexBuffer(); //We're going to use the LODResources to get the total number of vertices that the provided mesh has FStaticMeshLODResources& LodResources = SM->RenderData->LODResources[0]; //Creating a color array TArray<FColor> RandomColorArray; //Since we know beforehand the number of elements we might as well reserve the memory now RandomColorArray.Reserve(LodResources.GetNumVertices() - 1); for (int32 i = 0; i < LodResources.GetNumVertices(); i++) { //Generate a random color for the current vertex RandomColorArray.Add(FColor::MakeRandomColor()); } //Initialize the new vertex colros with the array we created above LODInfo->OverrideVertexColors->InitFromColorArray(RandomColorArray); //Initialize resource and mark render state of object as dirty in order for the engine to re-render it BeginInitResource(LODInfo->OverrideVertexColors); SMComp->MarkRenderStateDirty(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 void UVertexPaintingFunctionLibrary :: PaintSMVertices ( UStaticMeshComponent * SMComp ) { if ( ! SMComp ) return ; //Get the static mesh that we're going to paint UStaticMesh * SM = SMComp -> GetStaticMesh ( ) ; if ( SM ) { //Get the vertex buffer from the 1st lod //FPositionVertexBuffer* PositionVertexBuffer = &SM->RenderData->LODResources[0].VertexBuffers.PositionVertexBuffer; //Make sure that we have at least 1 LOD SMComp -> SetLODDataCount ( 1 , SMComp -> LODData . Num ( ) ) ; FStaticMeshComponentLODInfo * LODInfo = &SMComp -> LODData [ 0 ] ; //We're going to modify the 1st LOD only //Empty the painted vertices and assign a new color vertex buffer which will contain the new colors for each vertex LODInfo -> PaintedVertices . Empty ( ) ; LODInfo -> OverrideVertexColors = new FColorVertexBuffer ( ) ; //We're going to use the LODResources to get the total number of vertices that the provided mesh has FStaticMeshLODResources & LodResources = SM -> RenderData -> LODResources [ 0 ] ; //Creating a color array TArray < FColor > RandomColorArray ; //Since we know beforehand the number of elements we might as well reserve the memory now RandomColorArray . Reserve ( LodResources . GetNumVertices ( ) - 1 ) ; for ( int32 i = 0 ; i < LodResources . GetNumVertices ( ) ; i ++ ) { //Generate a random color for the current vertex RandomColorArray . Add ( FColor :: MakeRandomColor ( ) ) ; } //Initialize the new vertex colros with the array we created above LODInfo -> OverrideVertexColors -> InitFromColorArray ( RandomColorArray ) ; //Initialize resource and mark render state of object as dirty in order for the engine to re-render it BeginInitResource ( LODInfo -> OverrideVertexColors ) ; SMComp -> MarkRenderStateDirty ( ) ; } }

Just for demonstration purposes, I created a Blueprint with a static mesh that has the the M_VertexColors material I created above and inside the Tick function I connected the Static Mesh component with the PaintSMVertices and here’s the end result: