在这篇文章中,我们将学习如何从 C++ 代码创建新材质、创建节点和创建连接。

您可以将此代码添加到自定义插件中,以便在用户单击编辑器按钮时神奇地创建新材质。

请记住,此代码旨在在编辑器中执行。您的插件必须是编辑器插件。此代码在运行时无法工作...

图解步骤

1、新建一个插件

UE4 – 以编程方式创建新材质和内部节点_UE4

UE4 – 以编程方式创建新材质和内部节点_UE4_02

2、进入VS编写代码,把文章后面的完整代码放入Test.cpp的PluginButtonClicked函数里面(注意我的插件名字叫Test,你的可能和我不一样)。还有记得添加相关头文件和相应模块,要不然编译失败。

UE4 – 以编程方式创建新材质和内部节点_UE4_03

 UE4 – 以编程方式创建新材质和内部节点_UE4_04

UE4 – 以编程方式创建新材质和内部节点_UE4_05

 3、好像需要重新启动虚幻编辑器,要不然没有效果。重新启动以后,先导入一张图片,然后点击我们创建的按钮。

UE4 – 以编程方式创建新材质和内部节点_UE4_06

UE4 – 以编程方式创建新材质和内部节点_UE4_07

 4、发现材质创建了,双击材质。

UE4 – 以编程方式创建新材质和内部节点_UE4_08

UE4 – 以编程方式创建新材质和内部节点_UE4_09

5、节点都在一起,可以拖开进行查看效果。

UE4 – 以编程方式创建新材质和内部节点_UE4_10

详细步骤 创建一个新资产:材质

首先,我们必须以编程方式创建一个新资产,例如,让我们在根目录文件夹“/Game/”中创建一个名为“M_Material”的新材质。

为此,我们需要创建一个UPackage(这是我们资产的存储对象)。然后,我们使用一个材质工厂来创建我们的UMaterial

FString MaterialBaseName = "M_Material";
FString PackageName = "/Game/";
PackageName += MaterialBaseName;
UPackage* Package = CreatePackage(NULL, *PackageName);

// Create an unreal material asset
auto MaterialFactory = NewObject<UMaterialFactoryNew>();
UMaterial* UnrealMaterial = (UMaterial*)MaterialFactory->FactoryCreateNew(UMaterial::StaticClass(), Package, *MaterialBaseName, RF_Standalone | RF_Public, NULL, GWarn);

然后,我们必须让 Unreal 做一些关于资产创建的后台处理,并加载/设置我们刚刚创建的包。如果没有这个代码,资产创建就无法完成,并且可能会在以后带来一些问题……

FAssetRegistryModule::AssetCreated(UnrealMaterial);
Package->FullyLoad();
Package->SetDirtyFlag(true);

现在,我们的资产已创建但为空。

最后,一旦我们创建了材质并且——也许——添加了节点,让材质自行更新:

// Let the material update itself if necessary
UnrealMaterial->PreEditChange(NULL);
UnrealMaterial->PostEditChange();
// make sure that any static meshes, etc using this material will stop using the FMaterialResource of the original
// material, and will use the new FMaterialResource created when we make a new UMaterial in place
FGlobalComponentReregisterContext RecreateComponents;

您可以测试此代码以检查新的空材质创建。

UMaterialExpression

我们将添加的每个节点都是 UMaterialExpression 的子对象。查看虚幻引擎文档以查看可能的节点。

创建节点后,将其添加到材质表达式中。

基本上,必须将表达式分配给要链接到材质的节点。

我们将使用相同的方式在节点之间创建连接:如果我们要将乘法节点的结果设置为材质的基色,则将乘法节点分配给Material->BaseColor.Expression

这里有些例子:

用节点填充材质资产

将“0”常量分配给镜面反射

让我们创建最简单的节点:常量节点。

创建一个新的 UMaterialExpressionConstant 节点,然后将其添加到材质表达式中,将其赋值为 0,最后将其分配给镜面反射表达式。

UMaterialExpressionConstant* ZeroExpression = NewObject<UMaterialExpressionConstant>(UnrealMaterial);
ZeroExpression->R = 0.0f;
UnrealMaterial->Expressions.Add(ZeroExpression);
UnrealMaterial->Specular.Expression = ZeroExpression;

我们的第一个节点已创建并分配!

UE4 – 以编程方式创建新材质和内部节点_UE4_11

将纹理与材质基色连接起来 

我们假设您已经将纹理资产放入您的内容文件夹中。

我们需要获取UTexture引用来创建节点。所以我们需要通过路径获取这个资产:

FStringAssetReference DiffuseAssetPath("/Game/T_Texture");
UTexture* DiffuseTexture = Cast(DiffuseAssetPath.TryLoad());
if (DiffuseTexture)
{
...
}

然后,创建一个新的 TextureSample 材质表达式,为其指定我们的纹理并将其与材质基色连接。

// Make texture sampler
UMaterialExpressionTextureSample* TextureExpression = NewObject(UnrealMaterial);
TextureExpression->Texture = DiffuseTexture;
TextureExpression->SamplerType = SAMPLERTYPE_Color;
UnrealMaterial->Expressions.Add(TextureExpression);
UnrealMaterial->BaseColor.Expression = TextureExpression;

UE4 – 以编程方式创建新材质和内部节点_UE4_12

使用乘法节点 

如果我们想创建一个纹理平铺系统,我们需要将纹理坐标与一些标量参数相乘。

让我们创建一个乘法节点并将其分配给我们的纹理坐标。

// Tiling system
UMaterialExpressionMultiply* Multiply = NewObject<UMaterialExpressionMultiply>(UnrealMaterial);
UnrealMaterial->Expressions.Add(Multiply);
TextureExpression->Coordinates.Expression = Multiply;

将纹理坐标节点分配给乘法节点的 A 参数:

UMaterialExpressionTextureCoordinate* TexCoords = NewObject<UMaterialExpressionTextureCoordinate>(UnrealMaterial);
UnrealMaterial->Expressions.Add(TexCoords);
Multiply->A.Expression = TexCoords;

如您所想,您可以使用相同的方式将其他节点分配给 B 参数。

平铺系统

现在,通过创建 2 个包含 X 和 Y 纹理重复的标量参数来完成平铺系统。

// Tiling
UMaterialExpressionAppendVector* Append = NewObject<UMaterialExpressionAppendVector>(UnrealMaterial);
UnrealMaterial->Expressions.Add(Append);
Multiply->B.Expression = Append;
UMaterialExpressionScalarParameter* XParam = NewObject<UMaterialExpressionScalarParameter>(UnrealMaterial);
UMaterialExpressionScalarParameter* YParam = NewObject<UMaterialExpressionScalarParameter>(UnrealMaterial);
UnrealMaterial->Expressions.Add(XParam);
UnrealMaterial->Expressions.Add(YParam);
XParam->ParameterName = "TextureRepeatX";
XParam->DefaultValue = 1;
YParam->ParameterName = "TextureRepeatY";
YParam->DefaultValue = 1;
Append->A.Expression = XParam;
Append->B.Expression = YParam;
完整代码
FString MaterialBaseName = "M_Material";
FString PackageName = "/Game/";
PackageName += MaterialBaseName;
UPackage* Package = CreatePackage(NULL, *PackageName);

// create an unreal material asset
auto MaterialFactory = NewObject<UMaterialFactoryNew>();
UMaterial* UnrealMaterial = (UMaterial*)MaterialFactory->FactoryCreateNew(UMaterial::StaticClass(), Package, *MaterialBaseName, RF_Standalone | RF_Public, NULL, GWarn);
FAssetRegistryModule::AssetCreated(UnrealMaterial);
Package->FullyLoad();
Package->SetDirtyFlag(true);

// Tiling system
UMaterialExpressionMultiply* Multiply = NewObject<UMaterialExpressionMultiply>(UnrealMaterial);
UnrealMaterial->Expressions.Add(Multiply);

// Diffuse
FStringAssetReference DiffuseAssetPath("/Game/T_Texture");
UTexture* DiffuseTexture = Cast<UTexture>(DiffuseAssetPath.TryLoad());
if (DiffuseTexture)
{
	// make texture sampler
	UMaterialExpressionTextureSample* TextureExpression = NewObject<UMaterialExpressionTextureSample>(UnrealMaterial);
	TextureExpression->Texture = DiffuseTexture;
	TextureExpression->SamplerType = SAMPLERTYPE_Color;
	UnrealMaterial->Expressions.Add(TextureExpression);
	UnrealMaterial->BaseColor.Expression = TextureExpression;

	// Tiling
	TextureExpression->Coordinates.Expression = Multiply;
}


// Tiling
UMaterialExpressionAppendVector* Append = NewObject<UMaterialExpressionAppendVector>(UnrealMaterial);
UnrealMaterial->Expressions.Add(Append);
Multiply->B.Expression = Append;
UMaterialExpressionTextureCoordinate* TexCoords = NewObject<UMaterialExpressionTextureCoordinate>(UnrealMaterial);
UnrealMaterial->Expressions.Add(TexCoords);
Multiply->A.Expression = TexCoords;
UMaterialExpressionScalarParameter* XParam = NewObject<UMaterialExpressionScalarParameter>(UnrealMaterial);
UMaterialExpressionScalarParameter* YParam = NewObject<UMaterialExpressionScalarParameter>(UnrealMaterial);
UnrealMaterial->Expressions.Add(XParam);
UnrealMaterial->Expressions.Add(YParam);
XParam->ParameterName = "TextureRepeatX";
XParam->DefaultValue = 1;
YParam->ParameterName = "TextureRepeatY";
YParam->DefaultValue = 1;
Append->A.Expression = XParam;
Append->B.Expression = YParam;

// let the material update itself if necessary
UnrealMaterial->PreEditChange(NULL);
UnrealMaterial->PostEditChange();

// make sure that any static meshes, etc using this material will stop using the FMaterialResource of the original
// material, and will use the new FMaterialResource created when we make a new UMaterial in place
FGlobalComponentReregisterContext RecreateComponents;
结果

UE4 – 以编程方式创建新材质和内部节点_UE4_13