Delegate - Fire event from c++ and have blueprint act on it

As you have mentioned it is possible to set materials on an object via c++, these materials can also be used within Unreal Material Editor, and extended using Material Functions to create your desired blend effect during run time. (It’s also possible to paint layers together using the landscape tool)

Here are some links to documentation on Material Instances, Material Functions, and Landscape Materials:

[Material Instances][1]

[Material Functions][2]

[Landscape Materials][3]

Also here is a code example on setting materials via in c++

MaterialActor.h

 #pragma once
     
     #include "CoreMinimal.h"
     #include "GameFramework/Actor.h"
     #include "Engine/Classes/Materials/Material.h"
     #include "MaterialActor.generated.h"
     
     UCLASS()
     class TESTINGMATERIALS_API AMaterialActor : public AActor
     {
         GENERATED_BODY()
         
     public:    
         // Sets default values for this actor's properties
         AMaterialActor();
     
     protected:
         // Called when the game starts or when spawned
         virtual void BeginPlay() override;
     
     public:    
         // Called every frame
         virtual void Tick(float DeltaTime) override;
         
         UPROPERTY(VisibleAnywhere)
         UStaticMeshComponent* MeshComp;
     
         UPROPERTY(VisibleAnywhere)
         UMaterial* StoredMaterial;
             
 };

MaterialActor.Cpp

#include "MaterialActor.h"
 
 // Sets default values
 AMaterialActor::AMaterialActor()
 {
      // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
     PrimaryActorTick.bCanEverTick = true;
 
     MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
     RootComponent = MeshComp;
 
     static ConstructorHelpers::FObjectFinder<UStaticMesh>FoundMesh(TEXT("/Engine/EditorMeshes/EditorSphere.EditorSphere"));
 
     if (FoundMesh.Succeeded())
     {
         MeshComp->SetStaticMesh(FoundMesh.Object);
     }
 
     static ConstructorHelpers::FObjectFinder<UMaterial>FoundMaterial(TEXT("/Game/StarterContent/Materials/M_CobbleStone_Pebble.M_CobbleStone_Pebble"));
 
     if (FoundMaterial.Succeeded())
     {
         StoredMaterial = FoundMaterial.Object;
     }
 
  
     MeshComp->SetMaterial(0, StoredMaterial);
     
 }
 
 // Called when the game starts or when spawned
 void AMaterialActor::BeginPlay()
 {
     Super::BeginPlay();
     
 }
 
 // Called every frame
 void AMaterialActor::Tick(float DeltaTime)
 {
     Super::Tick(DeltaTime);
 
 }

This will yield a material change on the texture:

Hope this helps!