TiXL 网格变形指南:BlendMeshToPoints 运算符——用点缓冲驱动网格顶点混合
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
BlendMeshToPoints 是 TiXL 的 Lib.mesh.modify 模块中一个核心网格变形运算符,它允许用任意的点缓冲(Point Buffer)逐顶点驱动网格变形:先用 MeshVerticesToPoints 把网格顶点导出为点,再对这些点施加任意点修改器(Noise、SDF、模拟等),最后把修改后的点混合回网格顶点,从而获得完全可编程、可动画的网格形变。读完本文,你将掌握 BlendMeshToPoints 的全部输入参数语义、六种混合模式与两种配对模式的工作原理,并能搭建"网格 → 点 → 修改 → 混合回网格"的完整实战链路。
运算符定位与典型应用场景
BlendMeshToPoints 的官方描述(见 BlendMeshToPoints.md 及同目录 README.md)是:
Allows to deform a mesh with a point buffer, for example you can use MeshVerticesToPoints and apply a point modifier.
即:用一个点缓冲去变形一个网格。它属于 Lib.mesh.modify 家族的修改类运算符,与 BlendMeshVertices(在两个网格顶点集之间混合)、DeformMesh(Spherize/Taper/Twist)、DisplaceMesh(纹理置换)等并列,但 BlendMeshToPoints 的独特之处在于:
- 输入不是第二个网格,而是点缓冲——意味着你可以复用整个 TiXL 点管线(
Lib.point.*下的数百个运算符)来生成任意目标形态; - 逐顶点写入位置与朝向——混合结果不仅改变顶点位置,还同步混合切线/副切线/法线(TBN),保证光照与法线贴图在变形后依然正确;
- 支持加权混合与范围渐变——既可用全局 BlendValue 统一混合,也可按顶点 Selected 值或点缓冲的 FX1/FX2 通道做逐顶点权重,还能以滑动窗口(RangeBlend)实现带状过渡。
在 TiXL 中典型的使用方式是把生成的网格(如 SphereMesh、TorusMesh 或 LoadObj 导入的模型)接入 Mesh,把经过修改器处理后的点缓冲接入 Points,驱动"一个形态滑向另一个形态"的实时动画。仓库自带的完整示例 BlendMeshToPointsExample.t3 正是用 AnimValue 驱动整个变形过程的参考工程。
输入参数详解
BlendMeshToPoints 共 7 个输入参数。下表完整继承自官方文档,并补充了仓库中BlendMeshToPoints.t3(默认值)与BlendMeshToPoints.t3ui(UI 约束:Min/Max/Scale/Clamp)给出的实测配置:
| 名称 | 相关性/类型 | 官方描述 | 默认值 | UI 约束(来自 .t3ui) |
|---|---|---|---|---|
| Mesh | MeshBuffers(Required) | 输入 Mesh A | null | 标记为 Required,必接 |
| Points | BufferWithViews | 用于混合的点缓冲 | null | — |
| BlendValue | Single | 混合值 | 0.5 | Min=0.0,Max=1.0,Scale=0.01,ClampMin/ClampMax=true |
| Scatter | Single | 在过渡期间向顶点位置添加随机噪声 | 0.0 | Scale=0.01 |
| BlendMode | Int32 | 混合模式(见下节六种枚举) | 0(Blend) | 分组在 "Options" 组内 |
| RangeWidth | Single | 当 BlendMode 为 RangeBlend / RangeBlendSmooth 时定义范围宽度 | 0.5 | Scale=0.01 |
| Pairing | Int32 | 选择顶点配对混合的方式 | 0(WrapAround) | 分组在 "Options" 组内 |
上述默认值与 UI 约束可直接在 BlendMeshToPoints.t3 与 BlendMeshToPoints.t3ui 中核实;运算符的 C# 定义(含 Slot Guid 与类型)见 BlendMeshToPoints.cs。
输出说明
| 名称 | 类型 | 说明 |
|---|---|---|
| BlendedMesh | T3.Core.DataTypes.MeshBuffers | 混合后的网格缓冲,可直接接入 DrawMesh 等渲染运算符 |
从源码结构看(BlendMeshToPoints.t3 的连接关系),输出经由_AssembleMeshBuffers组装:计算着色器只改写顶点缓冲,法线、切线、UV、Selected、Color 等通道最终由_MeshBufferComponents收集后重组为完整的 MeshBuffers。因此下游可以继续连接 RecomputeNormals 之类的后续修改器,形成链式处理。
BlendMode:六种混合模式及底层数学
BlendMode 是一个映射到BlendModes枚举的 Int32 输入(见 BlendMeshToPoints.cs):
Blend, UseSelectedAsWeight, UseF1AsWeight, UseF2AsWeight, RangeBlend, RangeBlendSmooth其真实语义由计算着色器 BlendMeshToPoints.hlsl 中的分支逻辑实现。设t = i.x / resultCount为当前顶点的归一化进度(0→1),各模式计算出的混合系数f如下:
| 枚举值 | 分支条件 | 混合系数 f | 说明 |
|---|---|---|---|
| Blend | BlendMode < 0.5 | f = BlendValue | 全局统一混合,所有顶点使用同一个系数 |
| UseSelectedAsWeight | BlendMode < 1.5 | f = v.Selected | 以顶点自身的 Selected 属性作为逐顶点权重,可与 SelectVertices 等选区类运算符配合做局部变形 |
| UseF1AsWeight | BlendMode < 2.5 | f = 1 - p.FX1 | 以点缓冲的 FX1 通道反向加权(FX1 越大,越保持原网格形态) |
| UseF2AsWeight | BlendMode < 3.5 | f = p.FX2 | 以点缓冲的 FX2 通道正向加权 |
| RangeBlend | BlendMode < 4.5 | f = 1 - saturate((t - BlendValue) / Width - BlendValue + 1) | 沿顶点顺序滑动的一个硬边混合窗口,BlendValue 决定窗口位置,RangeWidth 决定窗口宽度 |
| RangeBlendSmooth | 其余 | f = 1 - smoothstep(0, 1, saturate((t - b) / Width - b + 1)),其中b = BlendValue % 2,且当b > 1时做 ping-pong 回卷 | 与 RangeBlend 相同但使用 smoothstep 软边,且 BlendValue 按模 2 支持往复回卷,便于做成循环动画 |
着色器里还保留了 Desmos 在线公式链接(desmos.com/calculator/zxs1fy06uh)作为两种 Range 模式的推导参考,说明该窗口曲线是经过公式化设计的。
Scatter:过渡期噪声
无论哪种 BlendMode,最终系数都会叠加 Scatter 噪声:
float fallOffFromCenter = smoothstep(0, 1, 1 - abs(f - 0.5) * 2); f += (hash11(t) - 0.5) * Scatter * fallOffFromCenter;即使用hash11(t)产生确定性随机量,并按"离混合中点越远、噪声越小"的fallOffFromCenter衰减——这样噪声集中在形变过渡带中央,边缘保持干净,视觉上像是"中途打散再聚合"。由于 hash 输入是顶点归一化位置t,同一帧内噪声是稳定的(不随时间抖动)。
Pairing:顶点配对模式
Pairing 映射到PairingModes枚举:
WrapAround, Adjust着色器中的配对逻辑为:
uint vIndex = i.x; uint pIndex = i.x; float t = i.x / (float)resultCount; if (PairingMode > 0.5 && countA != countB) { vIndex = (int)(countA * t); pIndex = (int)(countB * t); }- WrapAround(0):顶点与点按索引一一对应(第 i 个顶点配第 i 个点)。当两者数量不相等时,越界访问由缓冲区维度约束,行为是环绕式截断——因此它最适合"点数量恰好等于顶点数量"的场景,例如直接用 MeshVerticesToPoints 生成的点再原样接回。
- Adjust(1):当顶点数(countA)与点数(countB)不一致时,按归一化比例
t重新采样两边的索引(countA * t/countB * t),实现跨数量级的"拉伸对齐"。这也解释了为何从源码结构看,Pairing 输入会经过IntToFloat转换后才进入着色器参数缓冲(见 BlendMeshToPoints.t3 中 Pairing → IntToFloat 的连接)。
底层原理:计算着色器如何执行混合
BlendMeshToPoints 并非 C# 逐顶点计算,而是完整的 GPU 计算管线。从 BlendMeshToPoints.t3 的子图结构可以看到其内部装配:
- ComputeShader加载
Lib:shaders/3d/mesh/fx/BlendMeshToPoints.hlsl; - ComputeShaderStage以
Dispatch(64, 1, 1)发起调度,线程数由CalcDispatchCount依据顶点数计算得出; - StructuredBufferWithViews(Stride=32)承载点缓冲;VertexStride(64 字节)描述 PbrVertex 顶点步长;GetBufferComponents负责从缓冲区中拆分组件;
- FloatsToBuffer将 BlendValue/Scatter/RangeWidth/Pairing 等参数打包上传(HLSL 侧对应
cbuffer Params:BlendFactor, BlendMode, PairingMode, Width, Scatter); - ExecuteBufferUpdate触发缓冲更新,最终_AssembleMeshBuffers重组输出 BlendedMesh。
着色器核心数据结构(见 point.hlsl):
struct Point { float3 Position; // offset 0 float FX1; // offset 12 float4 Rotation; // offset 16 float4 Color; // offset 32 float3 Scale; // offset 48 float FX2; // offset 60 }; // 总步长 32 字节(与 BlendedMesh 内部 StructuredBufferWithViews 的 Stride=32 一致)值得注意:TiXL 的点与粒子共用同一结构(FX1 在粒子语境下是 Radius、Scale 是 Velocity、FX2 是 BirthTime),因此 BlendMeshToPoints 也能直接接受粒子模拟输出(如 PointSimulation 系列)作为 Points 输入——粒子系统生成的旋转、颜色信息同样会被用于混合。
位置与朝向的同步混合
着色器不是简单lerp位置就结束,而是完整地混合了位姿(Pose):
// 位置:直接线性插值 ResultVertices[i.x].Position = lerp(v.Position, p.Position, f); // 朝向:从网格局部坐标系(Tangent/Bitangent/Normal)构造旋转, // 与点的 Rotation 做球面插值后写回 TBN 三轴 float3x3 meshRotMatrix = float3x3(v.Tangent, v.Bitangent, v.Normal); float4 meshRot = qFromMatrix3Precise(transpose(meshRotMatrix)); float4 blendedRot = qSlerp(meshRot, p.Rotation, f); ResultVertices[i.x].Tangent = blendedMatrix[0]; ResultVertices[i.x].Bitangent = blendedMatrix[1]; ResultVertices[i.x].Normal = blendedMatrix[2];也就是说,BlendMeshToPoints 不只是把顶点"拉"到点的位置,还会把每个顶点的法线/切线坐标系从"网格自身的局部朝向"球面插值到"点的 Rotation"朝向。这保证了变形过程中:
- 法线不会因为顶点位移而变得破绽百出,光照(尤其 PBR 高光)在混合中保持连续;
- 沿网格表面的切线流场随之旋转,法线贴图与程序化纹理的映射在过渡带不会撕裂;
TexCoord、Selected、ColorRGB通道则原样保留自源网格顶点,不受混合影响。
实战工作流:网格 → 点 → 修改 → 混合回网格
结合官方描述与 MeshVerticesToPoints.md 的说明,推荐的标准链路如下:
- 生成源网格:接入 SphereMesh、TorusMesh 或 LoadObj 导入的模型,送入 BlendMeshToPoints 的Mesh(必接输入)。
- 导出顶点为点:用MeshVerticesToPoints(位于 Lib.point.generate,输入 Mesh、W、OffsetByTBN,输出 BufferWithViews)把网格的每个顶点转成点——它的设计初衷就是"在每个顶点处创建点,作为分析网格的辅助方法"。
- 施加点修改器:在点管线上串联任意
Lib.point.modify或Lib.point.sim运算符,例如 AddNoise、NoisePoints、MoveToSDF、PointSimulation、SoftTransformPoints 等,对点做目标形态/动态模拟。 - 接回 BlendMeshToPoints:把处理后的点缓冲送入Points。若点数量与顶点数完全一致,Pairing 保持默认的WrapAround;若两者数量不同,切换为Adjust以获得按比例对齐的配对。
- 控制混合:
- 全局动画:给BlendValue接 AnimValue/曲线,范围 0.0–1.0(UI 已做 Clamp 约束);
- 逐顶点权重:切换BlendMode为 UseSelectedAsWeight / UseF1AsWeight / UseF2AsWeight;
- 带状过渡:切换为 RangeBlend / RangeBlendSmooth,并用RangeWidth(默认 0.5)调节过渡带宽度;
- 添加扰动:调高Scatter在过渡中段制造随机散开效果。
- 渲染与后处理:把BlendedMesh输出接入 DrawMesh,必要时再串接 RecomputeNormals 重新计算平滑组。
仓库自带的可运行参考工程是 BlendMeshToPointsExample.t3(对应 C# 声明 BlendMeshToPointsExample.cs),其中使用 AnimValue(Rate=0.5、Shape=5、Amplitude=4、Offset=-2)、Clamp(Min=-1)与 Transform 等节点组合驱动变形,可以作为搭建自己图表的起点。
同类运算符对照
BlendMeshToPoints 不是 TiXL 中唯一的混合类运算符,理解它们的差异有助于选型:
| 运算符 | 混合对象 | 关键区别 |
|---|---|---|
| BlendMeshToPoints | 网格顶点 ↔ 点缓冲 | 可以借助整个点修改管线做"任意目标形态",并混合位置+TBN 朝向 |
| BlendMeshVertices | 网格 A ↔ 网格 B 的顶点 | 在两个网格之间混合,官方文档指出只有顶点数与拓扑完全一致的网格才有可预测结果;其 BlendModes 枚举为Blend, UseW1AsWeight, UseW2AsWeight, RangeBlend, RangeBlendSmooth(见 BlendMeshVertices.cs) |
| BlendPoints | 点缓冲 A ↔ 点缓冲 B | 纯点层面的混合(输出 BufferWithViews),对应枚举为Mix, UseA_F1, UseB_F1, MixWithSlidingRange, MixWithSlidingRangeSmooth(见 BlendPoints.cs),是 BlendMeshToPoints 概念在点域的同源实现 |
三者共享相同的设计基因:BlendMode 家族、Scatter、RangeWidth、Pairing(WrapAround/Adjust)参数形态一致,掌握 BlendMeshToPoints 后即可触类旁通。
小结
BlendMeshToPoints 的价值在于把网格变形从"两个固定网格之间"扩展为"网格与任意点场之间"的自由混合:借助 MeshVerticesToPoints 完成网格到点的桥接,再串联整个点修改与模拟生态,最后通过六种 BlendMode、两种 Pairing 模式和 Scatter 噪声精确控制混合行为。其 GPU 计算着色器实现(BlendMeshToPoints.hlsl)同时混合位置与 TBN 朝向,保证了实时渲染中的光照与纹理连续性——这是它在实时运动图形工作流中实用性的关键所在。
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考