DreamShaderLang
内置节点

通用 UE.Expression

按类名创建任意 UMaterialExpression 的通用反射调用,以及它的参数规则。

已注册内置节点只覆盖了 Unreal 数百个材质表达式中的 27 个。UE.Expression 是其余部分的逃生舱:它按名字创建任意非抽象的 UMaterialExpression 子类,并用命名参数填充它的输入 pin 和 UPROPERTY

float pulse = UE.Expression(Class = "Sine", OutputType = "float1", Input = UE.Time());

还有一种更短的写法。Class 默认取函数名,所以 UE.Sine(…)UE.Expression(Class = "Sine", …) 是同一个实现里的同一个调用:

float pulse = UE.Sine(OutputType = "float1", Input = UE.Time());

Expression 这个名字并不特殊,唯一的特别之处是它永远解析不出一个类 —— 这正是第一种写法必须写 Class= 的原因。

UE.Expression( Class = <class-specifier> , { OutputType | ResultType } = <type-token>
               [, { { Output | OutputName } = <text> | OutputIndex = <int> } ]
               [, <arg-name> = <expression> ] … )

UE.<ClassName>( { OutputType | ResultType } = <type-token> [, <arg-name> = <expression> ] … )
记号含义示例
<x>占位符——替换成实际内容,尖括号本身不写出来。Name = <string>
[ x ]可选——整段可以整体省略。[, Root = <string>]
{ a | b }多选一——从竖线分隔的写法里取其中一个。{ Node( … ) | Comment( … ) }
可重复——前一项可以出现任意多次。<property-declaration> …

所有参数都必须是命名参数。位置参数会失败: Generic {Namespace}.{Function} calls require named arguments. 文本参数可以带引号也可以不带: Class = SineClass = "Sine" 相同,OutputType = float3OutputType = "float3" 也相同。

和已注册内置节点不同,这条路径会校验每一个参数名。既匹配不上 pin 也匹配不上属性的名字是错误, 而不是被静默丢弃。

类解析

Class 必须是字面量 —— 带引号的字符串、裸标识符或带点的名字。说明符会先去掉首尾空白,然后按下列规则解析:

#规则
1去空白后为空 → 解析失败
2文本中含 /. 时,直接按 object path 加载,且只有加载出的类派生自 UMaterialExpression 才接受 —— Class = "/Script/Engine.MaterialExpressionSine"
3否则构建候选名列表,见下
4扫描所有已加载的 UClass;第一个派生自 UMaterialExpression抽象、且名字忽略大小写等于任一候选名的类胜出

对说明符 S 的候选名:

候选名加入条件
S总是
U + SS 不以 U 开头
MaterialExpression + SS 不以 MaterialExpression 开头
UMaterialExpression + SS 不以 UMaterialExpression 开头

前缀判断和最终比较都不区分大小写。

不要写 U 前缀。 扫描比较的是反射类名,而反射名不带 UUMaterialExpressionSine 的反射名是 MaterialExpressionSine,因此任何以 U 开头的说明符 —— USineUMaterialExpressionSine —— 永远匹配不上,上表中两行带 U 前缀的候选名在实践中根本无法命中。

能解析到 UMaterialExpressionSine 的写法是 Sinesine、任意大小写的 MaterialExpressionSine, 以及 object path /Script/Engine.MaterialExpressionSine

扫描只覆盖已加载的类。位于编辑器尚未加载的插件模块中的表达式类找不到,调用会以 UE.{Function} could not resolve MaterialExpression class '{Class}'. 失败。抽象类即使名字完全匹配 也会被跳过。

Substrate.* 路径上,类由内置描述符固定,Class= 会被直接拒绝。

解析顺序

调用按下列顺序处理。这个顺序是可观察的,因为只有第一个失败的步骤会被报出来。

#步骤失败消息
1拒绝位置参数Generic {Namespace}.{Function} calls require named arguments.
2Substrate.* 描述符查找与引擎版本门槛Substrate
3解析 OutputType / ResultTypeUnsupported UE builtin call …… OutputType must be a literal value.… OutputType '{Token}' is not supported.
4解析 Class… Class must be a literal value.UE.Expression requires Class="MaterialExpressionName".… could not resolve MaterialExpression class '{Class}'.
5拒绝 Output/OutputNameOutputIndex 同时出现… cannot use OutputName/Output together with OutputIndex.
6查节点复用缓存
7创建节点UE.{Function} failed to create '{Class}'.
8UMaterialExpressionCustom 初始化… OutputType="Substrate" is not supported by UMaterialExpressionCustom.
9把剩余参数分派到 pin、属性或 Custom 输入… '{Argument}' is not a property on '{Class}'.
10合成 Custom 节点的输出… OutputName must be a non-empty literal value.
11解析输出索引… OutputIndex is out of range …… output '{Name}' was not found …
12在材质上注册 static switch / static component mask 参数
13推导结果类型和分量数{Namespace}.{Function} output is not a Substrate value.
14组装结果并写入复用缓存

完全不带参数的 UE.Expression() 报的是 OutputType 错误,而不是缺少 Class —— 因为步骤 3 在步骤 4 之前。

OutputType

OutputType(别名 ResultType,只在没写 OutputType 时才会被读取)在这条路径上是必需的。 不写会得到:

Unsupported UE builtin call '{Function}' in Graph. For generic MaterialExpression calls, add OutputType="float1/2/3/4/Texture2D/TextureCube/Texture2DArray/VolumeTexture/Substrate".

这条提示里列了九种写法。实际接受的集合有 44 个 token。

对生成器能识别的类来说,OutputType 只是建议值。Substrate、MaterialAttributes 和纹理输出会从 节点真实的输出值类型重新推导,已知宽度表里的类也是如此 —— 这就是为什么 UE.Expression(Class = "WorldPosition", OutputType = "float1") 仍然得到 3 分量的值。

对其他所有类,声明的 token 就是宽度,不管节点真实的输出类型是什么。在 Custom 节点上,这个 token 的权威性更强:它会被写入节点,并决定 HLSL 的返回类型。

接受的 token

全部 token 及其在各个使用面上的可用性。匹配全程不区分大小写。

Token类别分量数通用 UE.*Custom 节点property 声明
float数值1
float1数值1
half数值1
half1数值1
int数值1
uint数值1
bool数值1
float2数值2
half2数值2
vec2数值2
int2数值2
uint2数值2
bool2数值2
ivec2数值2
uvec2数值2
bvec2数值2
float3数值3
half3数值3
vec3数值3
int3数值3
uint3数值3
bool3数值3
ivec3数值3
uvec3数值3
bvec3数值3
float4数值4
half4数值4
vec4数值4
int4数值4
uint4数值4
bool4数值4
ivec4数值4
uvec4数值4
bvec4数值4
MaterialAttributesmaterial attributes0
SubstrateSubstrate0since UE 5.4
StaticBool数值1
StaticBoolParameter数值1
Texture2D纹理对象 —— 2D0✔ †
SamplerState纹理对象 —— 2D0
TextureCube纹理对象 —— cube0✔ †
Texture2DArray纹理对象 —— 2D array0✔ †
Texture3D纹理对象 —— volume0✔ †
VolumeTexture纹理对象 —— volume0✔ †

† 在 property 声明这个使用面上,五个纹理 token 解析成同一种声明类别 —— 0 分量的纹理对象 property, 维度不会被记录。这和 Graph 使用面不同,那里 TextureCubeTexture2DArray 会产生类型不同的值。 维度要紧时请用普通类型 token 声明(TextureCube Tex;),参见类型与值

SamplerState 只在 Graph 使用面上是 Texture2D 的可接受写法。StaticBoolStaticBoolParameter 都解析为 1 分量值;它们存在只是为了让值能与 StaticBool 函数输入做类型检查,除此之外和 float 没有区别。

归一化

token 的归一化并不统一 —— 用了三种不同的比较方式,当 token 带多余空白时差异是可观察的。

token 组比较方式
34 个数值 token 和 MaterialAttributes去首尾空白、转小写,并去掉所有内部空格
Substrate去首尾空白并去掉内部空格,忽略大小写比较
StaticBoolStaticBoolParameterTexture2DSamplerStateTextureCubeTexture2DArrayTexture3DVolumeTexture忽略大小写,与书写原样比较 —— 不去空白,也不去空格

所以在 Graph 使用面上 OutputType = " float4 " 能解析,OutputType = " Texture2D " 不能。 这只有通过带引号的字面量才会碰到 —— 裸 token 里不可能有空白。在 property 声明使用面上值会先去空白再解析, 所以那里不存在这种不对称。下划线和连字符永远不会被去掉:float_4Material-Attributes 都不是可接受的写法。

归一化后的 token 也参与节点复用键。两个只在 OutputType 写法上不同的调用 —— float3vec3 —— 会构成不同的键,因此产生两个节点,尽管得到的值完全一样。

参数分派

有六个参数名是保留的,永远不会分派给节点:

保留名用途
Class类说明符
OutputType声明的输出类型
ResultTypeOutputType 的别名
Output按名字选择输出
OutputNameOutput 的别名
OutputIndex按索引选择输出

其余每个参数按下列顺序对创建出的节点解析,第一个匹配上的胜出。

#判断结果
a归一化后的参数名等于节点某个输入 pin 的归一化名字把表达式连到该 pin
b归一化后的参数名等于该类或任意父类上某个反射 FProperty 的归一化名字属性是表达式输入结构体则走 pin 路径,否则走字面量路径
c节点是 UMaterialExpressionCustom新建一个按书写原样命名的 Custom 输入 pin
dUE.{Function}: '{Argument}' is not a property on '{Class}'.

同名时输入 pin 优先于反射属性。 好几个引擎表达式两者都有 —— 一个叫 Input 的 pin 和一个相关名字的 UPROPERTY —— 而 pin 总是赢。被 pin 名遮蔽的属性没有别的写法可以触达;只能用属性真正的 UPROPERTY 名字,那正是第二遍匹配所比较的东西。

属性匹配分两遍:

遍次匹配说明
1任何名字在去空白转小写后等于参数名的 FProperty限于可编辑属性 —— private 和非 EditAnywhereUPROPERTY 也能触达
2FBoolProperty:属性名转小写后去掉开头的 bFractionalPart 就是这样触达 bFractionalPart

第二遍是转小写再去掉开头的 b,而且会对任意 bool 属性做这件事。所以名为 bTangent 的 bool 属性也能用 Tangent 触达,名为 BaseColor 的 bool 属性也能用 aseColor 触达。只有 bool 属性受影响。 两个属性在这些规则下冲突时,第一遍先跑,因此第一遍胜出。

输入 pin

解析到 pin 的参数 —— 无论是通过 pin 名,还是通过类型为表达式输入结构体(FExpressionInput, 或名为 MaterialAttributesInput 的结构体)的属性 —— 其值会作为 Graph 表达式求值并连接。 值携带的通道掩码会一并传递到连接上。

随后会用 pin 声明的值类型检查这个值:

pin 类型结果
Substrate非 Substrate{Namespace}.{Function} input '{Pin}' expects a Substrate value.
MaterialAttributes非 attributes{Namespace}.{Function} input '{Pin}' expects a MaterialAttributes value.
数值Substrate{Namespace}.{Function} input '{Pin}' does not accept Substrate values.
数值MaterialAttributes{Namespace}.{Function} input '{Pin}' does not accept MaterialAttributes values.
其他任意连接成功

pin 上不做宽度检查:把 float4 连到标量 pin 是引擎要处理的问题,不是 DreamShader 的。

字面量属性

其他匹配上的属性都用字面量写入。对象类型的属性按资产引用读取,其余按字面文本读取。

属性类型接受的值语法失败消息
FBoolPropertytrue / false,不区分大小写'{Value}' is not a valid boolean value for '{Property}'.
FIntProperty十进制整数'{Value}' is not a valid integer value for '{Property}'.
FUInt32Property0 … 4294967295 范围内的整数'{Value}' is not a valid unsigned integer value for '{Property}'.
FFloatProperty任意数值字面量'{Value}' is not a valid numeric value for '{Property}'.
FDoubleProperty任意数值字面量'{Value}' is not a valid numeric value for '{Property}'.
FStrProperty去空白后的文本,原样写入 —— 永远成功
FNameProperty去空白后的文本作为 FName —— 永远成功
FObjectPropertyBasePath(…) 或绝对 Unreal object path对象属性
FEnumProperty枚举值'{Value}' is not a valid enum value for '{Property}'.
由枚举支撑的 FByteProperty枚举值'{Value}' is not a valid enum value for '{Property}'.
无枚举的 FByteProperty0 … 255 范围内的整数'{Value}' is not a valid byte value for '{Property}'.
其他 —— 结构体、数组、set、mapUnreal 自己的导入文本,例如 (R=1,G=0,B=0,A=1)(X=1,Y=2,Z=3)Property '{Property}' on '{Class}' is not a supported literal type yet.

这些消息都会再包一层:UE.{Function} property '{Property}': {Message}

FStrPropertyFNameProperty 永远不会失败。参数携带的任何文本 —— 包括拼错的枚举名或解析不了的 路径 —— 都会被原样存下来。既不报诊断,也不回退到默认值。

枚举值

枚举值会以四种写法与该枚举的每个非隐藏条目比较。比较前双方都转小写,并去掉空格、_-:./ 这些字符。

#参与比较的写法
1条目短名 —— PPI_PostProcessInput0
2条目完全限定名 —— ESceneTextureId::PPI_PostProcessInput0
3条目显示名
4短名去掉第一个 _ 及其之前的全部内容 —— PostProcessInput0

规则 4 正是不带前缀的写法能生效的原因,也是 UE.SceneTexture 所依赖的机制。

对象属性

对象类型的属性接受 Path(<root>, "<asset>")Path("/Game/…") 或裸的绝对 object path。参见 Path 资产引用

情况结果
参数既不是字面量也不是 Path(…) 调用UE.{Function} property '{Property}' must use Path(...) or an Unreal object path.
文本以 Path(/ 开头但解析不了报解析器自己的消息
文本是字面量、不是资产引用、也不以上述两者开头Object property '{Property}' expects Path(...) or an absolute Unreal object path.
资产加载失败,且属性是名为 TextureTextureObjectUTexture 属性属性被设为 null,并算作成功
资产加载失败,其他属性Failed to load asset '{Path}' for '{Property}'.
资产加载成功但类型不对Asset '{Path}' is not compatible with '{Property}'. Expected '{Class}'.

失败置空这条规则是静默的。

UE.Expression(Class = "TextureSample", OutputType = "float4",
              Texture = Path(Game, "Missing/T_Nope"))

会带着一个未赋值的纹理编译通过,而不是报告资产缺失。只有 UTexture 类型属性上的 TextureTextureObject 这两个名字会这样。

选择输出

多输出的节点通过两个互斥的选择器之一读取。两个都写是错误;都不写则选择 output 0。

参数别名类型语义
OutputOutputName文本字面量先按名字解析,再按掩码伪名解析
OutputIndex≥ 0 的整数直接索引节点的输出

按名字解析时会按顺序遍历所有输出:

输出匹配方式
有名字的输出FName 比较名字 —— 不区分大小写
无名字的输出用下面的掩码伪名按顺序与输出的通道掩码比较

说明符会去掉首尾空白;空说明符选择 output 0。

伪名匹配掩码为下列内容的无名输出
RGR 和 G
RGBR、G 和 B
RGBAR、G、B 和 A
R仅 R
G仅 G
B仅 B
A仅 A

这些不是 OutputType 的取值,OutputType 的 token 也永远不是合法的 Output 选择器。 UE.Expression(Class = "BreakMaterialAttributes", OutputType = "float3", Output = "BaseColor") 是按输出名字选择;Output = "RGB" 是按掩码选择。

Custom 节点

UMaterialExpressionCustom 是唯一被专门处理的类 —— 它是把原始 HLSL 塞进 Graph 的方式。

方面行为
OutputType = "Substrate"拒绝 —— UE.{Function} OutputType="Substrate" is not supported by UMaterialExpressionCustom.
其他 OutputType必须能映射到 Custom 输出类型;Texture2DSamplerStateTextureCubeTexture2DArrayTexture3DVolumeTextureStaticBoolStaticBoolParameter 在别处是合法 OutputType,但在这里不是
声明的 OutputType权威 —— 会写入节点,并决定 HLSL 的返回类型
未匹配的参数变成按书写原样命名的新 Custom 输入 pin;Substrate 值会被拒绝:UE.{Function} Custom input '{Name}' does not accept Substrate values.
新建节点应用参数之前会清空它的 InputsAdditionalOutputs 数组
OutputName必须是非空字面量,并意味着请求附加输出索引 1
缺少的附加输出会合成名为 Output1Output2… 的占位输出直到请求的索引,然后重建节点的输出
结果宽度取自节点真实的输出值类型,所以次要输出的尺寸是对的
节点复用禁用 —— 每次 Custom 调用都新建节点
float3 tinted = UE.Expression(Class = "Custom", OutputType = "float3",
                              Code = "return In * 0.5f;",
                              In   = baseColor);

结果类型与分量数

结果按下列顺序从解析出的输出的真实值类型推导。

#条件结果
1OutputType = "Substrate" 但实际输出不是 Substrate{Namespace}.{Function} output is not a Substrate value.
2实际输出是 Substrate0 分量,Substrate 值,权威
3实际输出是 MaterialAttributes0 分量,attributes 值,权威
4实际输出是任意纹理类型纹理对象,权威
5Substrate.* 工具类内置节点宽度取自输出的值类型
6Custom 节点宽度取自输出的值类型
7TextureCoordinatePannerRotator2 分量
8其他查硬编码的已知宽度表,查不到则用声明的 OutputType

已知宽度表:

宽度
2TextureCoordinatePannerScreenPositionRotatorSceneTexelSize
3WorldPositionObjectPositionWSCameraVectorWSVertexNormalWSVertexTangentWSTransformTransformPositionSkyAtmosphereLightDirectionPixelNormalWSCrossProduct
1PixelDepthTwoSidedSignArctangent2FastLengthMaterialXLuminance

另有五个类的宽度由实际连到输入上的值决定:

宽度
Saturate所连 Input 的宽度
StaticSwitchParameter所连 TrueFalse 中较大的一个
If所连 AGreaterThanBAEqualsBALessThanB 中最大的一个
StaticComponentMaskParameter已设置的 DefaultR/DefaultG/DefaultB/DefaultA 个数,最小 1
CurveAtlasRowParameter所选输出掩码的通道数,最小 1

分量数为 0、又不是纹理也不是 Substrate 的结果,就是 MaterialAttributes 值。参见 类型与值

对材质的副作用

有两个类通过这条路径创建时会被注册为材质参数,因而出现在材质实例编辑器里:

注册内容
带参数名的 UMaterialExpressionStaticSwitchParameter材质或材质函数上的「仅编辑器」静态开关值
UMaterialExpressionStaticComponentMaskParameter「仅编辑器」静态通道掩码值,取自节点的四个默认通道

两者的 expression GUID 无效时都会被重新分配一个。

节点复用

这条使用面是去重的。每次调用会构建两个缓存键:

内容
表达式键所有非保留参数,加上解析出的类名和归一化后的 OutputType 文本
输出键表达式键加上输出选择器(OutputName=…OutputIndex=…,或 OutputIndex=0

输出键命中会直接返回上一次的结果。表达式键命中会复用节点、只重新解析输出 —— 这正是一个多输出节点 可以用不同 Output= 选择器服务两次读取的原因。键中的字面文本会做归一化:合并连续空格、统一换行符。 UMaterialExpressionCustom 的子类从不参与复用。

节点被复用时,它的输入和属性参数不会重新应用。这在语言层面观察不到 —— 参数本来就是键的一部分, 被复用的节点必然是用同样的参数构建的 —— 但这确实意味着一个节点会对应源码中的多个调用点。

与之相对,已注册内置节点完全不查缓存,每次调用都新建节点。

SampleTexture2D

float4 texel = SampleTexture2D(BaseTex, uv);

一个保留的双参数形式,在用户 property 和函数之前解析,且区分大小写 —— sampletexture2d(t, uv) 不是它。它会被改写成

UE.Expression(Class = "TextureSample", OutputType = "float4",
              TextureObject = <arg0>, Coordinates = <arg1>)

两个参数都是位置参数,且都必需: SampleTexture2D expects exactly two positional arguments: (textureObject, uv).

如果要采样已声明的纹理参数,优先用参数自己的 pin 调用形式 —— BaseTex(Coordinates = uv) —— 它会复用参数节点,而不是新建一个普通的 TextureSample。参见 Properties 类型

声明形式

通用的 UE.<Name>(…) 也可以作为 property 类型出现在 Properties section 中, 声明形式目录之外的名字都由同一套反射机制创建。 它的规则和 Graph 形式不同:

方面声明形式Graph 形式
OutputType / ResultType必需,取自缩减 token 集必需,取自完整集合
Class默认取内置节点名,可覆盖相同
输入 pin 名匹配不执行 —— 只匹配反射属性优先执行
未匹配的参数Custom 节点上变成 Custom 输入,否则 '{Argument}' is not a property on '{Class}'.相同
输入值先前声明的 property、标量字面量,或 2–4 分量向量字面量任意 Graph 表达式
ParameterName类暴露了该属性且作者没写时,自动取 property 名从不设置
输出选择Output / OutputName / OutputIndex;Custom 占位输出的合成方式相同相同
元数据 [ … ]创建后应用到节点上不适用
节点画布 X-800520
Properties {
    UE.Expression(Class = "ObjectRadius", OutputType = "float1") Radius;
}

这个使用面额外的消息:

消息原因
Unsupported vector literal '{Value}'.输入字面量不是 2–4 个数值分量
Failed to create a scalar constant expression.标量输入字面量的常量节点创建失败
Failed to create a float{N} constant expression.向量输入字面量的常量节点创建失败
'{Value}' is not a valid property reference or literal input.输入值既不是已声明的 property 也不是字面量
'{Class}' does not expose a ParameterName property.在没有该属性的类上写了 ParameterName 元数据
OutputIndex is out of range for '{Class}'.所选索引不存在

通常写在 Graph 里更清晰,因为输入关系在调用点就能看见。

一些值得知道的细节

  • 属性名是扁平的。这里不接受带点的路径和 [index] 选择器,那些只存在于 材质 Settings 中,那是另一个解析器。
  • 没有办法故意让引擎的必需 pin 保持未连接,也没有办法断开一个 pin。参数要么连一个值,要么不写。
  • 因为 Class 默认取函数名,类名写错时报的是 could not resolve MaterialExpression class 而不是「未知内置节点」。UE.Sinee(OutputType="float1")UE.Expression(Class="Sinee", OutputType="float1") 报的是同一条消息。
  • 通用调用可以像任何表达式一样 swizzle: UE.Expression(Class = "VertexColor", OutputType = "float4").rgb
  • 编辑器会把反射出的表达式清单导出到 Saved/DreamShader/Bridge/material-expressions.json,其中列出每个可解析类及其 pin 和属性。 这是查参数名最实用的办法 —— 参见编辑器工具

诊断

{Function} 保留作者书写的名字和大小写;{Namespace} 就是字面上的 UESubstrate

消息触发原因处理
Unsupported UE builtin call '{Function}' in Graph. For generic MaterialExpression calls, add OutputType="float1/2/3/4/Texture2D/TextureCube/Texture2DArray/VolumeTexture/Substrate".既没写 OutputType 也没写 ResultType。补上 OutputType。注意实际接受的集合远大于提示里的九种写法。
UE.Expression requires Class="MaterialExpressionName".函数名字面上就是 Expression,却没有给 Class。
UE.{Function} could not resolve MaterialExpression class '{Class}'.没有已加载的非抽象 UMaterialExpression 子类匹配上任何候选名。去掉 U 前缀 —— 写 Sine,不要写 USine 或 UMaterialExpressionSine。并确认该类所在模块已加载。
UE.{Function} OutputType '{Token}' is not supported.token 不在 44 个之列。
UE.{Function} OutputType must be a literal value.值是表达式而不是字面量。
Generic {Namespace}.{Function} calls require named arguments.通用路径上出现了位置参数。所有参数都写名字。只有已注册内置节点和数学内置接受位置参数。
UE.{Function}: '{Argument}' is not a property on '{Class}'.参数既匹配不上输入 pin 也匹配不上反射属性。在 Saved/DreamShader/Bridge/material-expressions.json 里查 pin 名和属性名。
UE.{Function} input '{Pin}': {Message}连到 pin 上的值求值失败。
UE.{Function} property '{Property}': {Message}字面量无法转换成属性的类型。
UE.{Function} property '{Property}' must use Path(...) or an Unreal object path.对象属性的值既不是字面量也不是 Path(…) 调用。详解
UE.{Function} cannot use OutputName/Output together with OutputIndex.两个输出选择器都写了。
UE.{Function} output '{Name}' was not found on '{Class}'.既没有同名输出,也没有匹配上掩码伪名。
UE.{Function} OutputIndex is out of range for '{Class}'.索引为负、不是整数,或超出节点输出数量。
UE.{Function} created '{Class}', but it has no material outputs.节点没有暴露任何输出。
UE.{Function} failed to create '{Class}'.节点创建返回空。
UE.{Function} OutputType="Substrate" is not supported by UMaterialExpressionCustom.向 Custom 节点索要 Substrate 输出。详解
UE.{Function} OutputType '{Token}' is not a valid Custom node output type.在 Custom 节点上用了纹理或 static bool 类的 OutputType。
{Namespace}.{Function} output is not a Substrate value.在真实输出不是 Substrate 的节点上写了 OutputType="Substrate"。
SampleTexture2D expects exactly two positional arguments: (textureObject, uv).参数个数不对,或者用了命名参数。

完整清单见错误速查

示例

Shader(Name="Docs/M_Generic")
{
    Properties {
        Texture2D BaseTex = Path(Game, "Textures/T_Noise");
        vec3      Dimmed  = vec3(0.2, 0.2, 0.2);
    }

    Settings { ShadingModel = "Unlit"; }

    Outputs {
        vec3 Color;
        Base.EmissiveColor = Color;
    }

    Graph {
        // Class 默认取函数名。
        float pulse = UE.Sine(OutputType = "float1", Input = UE.Time());

        // 显式 Class,以及不带前缀写法的枚举属性。
        float3 scene = UE.Expression(Class = "SceneTexture", OutputType = "float4",
                                     SceneTextureId = "PostProcessInput0").rgb;

        // 通过 Path(...) 写对象属性,并按 pin 名连接输入。
        float4 tex = UE.Expression(Class = "TextureSample", OutputType = "float4",
                                   Texture     = Path(Game, "Textures/T_Noise"),
                                   Coordinates = UE.TexCoord(Index = 0));

        // 宽度由所连输入决定的类。
        float3 sel = UE.Expression(Class = "StaticSwitch", OutputType = "float3",
                                   True = Dimmed, False = tex.rgb, Value = true);

        Color = (scene + sel) * pulse;
    }
}

生成的节点:

Time                                  -> Sine                       (pulse)
SceneTexture (PPI_PostProcessInput0)  -> mask .rgb                  (scene)
TextureCoordinate (Index 0)           -> TextureSample.Coordinates
TextureSample (Texture = T_Noise)                                   (tex)
Constant3Vector (0.2, 0.2, 0.2)       -> StaticSwitch.True
StaticSwitch  (Value = true)                                        (sel)
Add, Multiply                                                       (Color)

接下来

本页目录