Set Texture of Landscape's Material

I had a similar problem earlier, in which I needed to be able to change a dynamic property of a material applied to a landscape. This was accomplished, and I was hoping my current problem could be solved with similar code. My current problem is that data for my material is stored in a texture that needs to be modified at runtime. How could I go about modifying this texture or replacing it with a dynamic texture?

1 Like

I have fixed this problem by modifying the code provided by to modify texture parameters instead of vector parameters. The required code is this:



/**
 * Start of code taken from MaterialInstance.cpp
 */
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER_DECLARE_TEMPLATE(
	SetMIParameterValue,ParameterType,
	const UMaterialInstance*,Instance,Instance,
	FName,ParameterName,Parameter.ParameterName,
	typename ParameterType::ValueType,Value,ParameterType::GetValue(Parameter),
{
	Instance->Resources[0]->RenderThread_UpdateParameter(ParameterName, Value);
	if (Instance->Resources[1])
	{
		Instance->Resources[1]->RenderThread_UpdateParameter(ParameterName, Value);
	}
	if (Instance->Resources[2])
	{
		Instance->Resources[2]->RenderThread_UpdateParameter(ParameterName, Value);
	}
});

/**
 * Updates a parameter on the material instance from the game thread.
 */
template <typename ParameterType>
void GameThread_UpdateMIParameter(const UMaterialInstance* Instance, const ParameterType& Parameter)
{
	ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER_CREATE_TEMPLATE(
		SetMIParameterValue,ParameterType,
		const UMaterialInstance*,Instance,
		FName,Parameter.ParameterName,
		typename ParameterType::ValueType,ParameterType::GetValue(Parameter)
		);
}

/**
 * Cache uniform expressions for the given material.
 * @param MaterialInstance - The material instance for which to cache uniform expressions.
 */
void CacheMaterialInstanceUniformExpressions(const UMaterialInstance* MaterialInstance)
{
	// Only cache the unselected + unhovered material instance. Selection color
	// can change at runtime and would invalidate the parameter cache.
	if (MaterialInstance->Resources[0])
	{
		MaterialInstance->Resources[0]->CacheUniformExpressions_GameThread();
	}
}
/**
 * End of code taken from MaterialInstance.cpp
 */
void SetTextureParameterValue(ALandscapeProxy* Landscape, FName ParameterName, UTexture* Value)
{
	if (Landscape)
	{
		for (int32 Index = 0; Index < Landscape->LandscapeComponents.Num(); ++Index)
		{
			if (Landscape->LandscapeComponents[Index])
			{
				UMaterialInstanceConstant* MIC = Landscape->LandscapeComponents[Index]->MaterialInstance;
				if (MIC)
				{
					/**
					 * Start of code taken from UMaterialInstance::SetSetVectorParameterValueInternal and adjusted to use MIC instead of this
					 */
					 FTextureParameterValue* ParameterValue = GameThread_FindParameterByName( //from MaterialInstanceSupport.h
					 	MIC->TextureParameterValues,
					 	ParameterName
						);

					if(!ParameterValue)
					{
						// If there's no element for the named parameter in array yet, add one.
						ParameterValue = new(MIC->TextureParameterValues) FTextureParameterValue;
						ParameterValue->ParameterName = ParameterName;
						ParameterValue->ExpressionGUID.Invalidate();
						// Force an update on first use
						//ParameterValue->ParameterValue.B = Value.B - 1.f;
					}
					
						ParameterValue->ParameterValue = Value;
						// Update the material instance data in the rendering thread.
						GameThread_UpdateMIParameter(MIC, *ParameterValue);
						CacheMaterialInstanceUniformExpressions(MIC);
					/**
					 * End of code taken from UMaterialInstance::SetSetVectorParameterValueInternal and adjusted to use MIC instead of this
					 */
				}
			}
		}
	}
}

With this code, I am able to assign a dynamic texture to my landscape material allowing for real-time modification of my grid.

Good job.

Although it came to my mind that you could remove just one single line “check(GIsEditor)” in every UMaterialInstanceConstant::Set*ParameterValueEditorOnly function and you would be able to do the same (with far less code), but at the “cost” of an engine modification.

I don’t know when this was made available but now (I’m currently using 4.24) landscapeProxy have a method setLandscapeMaterialTextureParam (or scalar or vector) Set Landscape Material Texture Parameter Value | Unreal Engine Documentation

It only works if you check “use dynamic material instance” on your landscape actor.

1 Like

Thank you. Been trying to find out why I couldn’t set parameters on my material.