TiXL 2025-08 维护者聚会纪要:时间轴剪辑、节拍锁定、统一 PBR 渲染、场驱动点选择与输出系统的未来
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
导读
本文基于 TiXL(实时动态图形创作工具)2025-08-25 维护者线上聚会的完整记录整理而成。这场长达 5 小时 15 分的会议,由项目维护者带领逐条回顾了近两周的全部提交:从时间轴(Timeline)与时间剪辑(TimeClip)的线性编辑流程,到自动节拍锁定(Beat-locking)的底层算法,再到统一 PBR 渲染路径与 SDF 场驱动的点选择体系,最后以一场关于多输出/投影映射未来的开放讨论收尾。读完本文,你将掌握 TiXL 中时间剪辑、节拍同步、PBR 材质、场选择、粒子发射等核心功能的实际用法与底层实现原理,并了解输出子系统正在演进的方向。
一、时间轴与时间剪辑:线性编辑的核心工作流
会议首先聚焦时间轴(Timeline)与时间剪辑(TimeClip)的编辑体验,这是近两周提交中占比最高的部分。
1.1 剪辑即“映射”,未接线即“失效”
时间剪辑在 TiXL 中的本质不是一段独立的媒体资产,而是一个时间映射单元:它将一段时间轴区间(TimeRange)映射到一段源时间区间(SourceRange),并附带用于时间轴编辑器显示的其他属性。在 Core/Animation/TimeClip.cs 中,TimeClip被定义为:
public sealed class TimeClip : IOutputData { public TimeRange TimeRange; // 时间轴上的区间(单位:小节 Bars) public TimeRange SourceRange; // 源内容的时间区间 public int LayerIndex { get; set; } = 0; ... }会议中维护者反复强调的一个关键行为是:只有真正接线到活动输出(active output)的剪辑才会被渲染,未接线的剪辑会淡出显示,作为一种“当前没有效果”的警告。这与你见过的多数非线性编辑器(NLE)里剪辑的“启用/禁用”开关不同——TiXL 直接以连接关系作为剪辑是否生效的依据。
1.2 自动堆叠与 Ctrl+X 快速分割
两个显著提升编辑效率的行为:
- 新剪辑自动堆叠(auto-stack):新建时间剪辑时会自动调整位置以避免重叠。这与
TimeClip.IsClipOverlappingOthers的实现一致——TimeClip.cs中提供了对同层(LayerIndex相同)剪辑的区间重叠检测(TimeClip.cs),自动堆叠正是基于同一套重叠判断。 - Ctrl+X 在播放头处分割剪辑:分割后新生成的一半会自动重新接入同一分组(group),相比手工编辑重叠剪辑要快得多。分割在实现上就是将一个
TimeClip的TimeRange拆为两段,配合MapTimelineToSource的仿射映射(TimeClip.cs)保持源区间按比例跟随。
1.3 SourceUnit:Bars 与 Seconds 的双轨时间
TimeClip还有一个值得注意的细节:SourceRange的单位(ClipTimeUnits)分为Bars与Seconds两种(TimeClip.cs)。时间轴上的排布一律以小节(bars)为单位,但源区间可以按媒体类型选择:
- 视频/音频等“挂钟时间”媒体用
Seconds,这样当工程 BPM 变化时,裁切点与关键帧仍然停留在媒体内容上; - 嵌套合成、MIDI 等音乐性内容用
Bars,保持与工程节拍同步。
该单位由拥有剪辑的操作符类型经TimeClipSlot推导,并持久化到文件中;旧文件读取时会置NeedsSourceUnitConversion标志,在操作符类型(即单位)确定后进行一次性转换(TimeClip.cs)。换算公式在SourceToSeconds/SecondsToSource中(TimeClip.cs),以240.0(每分钟 4/4 小节数 × 每小节 4 拍 × 60 秒)为桥梁。
二、节拍锁定(Beat-Locking):从“手动打点”到“自动跟随”
本次聚会在音频同步上花了大量篇幅,核心议题是自动节拍锁定:让软件在播放中持续监听音频,自动把 BPM 与相位修正到检测到的军鼓/底鼓上,VJ 不再需要反复手动打点。
2.1 自动节拍同步的算法骨架
自动同步的实现在 Core/Audio/BeatSynchronizer.cs 中,它的工作方式值得完整理解:
- 初始化需要人工输入:用户先打几次点确定初始 BPM 与下拍时刻,然后按Resync(重新同步)键。
Resync(initialBpm)会把 BPM 钳制到[50, 190]区间,并把相位重置到上一小节开头(BeatSynchronizer.cs)。 - 按频段检测 onset:音频被分为 Bass / Snare / Hihat 三个频段(
FrequencyBand),每个频段用滑动窗口均值 + 阈值倍数判定 onset,并施加最小间隔(MinOnsetIntervalMs = 50)防抖(BeatSynchronizer.cs)。 - 用节奏模板计算相位误差:每个频段对应一组“节奏模板”(
RhythmicTemplate),例如底鼓模板把小节内 0.00/0.25/0.50/0.75 位置视为预期 onset 点,军鼓模板在 0.25/0.75,Hihat 则细分到 1/8 位置(BeatSynchronizer.cs)。每个 onset 与其最近模板的归一化偏差加权求和,得到整体相位误差。 - PID 风格修正:BPM 修正量由
proportionalBpmAdjustment = -0.4控制(注释中记录了作者对不同曲风的调参经验:-0.1会跟丢 Led Zeppelin,-0.3较快但抖动,-0.7太跳);相位则用phaseAdjustmentAmount = 0.01做小幅平滑,避免“泵动”感(BeatSynchronizer.cs)。
会议中维护者的结论是:自动锁定对电子音乐(dubstep、techno、electro 等)表现良好,但像爵士那样大幅变速的曲目容易滑脱——此时按 Resync 键在小节上重新锁定即可。
2.2 音频相关的实用技巧
- 音量可超过 1.0:音频音量允许推到 1100%,用于让播放比音源更响。
- 节拍同步偏移(beat-sync offset):设置一个固定提前量,让播放提前于节拍,用来补偿 LED 控制器或输出延迟较大的设备(例如半秒级延迟)。
- ForwardBeatTaps:把外部打点(如 iPad 上的 OSC 打点、Ableton 点击轨每拍一个脉冲)转发进节拍系统,实现“别人帮你打点”。
- PlaybackFFT 滑动窗口:把播放 FFT 喂进纹理历史,实现频谱随时间滚动,这也是滑动窗口节拍检测(逐拍对比前一个拍子的频谱模式)的基础。
2.3 节拍触发的动画原语
会议重点介绍了两个动画操作符:
- AnimBoolean:比手工接线
AnimValue更整洁的“on-beat 触发”。从 Operators/Lib/Symbols/numbers/anim/animators/AnimBoolean.cs 可以看到,它的核心逻辑就是比较上一帧与当前帧的归一化时间整数部分是否发生变化:
// 该操作符输出在跨过整数小节边界时置 true TriggerOutput.Value = (int)originalTime != (int)_normalizedTime;也就是说,Rate控制推进速度、Phase控制起始相位,跨拍瞬间输出一个脉冲,可直接驱动下游动画;它内部与AnimValue相同,但在图上可读性更好。
- AnimInt:与 AnimBoolean 的计数器内部一致,但没有 on-beat UI。会议上提出的需求是给它加取模回绕(modulo wrap)与速度因子,使其在范围内计数并循环。
三、速度因子(Speed Factors)与现场操控
3.1 用 SetSpeedFactors 统一调速
SetSpeedFactors是会议中演示较多的时间控制操作符:把它接在链首,即可覆盖全局时间因子 A 与 B;将一个 MIDI 旋钮映射到某个因子后,所有下游依赖时间的内容会同步加速或减速,非常适合现场变速 ramp 与 breakdown 段落。
两个独立的速度因子让“一个元素疾驰、另一个元素慢爬”成为可能:把因子 A、B 分别映射到两个 MIDI 输入,即可在同一小节内叠加快慢两层运动。
3.2 键盘与外部节拍介入
- KeyboardInputAsInt:把数字键行映射为整数并喂给时间因子,演出中可以直接用键盘“敲入”速度倍率。
- ui:InfinitySlider 技巧:按住右键拖动数值字段可以只拖动位置、不改数值,方便从锁死的小数值角落脱身。
四、统一 PBR 渲染路径:灯光、网格与 SDF 的同构
会议用较大篇幅展示了“统一 PBR 渲染路径”的进展——即 SDF 渲染分支与网格(mesh)渲染分支共享同一套灯光/环境设置,从而保证同一场景中 SDF 圆环与网格圆环看起来一致。
4.1 灯光操作符的取舍
- PointLight 优于 SetPointLight:
PointLight包装了同样的底层设置,但额外在视口中绘制一个可拖拽的抓取球(gizmo sphere);会议还指出其镜面(specular)项此前从错误的光源位置计算,现已修正。 - SetPointLight是更底层的版本,可直接使用但缺少 gizmo;其位置信息正喂给已修复的 specular 计算。
- SetEnvironment:切换到新的 “black” 环境预设,可以隔离并检视单个灯光的直接贡献,不受环境漫反射干扰。
4.2 场(Field)驱动的着色链路
统一 PBR 路径的核心是把“场”(field)作为颜色/因子的统一来源:
- SdfToColor:把距离场(distance field)转换为颜色,可路由进
DrawMesh、RepeatMeshAtPoints或DrawMeshAtPoints,让所有 PBR 路径从同一个场以相同方式着色。 - DrawMesh 现在接受场输入:把 SDF/平面场经
SdfToColor转换后喂给网格的 color 输入,即可在网格表面绘制程序化颜色贴图——与 SDF 渲染路径保持一致。 - RaymarchField 的注意点:它与网格分支共享统一灯光/环境,因此 SDF 圆环与网格圆环外观一致;但它目前不支持法线贴图(normal mapping),该分支上应关闭法线贴图。
4.3 网格加载与点分布
- LoadObj + DrawMesh + Camera是标准 3D 场景脚手架;会议提醒:给路径参数限制文件过滤器(如仅
.obj)能让资源选择器只显示匹配文件,很多操作符此前缺少过滤器,导致返回噪音或空结果。 - RepeatMeshAtPoints在点处重复网格,但重叠对象会使 overdraw 成本迅速上升,需要降低密度来恢复帧率。
- PointsOnMesh:在加载的网格表面散布着色点精灵(非球体);会议中近景淡出被标记为待修复的 bug。
五、场驱动的点选择系统:从 SDF 到 FX 属性
本次聚会的重头戏之一是用 SDF 场选择粒子点,并把选择结果写成 FX 属性,从而驱动任意逐点行为。
5.1 SelectPointsWithSDF 的输入与模式
在 Operators/Lib/Symbols/point/modify/SelectPointsWithSDF.cs 中,该操作符的输入包括:
Points(BufferWithViews):输入点集;SdfField(ShaderGraphNode):用于选择的 SDF 场,可直接接入CustomSDF;Strength与StrengthFactor(可选 F1/F2 因子):第二重选择可让效果局部化;Mode:Override / Add / Sub / Multiply / Invert,决定选择结果如何写入;Mapping(MappingModes):Centered / FromStart / PingPong / Repeat,决定场的距离如何映射到 0..1 选择区间;Range、Offset、GainAndBias:微调映射曲线(gain-bias);Scatter、ClampNegative、DiscardNonSelected等。
会议详细解释了映射语义:零距离处读取为最大选择值(即场表面处选择最强),结合 gain-bias 可把场的距离曲线重塑;四个映射模式分别适合“中心对称”“从起点推进”“乒乓往复”“循环重复”等不同需求。
5.2 一个完整的用法示例
会议演示的典型链路是:CustomSDF(如SphereSDF、CombineSDF平滑组合多个球)→SelectPointsWithSDF写入 FX 属性 → 该属性驱动粒子发射速度等逐点行为。例如从GridPoints向ParticleSystem发射时,让发射速度随场区域变化。
- MapPointAttributes:把选择属性(F1)映射到颜色,即可用手工构造的场给粒子效果的特定区域着色(例如把一个区域涂蓝)。
- VisualizeFieldDistance:临时接入以直接看到场的距离带,而不是靠猜;
SphereSDF等场可与它并行渲染,精确判断选择落在哪里。 - PointsOnImage:用图片(或
PlayVideo帧)播种点位置,把任意纹理/动态效果变成可供选择与绘制的点场。 - CombineFieldColor 的陷阱:它期望的是颜色/因子槽,而非任意场,接入
FractalNoise场时要有意识地使用。
5.3 随机、累积与精度陷阱
- RandomizePoints:动画化随机相位让点持续翻滚;配合颜色随机化可让饱和度/亮度在整场演出中抖动。新的 uniform 随机模式可以均匀铺开点(此前做不到);注意其 Scale 旋钮现在写入点拉伸,而 W 默认不受缩放影响。
- TriggerAnim 优于手工 AnimValue:把其脉冲喂给随机相位,每拍重新掷点位置。
- Accumulator 与 float 精度陷阱:
Accumulator每拍把一个量持续累加进数值以推进动画相位(单调推进而非振荡时用它)。但无限累加终将丢失浮点精度——会议给出实际经验:点数量超过约 10 万或单帧累加量很大时肉眼可见。干净的做法是让两个RandomizePoints朝一个零相位副本交叉混合(cross-blend),而不是把相位硬生生回拨。
5.4 点相关操作符的持续演进
- PointSimulation 更名提案:作为
ParticleSystem出现之前的操作符,它作为“逐点阻尼器”依然有用,因此提案是改名(如DampPoints)而非退役。 - MoveToSDF:把网格点分布到 SDF 表面(如平滑组合出的球体“monkey”);移动源点会重新分布,再在每个点上绘制网格即可“包覆”形状。其 amount 因子可被
SelectPointsWithSDF写的 FX 属性逐点驱动,surface-offset 模式控制点被拉到表面的远近,步数与最大距离在质量与速度间取舍。 - PointTrail / PointTrailFast:记录历史点位置绘制运动轨迹。
PointTrailFast用环形缓冲所以快,但DrawLines无法判断缓冲回绕处,会在轨迹线上留下一个持久缺口。 - MeshFacesPoints:基于新的彩色点格式重写,支持 FX 缩放/拉伸;新增“按多边形面面积缩放点精灵”的选项(由面的三个顶点平凡计算面积),适合做随面尺寸生长的尖刺或圆柱。
- TransformMeshUVs 破坏性变更:pivot 改为居中,缩放从中心生长(旧行为是角点 pivot),老工程可能位移。
- PlaneSDF 破坏性变更:上/下轴翻转以修复符号 bug,依赖旧朝向的项目需要更新。
六、粒子系统与触发时序
6.1 让发射器“跨多帧”发射
- DelayTriggerChange:把一帧宽的触发信号保持设定时长,使粒子发射器连续多帧发射而非单帧;同时还能把快速的双重触发去抖合并为单次发射。
- ParticleSystem 每拍发射:用节拍触发门控发射;加宽保持触发窗口即可发射一簇帧而非一个瞬间。
- DelayBoolean 的区分:它把布尔值延迟若干帧,而非保持触发——适合错峰(stagger)事件。
- Emit Velocity Factor:新增的发射速度因子缩放发射初速;把发射模式从 sequential 切换到 lines,可让
DrawLines把拖尾渲染为带明暗的丝带。 - TurbulenceForce:加入粒子设置,给直线发射引入可见的涡旋与结构。
6.2 变量共享:SetFloatVar / GetFloatVar
把SetFloatVar放在Execute内部最前,命名变量后即可在大量效果间共享同一个值;悬停时高亮显示所有读取该变量的连线。GetFloatVar按名字读取(带上下文自动补全),让你在整个大型项目中复用同一个驱动值(例如强度)。
6.3 Execute 与 SwapTextures
- Execute 优于 Group:当只需要更新/绘制副作用(设置变量、运行 OSC 输出)且无数据输出时,用
Execute比Group略快,因为它跳过了 Group 的变换/颜色开销。 - SwapTextures:一个“先执行副作用、再透传纹理”的操作符,先运行副作用,然后输出一个已渲染好的缓冲而不重新渲染它。
七、图形编辑器中的工程实践与性能
7.1 连线与布局
- 水平吸附(Alt+A):让操作符水平对齐,连接线路由器就能识别它们相关,保持平行线不重叠——对清理杂乱图很有效。
- DrawConnectionLines 的已知问题:垂直堆叠的操作符会退化为样条连接,无法悬停或断开;这种自上而下的接线场景尚未完成,这也是某些线断不开的原因。
7.2 缓存与回归测试
- 静态子树的缓存:完全静态的子树会缓存且不产生成本;选中操作符会使缓存失效(参数可能变化),因此要让场景稳定就取消选中。
- 图像对比单元测试:渲染复杂场景,输出漂移时标记失败;会议建议每次提交前运行,以捕捉 PBR 各路径的渲染回归。
- ui:EvaluationContext 的缓存行为:效果在无动画时应缓存为静态图像;接入任何动画参数(哪怕只是拖动中的静态渐变)都会打破缓存并强制逐帧重渲染——注意那些无法淡出(缓存指示器)的效果。
- 节点文字缩放模糊的原因:ImGui 用固定字体图集把每个字形绘制为纹理四边形(只有四种字号),未来 2D-SDF 字体着色器可使其锐利。
7.3 渲染质量与几何小贴士
- 32 位浮点 RenderTarget:把输出接进 32-bit(float)渲染目标而非 8-bit,对累积/反馈类效果有肉眼可见的画质提升。
- LoadSvg 的边界:加载 SVG 路径但不闭合环路也不填充,底层 SVG 标准庞大——把它当线段数据处理、动画化其 UV/过渡进度,且应确保导出的 SVG 干净,别指望全特性覆盖。
- SdfToVector 与向量场:
SdfToVector从场法线计算 SDF 表面附近的位置,把距离场转成方向/向量场供力使用;VectorFieldForce沿场法线驱动粒子,配合TransformField的新复选框(旋转场时同时旋转存储的向量,否则方向与空间不一致)。 - FieldVolumeForce:现在是
FieldDistanceForce的超集,新增碰撞反射(reflect-on-collision)与碰撞着色(apply-color-on-collision)选项,后者把 SDF 的颜色采样到碰撞点。
7.4 颜色与渐变
- HDR 徽标:颜色亮度超过 1(Ctrl-拖拽推高)时出现小 HDR 徽标;悬停时的迷你插值曲线展示渐变如何混合,暴露 OKLab 经过零点的异常凹陷。
- BuildGradient:从颜色列表加可覆盖的位置列表程序化构建渐变——把
PlaybackFFT(或任意浮点列表)喂进位置列表,即可得到用于位移与着色的音频响应渐变。 - SampleGradient 插值覆盖:新增插值覆盖,可强制线性 vs 样条混合,把输出插值从渐变类型中解耦。
- ColorsToList:把可复用调色板建成列表喂给
BuildGradient并重排,免去手工逐色接线。
八、输出系统的未来:多输出与投影映射
聚会的最后一段是开放式设计讨论:用项目级输出列表取代单一输出固定(pinning)。设想中的系统包括:
- 一个项目级输出列表:Display / Spout / NDI / 虚拟画布(virtual canvas);
- 实时路由切换(例如 Ctrl+1/2);
- 在 UV 画布上排布输出,用于投影映射;
- 同时保持严格的“输出优先”求值顺序(output-first evaluation order)不变。
8.1 现有输出的现实约束
- SpoutOutput 需要 B8G8R8A8 格式,目前需要前置一个
ConvertFormat;诉求是像PlayVideo那样内部用 compute shader 自动转换。 - SpoutInput 的活用:把帧送出到 NestDrop 等宿主再收回,借宿主的效果库在 TiXL 中重组结果。
8.2 其他值得留意的点
- DetectEdges 配 mirror-repeat 包裹:随手把平淡输出升级成更有趣的画面。
- Sketch 作为快速草图层:直接在输出上徒手涂画、现场讲解创意,成本极低。
- RadialPoints 与 F1/F2:与
LinePoints/GridPoints不同,它缺少逐点颜色;F1/F2 的“因子+偏移”对驱动沿半径的值范围,被证明在很多设置中很有用。 - GridPoints 默认值变更:默认改为 cell 模式(点间固定间距)而非 bounds 模式(固定外框由数量填充);
GridPointsOld为老工程保留旧默认。
九、社区 SDF 与生态备注
- 一个社区制作的 SDF-on-the-fly 方案以 JSON 节点加载 SDF,必须接入
RaymarchField才能显示任何内容;会议将其标记为需要清理与文档化。 - OrbitCamera 的修复:此前中键旋转会悄悄改变相机位置与目标之间的距离,破坏已保存的相机动画;修复后轨道旋转保持该距离不变。
结语
这场 5 小时 15 分的维护者聚会,实际是 TiXL 近两周开发节奏的缩影:时间轴从“可编辑”走向“高效编辑”,节拍系统从“手动打点”走向“自动锁定”,渲染从“多套路径”走向“统一 PBR + 场驱动”,点系统从“静态缓冲区”走向“SDF 选择 → FX 属性 → 逐点驱动”的完整流水线,而输出端正在酝酿从“单一 pin 输出”走向“多输出投影映射”的架构升级。对使用者而言,上述操作符大多位于 Operators/Lib/Symbols 与 Operators/TypeOperators/Symbols 目录下,可结合对应.t3与.t3ui文件继续深入研究;若想复现自动节拍同步,可从 BeatSynchronizer.cs 的EnableBeatSyncProfiling开关入手,观察不同曲风的匹配过程。
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考