how to initialize umaterial and assign texture to it?

learning unreal is really frustrating as there lacks documents and examples.

I want to create a UMaterial and assign a procedural texture map to it. but I can’t figure out how.

I don’t know, for example, how to write its constructor.

I have seen example for AActor class:

AActor(const class FPostConstructInitializeProperties& PCIP)

and for UGeneratedMeshComponent

UGeneratedMeshComponent(const FObjectInitializer& Initializer)

As you can see, they use different inputs. but what about UMaterial? there is simply no clue in the document. I even checked the engine source code, I can’t find anything!

how to assign texture map to a material is also a mystery.

could you please show me an example?

Thanks

1 Like

That’s not something I’ve had to do before, but I have managed to change a texture on a mesh to a Dynamic Instance which I can then draw to using:

ButtonMaterial = GetStaticMeshComponent()->CreateAndSetMaterialInstanceDynamic(0);

I don’t know if that helps you though. I figured I’d post it while you wait for someone smarter to come along :slight_smile:

thank you!
so after you obtain the ButtonMaterial, how would you draw on it?

Here’s how I draw very low quality text to my button (FCanvas, not UCanvas)




void AMenuButton::CreateTextTexture(FString TextToDraw)
{
	if (ButtonMaterial == NULL) 
	{ 
		GEngine->AddOnScreenDebugMessage(-1, 100.0f, FColor::Red, FString("AMenuButton::CreateTextTexture ButtonMaterial == NULL, text = "+TextToDraw));
		return; 
	}
	UTextureRenderTarget2D* Texture = NewObject<UTextureRenderTarget2D>(this);
	Texture->InitAutoFormat(256,128);
	Texture->bNeedsTwoCopies = false;
	Texture->bHDR = false;
	Texture->ClearColor = FLinearColor::Black;
	Texture->UpdateResourceImmediate();

	FCanvas Canvas = FCanvas(Texture->GameThread_GetRenderTargetResource(), NULL, GetWorld(), GMaxRHIFeatureLevel);
	Canvas.Clear(FLinearColor::Black);
	Texture->UpdateResourceImmediate(false);

	FText ThisText = FText::FromString(TextToDraw);
	FFontRenderInfo RenderInfo = FFontRenderInfo();
	RenderInfo.bClipText = true;
	FCanvasTextItem TextItem(FVector2D(0.0, 0.0), ThisText, Font, FLinearColor::White);
	TextItem.Scale = TextScale,
	TextItem.BlendMode = SE_BLEND_Translucent;
	TextItem.FontRenderInfo = RenderInfo;
	Canvas.DrawItem(TextItem);

	// TODO: Center the text, using TextItem.DrawnSize which will tell us how big it is - maybe use the material editor and put an offset in there;
	
	ButtonMaterial->SetTextureParameterValue(TEXT("TextTexture"), Texture);


	Canvas.Flush_GameThread();

	//GEngine->AddOnScreenDebugMessage(-1, 20.0f, FColor::Red, FString("Updated button text to : "+TextToDraw));


}


I never quite figured out how to turn the FCanvas into a UCanvas which has more features.

sigh, I also found the function SetTextureParameterValue from an old example, but it is deprecated I think. I did global search in 4.8.1 source code, can’t find it anymore.

SetTextureParameterValue is definitely not deprecated, I use it all over my code with MID’s I create.

SetTextureParameterValue is declared in MaterialInstanceDynamic.h ( /Runtime/Engine/Classes/Materials/ )

thank you very much, but I have one more question to bother you.

when using SetTextureParameterValue, a texture name needs to be provided. But how do I get the name? for diffuse color, for example?

my current code looks like this:



	UMaterial *mt = LoadObject<UMaterial>(nullptr, TEXT("/Game/StarterContent/Materials/M_Wood_Walnut"));
	TArray<FName> OutParameterNames;
	TArray<FGuid> OutParameterIds;
	mt->GetAllTextureParameterNames(OutParameterNames, OutParameterIds);

	//UMaterialInstanceDynamic *md = SphereVisual->CreateAndSetMaterialInstanceDynamic(0);

	SphereVisual->SetMaterial(0, mt);
	

	UTexture2D *DynamicTexture = UTexture2D::CreateTransient(256, 256);

	// Allocate the texture HRI
	DynamicTexture->UpdateResource();
	UMaterialInstanceDynamic *md = UMaterialInstanceDynamic::Create(mt, nullptr);
	md->SetTextureParameterValue(FName(TEXT("BaseColorTextureSamples")), DynamicTexture);


I noticed (within the editor) that in this material: /Game/StarterContent/Materials/M_Wood_Walnut

there is a texture map called: BaseColorTextureSamples

so I did this:

md-&gt;SetTextureParameterValue(FName(TEXT("BaseColorTextureSamples")), DynamicTexture);

and my program crashed. it doesn’t seem to find BaseColorTextureSamples

So then I did this GetAllTextureParameterNames(OutParameterNames, OutParameterIds); to find out all texture names associated with the material,

but the result is an empty array.

after looking at the engine’s source code, it seems to be allowed to use any name for texture. by default, it just replaces the diffuse color.

my problem was calling the function in the constructor of my AActor class. the program crashes because when the AActor constructor gets called, there is no GEngine object .

I moved stuff to the BeginPlay function, and it won’t crash anymore.

Man, should learning be this hard?

A good rule of thumb is to never call anything in the constructor that might fail. Generally in UE4 I just use the constructor to assign default values. I do most of the work in other functions like:

UGeneratedMeshComponent(const FObjectInitializer& Initializer)

or even BeginPlay()

Regarding the texture names - in your material you create parameters that have names - like a texture parameter for example. That is the name you use when assigning parameters from c++.

Materials don’t work this way. This sounds like a lack of familiarity with the Material Editor / Material system in general. Basically you have to create a Material asset first, in the engine. You then need to create a texture sample parameter within that material, give it a name (e.g. TextureMap).

You then need to create a DynamicMaterialInstance of that material in code, and tell the mesh to use that DMI. Then, you can change the texture using SetTextureParameterValue. In order to get the texture, you could either create a UPROPERTY of type UTexture2D* (I think that’s the class) and specify it in a Blueprint, or you can use ConstructorHelpers::FObjectFinder instead if you have a static asset somewhere. Alternatively if you’ve created a procedural texture or a render target, you can just make it use that too.

For example:

Then in code:

MyClass.h



        UPROPERTY(EditDefaultsOnly, Category = "My Texture")
	UTexture2D* TextureToUse;


MyClass.cpp



        UDynamicMaterialInstance* MyDMI = SphereVisual->CreateAndSetMaterialInstanceDynamic(0)
        if (MyDMI)
        {
               MyDMI->SetTextureParameterValue(TEXT("TextureMap"), TextureToUse);
        }


The FName in SetTextureParameterValue isn’t the name of a texture, its the name of the parameter you want to assign that texture too.

EDIT:

Additionally, I would not use BeginPlay() to set initial properties. Instead there is a virtual function for all Actors that you can Override, called PostInitializeComponents() This function runs immediately after the construction script and after all default parameters have been set (including any overrides from Blueprint), meaning it’s safe to do most things there like DynamicMaterials etc. Just don’t forget to call Super::PostInitializeComponents(); if you do override it.

The reason to use that over BeginPlay(), is because BeginPlay is ONLY called by the GameMode when the game begins - which isn’t necessarily/always straight away.

3 Likes