如何查看 UE4 材质表达式(Material Expression)

“我正在参加掘金社区游戏创意投稿大赛个人赛,详情请看:游戏创意投稿大赛

材质表达式和材质函数

我一直想知道 Material Expression 的实现。  

这是因为尽管可以通过阅读文档了解如何使用它,但经常不知道它具体的实现。  

材质表达式的例子  

如下所示绿色节点,双击无法看到实现。 Desaturation文档

image.png

然而很容易从编辑器中看到材质函数的实现!

材质函数的例子

如下所示的蓝色节点,双击即可看到其实现

image.png

image.png

材质表达本身有很多种类型。

有关详细信息,请阅读本文档。(注意该为文档是关于 UE5 的)

在哪可以查看材质表达式?

从 HLSL 代码查看

通过在材质编辑器中打开 “窗口->着色器代码->HLSL代码”,可以查看整个材质的 HLSL

image.png

我将其复制并粘贴到适当的编辑器中,搜索 “desaturation”,但问题是它没有命中。

可是它实际上是正确实现的,只不过包含在了 .ush 内。但是这些都是虚幻着色器文件(Unreal Shader Header)的缩写,并且注册了各种有用的函数等。

从 MaterialExpression.cpp 中查看

最好从 MaterialExpression.cpp 去查看材质表达式的实际实现。它在Engine\Source\Runtime\Engine\Private\Materials\MaterialExpression.cpp 中被描述 。 对于 Desaturation 的实现,我们将从UMaterialExpressionDesaturation() 函数中查看。

UMaterialExpressionDesaturation::UMaterialExpressionDesaturation(const FObjectInitializer &ObjectInitializer)
	: Super(ObjectInitializer)
{
	// Structure to hold one-time initialization
	struct FConstructorStatics
	{
		FText NAME_Color;
		FText NAME_Utility;
		FConstructorStatics()
			: NAME_Color(LOCTEXT("Color", "Color")), NAME_Utility(LOCTEXT("Utility", "Utility"))
		{
		}
	};
	static FConstructorStatics ConstructorStatics;

	LuminanceFactors = FLinearColor(0.3f, 0.59f, 0.11f, 0.0f);

#if WITH_EDITORONLY_DATA
	MenuCategories.Add(ConstructorStatics.NAME_Color);
	MenuCategories.Add(ConstructorStatics.NAME_Utility);
#endif
}

#if WITH_EDITOR
int32 UMaterialExpressionDesaturation::Compile(class FMaterialCompiler *Compiler, int32 OutputIndex)
{
	if (!Input.GetTracedInput().Expression)
		return Compiler->Errorf(TEXT("Missing Desaturation input"));

	int32 Color = Compiler->ForceCast(Input.Compile(Compiler), MCT_Float3, MFCF_ExactMatch | MFCF_ReplicateValue),
		  Grey = Compiler->Dot(Color, Compiler->Constant3(LuminanceFactors.R, LuminanceFactors.G, LuminanceFactors.B));

	if (Fraction.GetTracedInput().Expression)
		return Compiler->Lerp(Color, Grey, Fraction.Compile(Compiler));
	else
		return Grey;
}
#endif // WITH_EDITOR
复制代码

乍一看,你可能不确定自己在做什么,但其实现本身非常简单。 只需查看 UMaterialExpressionDesaturation::Compile()中的部分。

int32 UMaterialExpressionDesaturation::Compile(class FMaterialCompiler *Compiler, int32 OutputIndex)
{
	... int32 Color = Compiler->ForceCast(Input.Compile(Compiler), MCT_Float3, MFCF_ExactMatch | MFCF_ReplicateValue),
			  Grey = Compiler->Dot(Color, Compiler->Constant3(LuminanceFactors.R, LuminanceFactors.G, LuminanceFactors.B));

	if (Fraction.GetTracedInput().Expression)
		return Compiler->Lerp(Color, Grey, Fraction.Compile(Compiler));
	else
		return Grey;
}
复制代码

LuminanceFactors 和输入颜色中进行了 Dot() 运算,并且 如果有输入 Fraction,则执行 Lerp,如果没有输入 Fraction,则返回 Dot() 的结果。 LuminanceFactorsUMaterialExpressionDesaturation() 定义,是将颜色转换为单调颜色的合适因子。

LuminanceFactors = FLinearColor(0.3f, 0.59f, 0.11f, 0.0f);
复制代码

你无法跟随 HLSL 代码?

看看你在前面复制的 HLSL 。看起来像生成了近 3000 行代码,但实际上你应该关注的仅仅是 CalcPixelMaterialInputs() 函数。

void CalcPixelMaterialInputs(in out FMaterialPixelParameters Parameters, in out FPixelMaterialInputs PixelMaterialInputs)
{
	// Initial calculations (required for Normal)

	// The Normal is a special case as it might have its own expressions and also be used to calculate other inputs, so perform the assignment here
	PixelMaterialInputs.Normal = MaterialFloat3(0.00000000, 0.00000000, 1.00000000);

	// Note that here MaterialNormal can be in world space or tangent space
	float3 MaterialNormal = GetMaterialNormal(Parameters, PixelMaterialInputs);

#if MATERIAL_TANGENTSPACENORMAL
#if SIMPLE_FORWARD_SHADING
	Parameters.WorldNormal = float3(0, 0, 1);
#endif

#if FEATURE_LEVEL >= FEATURE_LEVEL_SM4
	// Mobile will rely on only the final normalize for performance
	MaterialNormal = normalize(MaterialNormal);
#endif

	// normalizing after the tangent space to world space conversion improves quality with sheared bases (UV layout to WS causes shrearing)
	// use full precision normalize to avoid overflows
	Parameters.WorldNormal = TransformTangentNormalToWorld(Parameters.TangentToWorld, MaterialNormal);

#else // MATERIAL_TANGENTSPACENORMAL

	Parameters.WorldNormal = normalize(MaterialNormal);

#endif // MATERIAL_TANGENTSPACENORMAL

#if MATERIAL_TANGENTSPACENORMAL
	// flip the normal for backfaces being rendered with a two-sided material
	Parameters.WorldNormal *= Parameters.TwoSidedSign;
#endif

	Parameters.ReflectionVector = ReflectionAboutCustomWorldNormal(Parameters, Parameters.WorldNormal, false);

#if !PARTICLE_SPRITE_FACTORY
	Parameters.Particle.MotionBlurFade = 1.0f;
#endif // !PARTICLE_SPRITE_FACTORY

	// Now the rest of the inputs
	MaterialFloat3 Local0 = lerp(MaterialFloat3(0.00000000, 0.00000000, 0.00000000), Material.VectorExpressions[1].rgb, MaterialFloat(Material.ScalarExpressions[0].x));
	MaterialFloat Local1 = MaterialStoreTexCoordScale(Parameters, Parameters.TexCoords[0].xy, 0);
	MaterialFloat4 Local2 = ProcessMaterialLinearColorTextureLookup(Texture2DSampleBias(Material.Texture2D_0, Material.Texture2D_0Sampler, Parameters.TexCoords[0].xy, View.MaterialTextureMipBias));
	MaterialFloat Local3 = MaterialStoreTexSample(Parameters, Local2, 0);
	MaterialFloat Local4 = dot(Local2.rgb, MaterialFloat3(0.30000001, 0.58999997, 0.11000000));
	MaterialFloat3 Local5 = lerp(Local2.rgb, MaterialFloat3(Local4, Local4, Local4), MaterialFloat(0.75000000));

	PixelMaterialInputs.EmissiveColor = Local0;
	PixelMaterialInputs.Opacity = 1.00000000;
	PixelMaterialInputs.OpacityMask = 1.00000000;
	PixelMaterialInputs.BaseColor = Local5;
	PixelMaterialInputs.Metallic = 0.00000000;
	PixelMaterialInputs.Specular = 0.50000000;
	PixelMaterialInputs.Roughness = 0.50000000;
	PixelMaterialInputs.Anisotropy = 0.00000000;
	PixelMaterialInputs.Tangent = MaterialFloat3(1.00000000, 0.00000000, 0.00000000);
	PixelMaterialInputs.Subsurface = 0;
	PixelMaterialInputs.AmbientOcclusion = 1.00000000;
	PixelMaterialInputs.Refraction = 0;
	PixelMaterialInputs.PixelDepthOffset = 0.00000000;
	PixelMaterialInputs.ShadingModel = 1;

#if MATERIAL_USES_ANISOTROPY
	Parameters.WorldTangent = CalculateAnisotropyTangent(Parameters, PixelMaterialInputs);
#else
	Parameters.WorldTangent = 0;
#endif
}
复制代码

又一次,冗长的代码。。。但实际上只有两行代码值得进一步关注。

MaterialFloat Local4 = dot(Local2.rgb, MaterialFloat3(0.30000001,0.58999997,0.11000000));
MaterialFloat3 Local5 = lerp(Local2.rgb,MaterialFloat3(Local4,Local4,Local4),MaterialFloat(0.75000000));
复制代码

如果仔细观察这个过程,没有关于 Desaturation() 的描述,但正在执行的过程与前面在 MaterialPression.cpp 中看到的过程相同。对输入 Fraction 使用 Lerp(),对输入颜色进行 Dot() 运算使用与 LuminanceFactors = FLinearColor(0.3f, 0.59f, 0.11f, 0.0f); 相同的系数。

猜你喜欢

转载自juejin.im/post/7087933561856065550