news 2026/9/19 3:28:41

TiXL SDF 布尔运算进阶:StairCombineSDF 算子的楼梯与立柱合并技法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TiXL SDF 布尔运算进阶:StairCombineSDF 算子的楼梯与立柱合并技法

TiXL SDF 布尔运算进阶:StairCombineSDF 算子的楼梯与立柱合并技法

【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3

StairCombineSDF 是 TiXL(tooll3)开源实时动态图形工具中Lib.field.combine系列的核心算子之一,专门用于把两个或多个 SDF(符号距离场)的交界处改造成**台阶、立柱、木工槽口(Groove)与榫头(Tongue)**等结构化起伏效果。读完本文,你将掌握该算子的全部 9 种合并方法、KSteps两个关键参数的调参逻辑,并能理解它如何通过IGraphNodeOp接口在 CodeAssembleContext.cs 中动态拼接 HLSL 距离函数——从而在自己的 Shader Graph 场系统中实现"布尔运算 + 工业感细节"的实时造型。

一、算子定位:Lib.field.combine 家族的一员

在 TiXL 的场(Field)系统中,Lib.field.combine目录下的算子负责将多个已生成的 SDF 字段以不同方式合并。根据 combine 目录文档,该家族包含四个成员:

算子文档描述
BlendSDFWithSDF混合两个 SDF
CombineFieldColor合并场颜色
CombineSDF用指定混合方法合并两个及以上连接的场
StairCombineSDF一组可将相交处变成台阶或凸起的 SDF 合并操作

StairCombineSDF 的定位与普通布尔合并(min/max)不同:它不再追求平滑的接缝,而是刻意在相交区域生成离散化、阶梯化的结构细节,非常适合齿轮、机械、电路板、折纸、几何装饰等风格的实时渲染。

算子的官方描述定义在其 UI 元数据文件 StairCombineSDF.t3ui 中:

"A set of sdf combine operations that will turn intersections into stairs or bumps."

其算子实现位于 StairCombineSDF.cs,对应实例配置文件为 StairCombineSDF.t3。

二、输入输出参数全解

原文档给出了该算子的完整接口签名,下表在此基础上补充了源码中确认的默认值与类型细节(默认值来自 StairCombineSDF.t3):

输入参数

名称类型相关度默认值说明
InputFieldsShaderGraphNode(多输入)Required 必需null待合并的场节点,可连接多个;少于 2 个输入时算子直接跳过合并(见下文"代码生成链路")
KSingle0.5合并的半径/影响范围,控制台阶、立柱、槽口的整体尺寸
StepsSingle3.0台阶或立柱的数量 n,决定离散化密度
CombineMethodInt323(即UnionStairs合并方法选择,共 9 种,见第三节枚举表

输出参数

名称类型
ResultT3.Core.DataTypes.ShaderGraphNode

从 StairCombineSDF.cs 可以看到,输出Result是一个Slot<ShaderGraphNode>,构造时即被初始化为该算子自身的ShaderNode

[Output(Guid = "cb491f9b-837f-4e73-9ab8-8ce1e8abb46d")] public readonly Slot<ShaderGraphNode> Result = new(); public StairCombineSDF() { ShaderNode = new ShaderGraphNode(this, InputFields); Result.UpdateAction += Update; Result.Value = ShaderNode; }

InputFields在源码中声明为MultiInputSlot<ShaderGraphNode>(StairCombineSDF.cs),因此可以同时接入任意多个场节点,合并按连接顺序依次进行。

三、9 种合并方法:枚举与 HLSL 实现

CombineMethod输入在 C# 中是一个int槽,运行时通过CombineMethod.GetEnumValue<CombineMethods>(context)映射为私有枚举(StairCombineSDF.cs):

private enum CombineMethods { UnionColumns, // 0 DifferenceColumns, // 1 IntersectionColumns, // 2 UnionStairs, // 3 ← 默认值 IntersectionStairs, // 4 DifferenceStairs, // 5 Groove, // 6 Tongue, // 7 }

枚举按声明顺序映射,因此默认值3对应UnionStairs。下面逐一解析每种方法的 HLSL 实现(全部内嵌在 StairCombineSDF.cs 的AddDefinitions中)。

1. UnionColumns(并集立柱)

在 45 度对角线上放置 n-1 个圆柱形立柱来连接两个物体。代码注释明确指出:

The "Columns" flavour makes n-1 circular columns at a 45 degree angle

float fOpUnionColumns(float a, float b, float r, float n) { if ((a < r) && (b < r)) { float2 p = float2(a, b); float columnradius = r*sqrt(2)/((n-1)*2+sqrt(2)); pR45(p); p.x -= sqrt(2)/2*r; p.x += columnradius*sqrt(2); if (mod(n,2) == 1) { p.y += columnradius; } // At this point, we have turned 45 degrees and moved at a point on the // diagonal that we want to place the columns on. // Now, repeat the domain along this direction and place a circle. pMod1(p.y, columnradius*2); float result = length(p) - columnradius; result = min(result, p.x); result = min(result, a); return min(result, b); } else { return min(a, b); } }

其思路是:仅当两个场距离都小于r(进入相交区)时,把 2D 距离坐标旋转 45 度(pR45),沿对角线方向用pMod1域重复(domain repetition),从而生成一系列等距排列的圆柱;非相交区域退化为普通min(a, b)n越大,立柱越细越多,columnradius随之变小。

2. DifferenceColumns(差集立柱)与 3. IntersectionColumns(交集立柱)

两者共用一个代码块。DifferenceColumns先对a取反(a = -a)再做类似计算,最终返回-min(result, b);而IntersectionColumns直接复用差集实现:

float fOpIntersectionColumns(float a, float b, float r, float n) { return fOpDifferenceColumns(a,-b,r, n); }

这与 SDF 布尔运算的经典等价关系一致:intersection(a,b) = difference(a,-b)。源码注释还指出快速路径会产生"不连续(discontinuity)":

avoid the expensive computation where not needed (produces discontinuity though)

4. UnionStairs(并集台阶)

默认方法,采用 paniq 的"聪明得多"的版本(源码注释原话:"much less stupid version by paniq"):

float fOpUnionStairs(float a, float b, float r, float n) { float s = r/n; float u = b-r; return min(min(a,b), 0.5 * (u + a + abs ((mod (u - a + s, 2 * s)) - s))); }

台阶高度为s = r/n,通过模运算把两个场的差折叠成锯齿波形,从而在r范围内生成 n 级台阶。这是一个极其紧凑的"三角波 + min"公式。

5. IntersectionStairs 与 6. DifferenceStairs

与立柱家族类似,台阶家族也通过取反复用:

float fOpIntersectionStairs(float a, float b, float r, float n) { return -fOpUnionStairs(-a, -b, r, n); } float fOpDifferenceStairs(float a, float b, float r, float n) { return -fOpUnionStairs(-a, b, r, n); }

7. Groove(槽口)

给第一个物体在交界处"切"出一道木工风格的凹槽:

float fOpGroove(float a, float b, float ra, float rb) { return max(a, min(a + ra, rb - abs(b))); }

8. Tongue(榫头)

与 Groove 互补,给第一个物体在交界处"长"出一个凸榫:

float fOpTongue(float a, float b, float ra, float rb) { return min(a, max(a - ra, abs(b) - rb)); }

注意:Groove 与 Tongue 的 HLSL 签名中第二个半径参数名为rb,但算子调用时传入的是KSteps{n}K, {n}Steps),即Steps在槽口/榫头模式下扮演第二个尺寸参数的角色——这与其他方法中"台阶数"的语义不同,调参时需要留意。

方法速查表

CombineMethod 值方法HLSL 函数效果
0UnionColumnsfOpUnionColumns并集,45° 斜向立柱连接
1DifferenceColumnsfOpDifferenceColumns差集,立柱镂空
2IntersectionColumnsfOpIntersectionColumns交集,保留立柱骨架
3(默认)UnionStairsfOpUnionStairs并集,n 级台阶
4IntersectionStairsfOpIntersectionStairs交集台阶
5DifferenceStairsfOpDifferenceStairs差集台阶
6GroovefOpGroove第一物体切出槽口
7TonguefOpTongue第一物体长出榫头

这些算法来源于开源 SDF 库 hg_sdf(Mercury),该算子的 StairCombineSDF.t3ui 的 Links 元数据中明确标注了引用来源,合并相关的辅助函数(pR45pMod1modsgn)定义在 ShaderGraphIncludes.cs 的CommonHgSdf常量中。

四、核心原理:算子如何变成一段 HLSL 距离函数

StairCombineSDF 之所以能"无代码"地在图编辑器中工作,是因为它实现了IGraphNodeOp接口(IGraphNodeOp.cs)。该接口定义了 Shader Graph 节点参与代码生成的四个钩子方法:AddDefinitions(注册全局函数)、TryBuildCustomCodeGetPreShaderCodeGetPostShaderCode

1. AddDefinitions:把 C# 字符串注入 HLSL 全局区

IGraphNodeOp.AddDefinitions在遍历连接场之前被调用一次,用于把纯函数注册进CodeAssembleContext.Globals字典(CodeAssembleContext.cs):

void IGraphNodeOp.AddDefinitions(CodeAssembleContext c) { c.Globals["Common"] = ShaderGraphIncludes.Common; c.Globals["CommonHgSdf"] = ShaderGraphIncludes.CommonHgSdf; switch (_combineMethod) { case CombineMethods.UnionColumns: c.Globals["fOpUnionColumns"] = """..."""; ... } }

StairCombineSDF 固定引入CommonPI/TAU/mod宏定义)与CommonHgSdfpR45pMod1等),随后按当前CombineMethod只注册对应的那个函数——因此切换方法会触发代码重生成

2. 方法切换与脏标记

Update方法中,算子会读取CombineMethod枚举值,一旦与上次不同,就调用ShaderNode.FlagCodeChanged()强制重建着色器(StairCombineSDF.cs)。这是 TiXL 运行时"参数变更 → 代码热更新"机制的体现:ShaderGraphNode.Update()会统计结构、代码、参数三类变更标记(见 ShaderGraphNode.cs),供上层决定是否重新编译 HLSL。

3. GetPostShaderCode:按输入顺序逐层合并

真正写入距离函数主体的是GetPostShaderCode(StairCombineSDF.cs),它在每个输入字段求值后回调:

  • 输入不足InputNodes.Count <= 1时只追加一行注释// skipping combine with single or no input...,直接透传;
  • 第一个输入inputIndex == 0):f{pc} = f{c};保留初始场值;
  • 后续输入:按当前方法调用对应的fOp*函数合并到父上下文:
    c.AppendCall($"f{pc}.w = fOpUnionStairs(f{pc}.w, f{c}.w, {n}K, {n}Steps);"); ... c.AppendCall($"f{pc}.xyz = f{pc}.w < f{c}.w ? f{pc}.xyz : f{c}.xyz;");

这里f.w保存距离值,f.xyz保存颜色,最后一行确保颜色取自距离更小(更靠近表面)的那个场,与min合并的距离语义保持一致。上下文编号取自ContextIdStack(CodeAssembleContext.cs),PushContext会为每个子场生成独立的p{sub}f{sub}局部变量,从而支持任意深度的嵌套连接。

五、实战调参指南

K(默认 0.5)

  • 语义:合并影响半径r,即台阶/立柱生成的"过渡带"宽度;
  • 影响UnionStairs中台阶总高rr越大过渡越宽、台阶越明显;UnionColumns中它同时决定立柱粗细(columnradiusr成正比)和存在范围(仅当a<r && b<r时生成立柱);
  • 建议:先固定Steps,从小到大扫描K,观察交界处细节的扩张速度。

Steps(默认 3.0)

  • 语义:台阶级数 / 立柱个数 n;
  • 影响UnionStairs中每级台阶高r/n,n 越大台阶越密越细腻;UnionColumns中生成 n-1 个立柱,n 越大柱体越多越细;在 Groove/Tongue 模式下它作为第二半径rb使用;
  • 建议Steps取 2~6 通常视觉最清晰,过大时台阶会趋近于普通布尔运算,过小则只剩 1 级或退化为倒角。

CombineMethod(默认 3 = UnionStairs)

  • 想要"齿轮感"立面 →UnionStairs/IntersectionStairs
  • 想要"铆钉/管柱"连接 →UnionColumns
  • 想要"榫卯结构"拼装感 →Groove+Tongue组合使用(一个物体切槽、另一个出榫,可形成互锁效果);
  • 注意切换方法会改变着色器结构,同一参数下不同方法的效果差异极大,建议在参数面板上快速轮播预览。

与其他算子协同

  • 与同目录的 CombineSDF 对比:CombineSDF提供平滑/圆角/倒角/管道/雕刻等 13 种光滑过渡合并,而StairCombineSDF专攻硬朗的离散结构;两者可按需在一条链上串联使用;
  • 输入字段通常来自field目录下的 Shape SDF 算子(Box、Sphere、RoundBox 等),多路输入按顺序累积合并,顺序会影响差集/交集的结果方向;
  • InputFields的 UI 相关度标记为Required(见 StairCombineSDF.t3ui),未连接任何场时节点输出为空。

六、注意事项与已知限制

  1. 仅单输入不合并:连接数 ≤ 1 时,GetPostShaderCode直接跳过,不会报错,但也没有任何合并效果;
  2. 立柱快速路径不连续:源码注释明确提示fOpDifferenceColumns的快速分支会引入距离场不连续,在需要严格 Lipschitz 连续的场合(如后续再做平滑偏移、法线计算)需谨慎使用;
  3. Steps 语义随方法变化:Groove/Tongue 中Steps不再是数量而是尺寸,跨方法复用时需重新审视参数;
  4. 颜色取近原则:合并后颜色取自距离更小的一方(f{pc}.w < f{c}.w ? f{pc}.xyz : f{c}.xyz),若希望两场颜色混合,应改用带GetColorBlendFactorCombineSDF之类的混合算子;
  5. 代码按需注册:只有当前选中的方法会被注入 HLSL,未使用的方法函数不会进入最终着色器,编译负担很小。

七、参考与延伸阅读

  • 算子文档:StairCombineSDF.md、combine 目录 README
  • 算子实现:StairCombineSDF.cs、实例配置 StairCombineSDF.t3、UI 配置 StairCombineSDF.t3ui
  • 框架机制:IGraphNodeOp.cs、CodeAssembleContext.cs、ShaderGraphIncludes.cs、ShaderGraphNode.cs
  • 同类算子:CombineSDF.cs

【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 3:28:02

软件配置管理全解:从CMMI基线到Git/SVN落地实践

简介&#xff1a;软件配置管理全解PPT学习教案是一份面向软件工程学习者、开发团队及质量管理人员的专业教学课件。内容系统讲解配置管理核心概念与能力成熟度模型集成&#xff08;CMMI&#xff09;对应实践&#xff0c;从建立基线、跟踪并控制变更、建立完整性三个目标层层展开…

作者头像 李华
网站建设 2026/9/19 3:25:02

脑语言2500单字:面向脑机接口的中间符号层与语义编码方案

1. 从一次脑机打字延迟说起&#xff1a;自然语言为什么需要“中间符号层”前两年帮一家康复机构调脑机接口拼写器&#xff0c;最让我头疼的不是信号采集&#xff0c;反而是“语言解码”这一环。当时用的是全词汇拼音方案&#xff0c;词库将近十万条&#xff0c;屏幕上的候选词翻…

作者头像 李华
网站建设 2026/9/19 3:24:13

多模态Agent如何避免“自信胡说”?InEx内省与跨模态核验机制拆解

最近半年一直在折腾多模态Agent的落地&#xff0c;踩得最深的一个坑就是&#xff1a;模型看起来什么都会&#xff0c;但真正到关键决策的时候&#xff0c;经常一本正经地胡说八道。尤其在音视频、图文混合输入的场景下&#xff0c;视觉模块说“图里有一只狗”&#xff0c;语言模…

作者头像 李华
网站建设 2026/9/19 3:23:19

UE5运行时FBX导入:FBX SDK与ProceduralMeshComponent实战解析

1. 先交代背景&#xff1a;为什么一个“导入模型”功能要自己写去年在数字孪生项目里被一个需求卡了好几天&#xff1a;要在UE5项目里实现运行时动态导入FBX模型&#xff0c;用户点一个按钮就能把本地FBX放进场景&#xff0c;而不是像传统做法那样在编辑器里导入。当时第一反应…

作者头像 李华