宏模板语法规范 (Macros)
宏 (Macro) 是一种通过插值替换具名变量来动态生成具体物理文件路径的模板字符串。在工程项目系统中,宏广泛应用于场景模板 (Situation Templates) 与目录映射 (Directory Definitions) 中。
工程文件路径宏 vs 工作流变量
本文档专门阐述工程项目系统所使用的文件物理路径宏 (File-path Macros)。若需了解在画布工作流内部创建并读取的具名数据流变量——包含在普通文本参数中使用的 {name} 占位替换,请参阅 工作流变量手册。
在深入解析完整语法规则之前,先看一个生动直观的宏计算案例:
模板字符串: {outputs}/{node_name?:_}{file_name_base}{_index?:03}.{file_extension}
场景 A:所有变量均就绪存在:
outputs="outputs", node_name="ImageGen", file_name_base="render", _index=2, file_extension="png"
→ outputs/ImageGen_render002.png
场景 B:省略所有可选变量:
outputs="outputs", file_name_base="render", file_extension="png"
→ outputs/render.png
上例中,{outputs} 是由工程系统自动注入的目录物理路径。{node_name?:_} 属于可选变量——当节点名称已知时,其值后会自动附带 _ 分隔符;当节点名称未知时,整个大括号块及其分隔符会彻底消失不见。{_index?:03} 同样属于可选变量,在存在时会自动以前导零补齐至三位数字。
变量语法详细参考 (Variable syntax reference)
1. 必选变量 (Required variable)
{variable_name}
该变量必须在调用时就绪。如果宏解析时该变量缺失,系统会抛出明确异常并宣告路径解析失败。
2. 可选变量 (Optional variable)
{variable_name?}
问号 ? 将变量标记为可选。如果调用方未提供该变量,则包括其格式化修饰符在内的整个 {} 代码块都会从最终输出中完整剔除,宏的其余部分不受影响继续拼接。
尾随修饰写法:? 亦可写在最后一个格式修饰符的末尾——例如 {shot:upper?} 与 {shot?:upper} 在语义上完全等价。这同样适用于序列帧简写:{###:upper?} 与 {###?:upper} 效果相同。若需将 ? 作为字面量字符保留,请使用单引号包裹({shot:'lower?'})。
3. 后置分隔符修饰 (Separator format)
{variable_name:separator}
在变量取值之后紧随追加 separator 文本。凡是未被系统识别为内置关键字(详见下文的字符串变换)且不是纯数字位宽补齐的文本,均会被解析为后置分隔符。
这在构建“无值时不留多余下划线或斜杠”的优雅前缀时极为实用。例如 {node_name?:_} 会在节点名称存在时输出 node_name_,在节点名称缺失时连同下划线一并隐形:
{node_name?:_}{file_name_base}
node_name="ImageGen", file_name_base="render" → ImageGen_render
未提供 node_name, file_name_base="render" → render
多级子目录同样适用——{sub_dirs?:/} 仅在显式声明了子目录时才会追加正斜杠 /:
{outputs}/{sub_dirs?:/}{file_name_base}.{file_extension}
sub_dirs="lighting/pass_a", file_name_base="render", file_extension="exr"
→ outputs/lighting/pass_a/render.exr
未提供 sub_dirs, file_name_base="render", file_extension="exr"
→ outputs/render.exr
4. 前置修饰符 (Leading separator)
{variable_name:^prefix}
与后置分隔符镜像对称,但指定的前缀文本会被预先置于变量取值的前方。以格式修饰符最前端的 ^ 符号作为起始标记,^ 之后的所有文本均为字面量前缀。仅在变量成功解出取值时生效——未绑定的可选变量会将其前置修饰符一同带走:
{file_name_base}{version?:^_v}.{file_extension}
file_name_base="render", version=3, file_extension="png" → render_v3.png
file_name_base="render", 未提供 version → render.png
配合序列插槽使用构成了最经典的工业级版本后缀实践:
render{###?:^_v}.png
第 1 次落盘 (插槽未触发) → render.png
第 2 次落盘 (碰撞触发插槽) → render_v001.png
第 3 次落盘 → render_v002.png
组合规则:
- 每个变量最多只能携带一个前置修饰符;
- 无论你在大括号中书写的顺序如何,前置修饰符始终在同一变量的所有其他修饰符(如大小写转换、位宽补齐)计算完毕后最后生效。{shot:03:^_v} 与 {shot:^_v:03} 渲染 shot=5 的结果完全一致,均为 _v005。
5. 数字前导零位宽补齐 (Numeric padding)
{variable_name:03}
将输入的整数以前导零补齐至指定的字符位宽。该变量底层必须承载整型数值。
{_index:03} 当 _index = 5 → "005"
{_index:04} 当 _index = 12 → "0012"
常用于配合 create_new 冲突策略实现文件名的自动递增:
- 可选形式 {_index?:03}:首次保存不带序号,重名发生碰撞时自动追加 _001、_002...;
- 必选形式 {_index:03}:从第一次保存开始即强制固定带上前导零序号:_001、_002、_003...
模板: {file_name_base}_v{_index:03}.{file_extension}
第 1 次落盘 → render_v001.png
第 2 次落盘 → render_v002.png
第 3 次落盘 → render_v003.png
6. 工业标准序列帧插槽 ({###})
{#} → 至少 1 位有效数字 (1, 2, ..., 9, 10, 11, ...)
{###} → 至少 3 位位宽补零 (001, 002, ..., 999, 1000, ...)
{####} → 至少 4 位位宽补零 (0001, 0002, ..., 9999, 10000, ...)
{##?} → 至少 2 位可选序号 (首存省略,碰撞时追加 01, 02, ...)
在大括号内部包裹连续的 # 字符,是工程系统用于显式声明版本自增插槽的标准工业语法。每一个 # 代表最小渲染位宽的一位数字。当数值超过当前位宽容量时(例如 3 位容纳到 1000),系统会自然向上溢出扩展至 4 位,绝不发生硬截断。这与 FFmpeg (%03d)、Houdini ($F4)、Nuke (####) 以及 Python 的 :03 行业惯例完全契合。
模板: {file_name_base}_v{###}.{file_extension}
第 1 次落盘 → render_v001.png
第 2 次落盘 → render_v002.png
...
第 999 次落盘 → render_v999.png
第 1000 次落盘 → render_v1000.png (自然溢出扩展为 4 位,无截断)
单个宏模板中严禁包含两个 {###} 插槽:若在一个模板中同时声明两个序列槽(如 {###}_take_{##}.png),系统在编译期会直接拒绝解析,因为系统无法猜测哪一个序号属于自动递增主轴。
7. 未就绪序列插槽的裁决策略 (Unresolved sequence slots)
一个必选的 {###} 插槽在真实写入磁盘之前尚未分配具体数字。若有代码需要在实际落盘前预估解析路径(例如节点在界面上计算预览图落地地址),系统通过 UnresolvedSequenceSlotBehavior 枚举定义了仲裁规则:
| 裁决策略 | 渲染产物 | 适用场景 |
|---|---|---|
FAIL (默认) |
抛出 MISSING_REQUIRED_VARIABLES 异常 |
实际写入路径。底层写入系统以此异常为信号,探测首个可用索引并启动重试自增。 |
RENDER_SEQUENCE_PATTERN |
渲染为裸 # 字符(如 ### 或 ####) |
仅供界面展示。以行业通用记号向用户呈现排版格式。严禁将此产物送入底层 I/O 函数。 |
START_AT_ZERO |
000 |
用于从 0 开始索引的序列预览。 |
START_AT_ONE |
001 |
用于预估“如果是首次保存将落盘为何处”的前端展示。 |
8. 字符串样式变换 (String transformations)
| 格式修饰符 | 变换规则 | 示例输出 |
|---|---|---|
:lower |
全小写 | "my autumn shoot" |
:upper |
全大写 | "MY AUTUMN SHOOT" |
:title |
首字母大写 (Title Case) | "My Autumn Shoot" |
:snake |
蛇形命名 (snake_case) | "my_autumn_shoot" |
:pascal |
大驼峰命名 (PascalCase) | "MyAutumnShoot" |
:camel |
小驼峰命名 (camelCase) | "myAutumnShoot" |
:screaming_snake |
全大写蛇形命名 | "MY_AUTUMN_SHOOT" |
:slug |
URL 净化短语 (空格转中划线,剔除非字母数字) | "my-autumn-shoot" |
:dot |
点号分隔命名 (dot.case) | "my.autumn.shoot" |
:abbrev |
单词首字母缩写提取 | "MAS" |
:trim |
剔除首尾空白字符 | "My Autumn Shoot" |
修饰符如 :snake、:pascal、:camel 等内部具备智能大小写跳跃感知,能够精准将驼峰命名的输入切分拆解为标准的下划线命名。
9. 缺省兜底默认值 (Default value)
{variable_name|default_value}
若变量未提供,直接采用竖线后的 default_value 代替:
{workflow_name|untitled} → 未提供工作流名称时使用 "untitled"
10. 链式级联格式修饰 (Chaining format specs)
多个修饰符通过冒号 : 级联,自左向右依次计算:
{variable_name:_:lower} → 先转为全小写,并在末尾追加下划线
{variable_name:trim:snake} → 先剔除多余首尾空格,再转换为蛇形命名
宏的反向模式匹配 (Reverse matching)
宏系统不仅能够“正向生成路径”,还具备强大的逆向解析能力 (Reverse Matching):给定一条真实的磁盘物理路径与一个宏模板,系统能够反向推演并提取出当初注入其中的各个变量取值!
这一机制被广泛应用于工程资产审计以及另存为新版本 (create_versioned_workflow) 流程中——引擎逆向扫描上一次保存的文件名,从中解算出当前落盘到了第几号版本,从而准确递增下一版本号。
核心公有 API 为 ParsedMacro.extract_variables(path, known_variables, secrets_manager)。
模板: {outputs}/{node_name?:_}{file_name_base}{_index?:03}.{file_extension}
真实路径: outputs/StyleTransfer_portrait003.png
反向解算: outputs="outputs", node_name="StyleTransfer", file_name_base="portrait", _index=3, file_extension="png"
逆向边界锚点与二义性消除
当模板中存在可选变量 (?) 时,系统会在内部穷举并验证每一种状态组合。为了确保你的宏模板具备绝对可靠的反向可推演性:
- 相邻变量间务必添加静态分隔符:避免书写
{name}{version}这种两端均无边界锚点的模态,推荐书写为{name}_{version}或带有前缀锚点的{file_name_base}{###?:^_v}; - 提前传入已知变量:通过
known_variables预先传入确定的上下文变量,可以迅速降维并消除解析二义性; - 反向匹配支持的字符上限:为防止极端病态规则导致穷举超时,系统对未绑定的可选变量组合数量限制为 5 个($2^5 = 32$ 种组合分支)。
宏语法常见错误排查 (Syntax errors)
宏解析器在报错时会明确指出出错字符所在的行内位置编号:
- 大括号未闭合:
{variable_name(缺失右闭合括号}); - 孤立闭合括号:
variable}name; - 大括号嵌套混用:
{outer{inner}}(严禁大括号内嵌套大括号); - 空变量声明:
{}。