UE4 – 以编程方式创建新材质和内部节点
在这篇文章中,我们将学习如何从 Cpp 代码创建新材料、创建节点和创建链接。 您可以将此代码添加到自定义插件中,以便在用户单击编辑器按钮时神奇地创建新材料。 请记住,此代码旨在在编辑器中执行。您的插件必须是编辑器插件。此代码在运行时无法工作... 创建一个新资产:材料首先,我们必须以编程方式创建一个新资产,例如,让我们在根目录文件夹“/Game/”中创建一个名为“M_Material”的新材质。 为此,我们需要创建一个UPackage(这是我们资产的存储对象)。然后,我们使用一个材料工厂来创建我们的UMaterial。 FString MaterialBaseName = "M_Material";FString PackageName = "/Game/" ackageName += MaterialBaseName;UPackage* Package = CreatePackage(NULL, *PackageName);// Create an unreal material assetauto MaterialFactory = NewObject<UMaterialFactoryNew>();UMaterial* UnrealMaterial = (UMaterial*)MaterialFactory->FactoryCreateNew(UMaterial::StaticClass(), Package, *MaterialBaseName, RF_Standalone | RF_Public, NULL, GWarn);[size=0.8em][url=]复制[/url]
然后,我们必须让 Unreal 做一些关于资产创建的后台处理,并加载/设置我们刚刚创建的包。如果没有这个代码,资产创建就无法完成,并且可能会在以后带来一些问题…… FAssetRegistryModule::AssetCreated(UnrealMaterial) ackage->FullyLoad() ackage->SetDirtyFlag(true);[size=0.8em][url=]复制[/url]
现在,我们的资产已创建但为空。 最后,一旦我们创建了材质并且——也许——添加了节点,让材质自行更新: // Let the material update itself if necessaryUnrealMaterial->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 placeFGlobalComponentReregisterContext RecreateComponents;[size=0.8em][url=]复制[/url]
您可以测试此代码以检查新的空材质创建。
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;[size=0.8em][url=]复制[/url]
我们的第一个节点已创建并分配! 将纹理与材质基色链接起来我们假设您已经将纹理资产放入您的内容文件夹中。 我们需要获取UTexture引用来创建节点。所以我们需要通过路径获取这个资产: FStringAssetReference DiffuseAssetPath("/Game/T_Texture");UTexture* DiffuseTexture = Cast(DiffuseAssetPath.TryLoad());if (DiffuseTexture){...}[size=0.8em][url=]复制[/url]
然后,创建一个新的 TextureSample 材质表达式,为其指定我们的纹理并将其与材质基色链接。 // Make texture samplerUMaterialExpressionTextureSample* TextureExpression = NewObject(UnrealMaterial);TextureExpression->Texture = DiffuseTexture;TextureExpression->SamplerType = SAMPLERTYPE_Color;UnrealMaterial->Expressions.Add(TextureExpression);UnrealMaterial->BaseColor.Expression = TextureExpression;[size=0.8em][url=]复制[/url]
使用乘法节点如果我们想创建一个纹理平铺系统,我们需要将纹理坐标与一些标量参数相乘。 让我们创建一个乘法节点并将其分配给我们的纹理坐标。 // Tiling systemUMaterialExpressionMultiply* Multiply = NewObject<UMaterialExpressionMultiply>(UnrealMaterial);UnrealMaterial->Expressions.Add(Multiply);TextureExpression->Coordinates.Expression = Multiply;[size=0.8em][url=]复制[/url]
将纹理坐标节点分配给乘法节点的 A 参数: UMaterialExpressionTextureCoordinate* TexCoords = NewObject<UMaterialExpressionTextureCoordinate>(UnrealMaterial);UnrealMaterial->Expressions.Add(TexCoords);Multiply->A.Expression = TexCoords;[size=0.8em][url=]复制[/url]
如您所想,您可以使用相同的方式将其他节点分配给 B 参数。
平铺系统现在,通过创建 2 个包含 X 和 Y 纹理重复的标量参数来完成平铺系统: // TilingUMaterialExpressionAppendVector* 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;[size=0.8em][url=]复制[/url]
完整代码FString MaterialBaseName = "M_Material";FString PackageName = "/Game/" ackageName += MaterialBaseName;UPackage* Package = CreatePackage(NULL, *PackageName);// create an unreal material assetauto MaterialFactory = NewObject<UMaterialFactoryNew>();UMaterial* UnrealMaterial = (UMaterial*)MaterialFactory->FactoryCreateNew(UMaterial::StaticClass(), Package, *MaterialBaseName, RF_Standalone | RF_Public, NULL, GWarn);FAssetRegistryModule::AssetCreated(UnrealMaterial) ackage->FullyLoad() ackage->SetDirtyFlag(true);// Tiling systemUMaterialExpressionMultiply* Multiply = NewObject<UMaterialExpressionMultiply>(UnrealMaterial);UnrealMaterial->Expressions.Add(Multiply);// DiffuseFStringAssetReference 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;}// TilingUMaterialExpressionAppendVector* 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 necessaryUnrealMaterial->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 placeFGlobalComponentReregisterContext RecreateComponents;[size=0.8em][url=]复制[/url]
结果
|