目标
UE4的材质节点有各种颜色:
这些颜色其实也是一种“信息”,它提示了节点本身的一种“类别”。
本篇的目标是:
- 观察代码中决定材质节点颜色的逻辑
- 总结出各个颜色所代表的类别
找到决定颜色的逻辑在哪
下面我们先观察代码中,决定材质节点颜色的逻辑在哪。
在UE4中,一个图表节点由一个UEdGraphNode
表示,而它对应的界面控件由一个SGraphNode
表示,这在之前的博客【UE4编辑器扩展】学习图表编辑器(UEdGraph):节点外观(SGraphNode) 中也有讨论。
对于材质节点来说:
- 它的
UEdGraphNode
具体子类是UMaterialGraphNode
- 它的界面控件还是
SGraphNode
,没有专门的子类。
对于SGraphNode
来说,它的节点颜色(或者说标题的颜色)将由自身的SGraphNode::GetNodeTitleColor
函数决定,在这其中的逻辑是询问它所对应的UEdGraphNode
调用其UEdGraphNode::GetNodeTitleColor
函数。
UEdGraphNode::GetNodeTitleColor
是一个虚函数。那么答案就很明确了,材质节点的颜色,将由UMaterialGraphNode::GetNodeTitleColor
函数决定。调试也印证了:
决定颜色的逻辑
UMaterialGraphNode::GetNodeTitleColor
的内容如下:
整体来说,其中的逻辑就是不断地做为所有情况做分支判断。
而大多数返回的结果,并不是直接的颜色值,而是UGraphEditorSettings
中预先设置好的值:
具体的分支判断
0. 当前预览节点
右键一个节点可选择开始预览它
如果节点在预览状态,则是深蓝色:

1. 布尔型
具体代码如下:
它针对于【静态的bool】与【bool参数】:
2. 浮点数型
具体代码如下:
它针对于【常数】与【标量参数】:
3. 向量型
具体代码如下:
它针对于【二/三/四 维的常量】以及【向量参数】:
4. 纹理类
具体代码如下:
针对于【纹理采样】和【字体采样】:
5. 输入类
具体代码如下:
大体上表示的意思是“它是节点逻辑的外部输入”,具体三种情况:
(1) “Shader输入型数据”,即材质表达式的bShaderInputData
为真。
bShaderInputData
为真的材质节点有很多,可以尝试在MaterialExpressions.cpp
中进行搜索,可以找到出现54次:
这意味着大约(之所以说“大约”是因为用字符串搜索来判断是不准确的)有54种节点被视为“Shader输入型数据”。
这些“Shader输入型数据”包括【顶点数据】、【UniformBuffer中的数据】、【时间】等等:
(2) 材质函数中的输入
(节点标题以Input开头)
(3) 纹理坐标UV
6. 材质函数调用(UMaterialExpressionMaterialFunctionCall)
7. 材质图层(UMaterialExpressionMaterialAttributeLayers)
(关于“材质图层”的官方文档:材质图层 | Unreal Engine Documentation)
8. 材质函数中的输入(UMaterialExpressionFunctionInput)
由于在 【5. 输入类】 中已经对此做了判断,所以此步的逻辑永远不会走到。
所以材质函数中输入的颜色会是红色而不是此处的蓝色。
9. 材质函数的输出(UMaterialExpressionFunctionOutput)
10. 材质图层的输出(UMaterialExpressionMaterialLayerOutput)
11. 自定义输出(UMaterialExpressionCustomOutput)
UMaterialExpressionCustomOutput
的子类:
12. 参数类(当前针对UMaterialExpressionStaticComponentMaskParameter)
这个分支下,颜色是硬编码的:
对于UMaterial::IsParameter
,它的内容如下:
首先它判断是否是UMaterialExpressionParameter
,它有四个子类:
其中的三个其实已经在 【1. 布尔型】、【2. 浮点数型】、【3. 向量型】 中判断过了,只有UMaterialExpressionTextureSampleParameter
是还没判断的。
而随后的判断:
由于UMaterialExpressionTextureSampleParameter
其实继承自UMaterialExpressionTextureBase
、UMaterialExpressionFontSampleParameter
继承自UMaterialExpressionFontSample
。而他们都在 【4. 纹理类】 中判断过了,所以这里的代码不会走到。
最终,只有UMaterialExpressionStaticComponentMaskParameter
满足:
而随后的HasDuplicateParameters
判断也有点意思,它判断这个节点是否有重复(指类型与参数名都重复),如果有,则会变为更亮的颜色:
13. 动态输入(UMaterialExpressionDynamicParameter)
与前者类似,颜色是硬编码的:
对于UMaterial::IsDynamicParameter
,它判断是否是一个动态输入(UMaterialExpressionDynamicParameter
)
然而,由于UMaterialExpressionDynamicParameter
的bShaderInputData
为真:
所以会早在 【5. 输入类】 中就得到判断,变成红色,而非这里的颜色。
因此这个分支永远也走不到
x. 其余所有类型(pure functions)
剩下的所有材质节点类型,都被视为“pure functions”:
它们有一系列很容易被观察的特点:
- 它们与环境无关。给定一个固定的输入,就输出一个固定的结果。
- 他们足够简单,基本上没法做更细粒度的分割。
代表的节点如 【加】【减】【乘】【除】【正弦】【余弦】【点乘】【叉乘】等等
总结
输入
如果将材质节点网络视为一段逻辑,那么这段逻辑的输入即为“输入节点”。
主要包括:
- 材质函数的输入。
- 从资源中获得的数据,如顶点数据等。
输出
如果将材质节点网络视为一段逻辑,那么这段逻辑的最终目的即为计算出一个结果,表示为“输出节点”。
- 最主要的即为材质的输出,上面有“基础色”、“金属度”等的输出项。
- 也包括材质函数的输出。
- 还有各种其他输出,比如地形材质上的草输出GrassType。
数据类
材质网络中有多种数据,他们颜色各不相同:
布尔:
浮点数:
向量:
纹理:
每种数据都有固定的常量与可变的参数之分。但他们的颜色一样。
还有一种参数是“通道遮罩”(UMaterialExpressionStaticComponentMaskParameter)它的颜色是: