本节摘要:散布(Scattering)把"点"撒到表面,再用 Copy to Points 实例化成万物。质量的分水岭在密度控制:用属性图与规则掩码决定"哪里多哪里少",用蓝噪声保证"匀而不齐"。本节搭一个完整的属性驱动散布系统,并对比随机、抖动网格与最近点避让三种分布算法。
接着山体做。目标:石头集中在山腰陡坡,山顶稀疏,山脚几乎不放。整条链四步:
density 点属性(Wrangle,Run Over Points):float slope = degrees(acos(clamp(dot(v@N, {0,1,0}), -1, 1))); float h = relbbox(0, @P).y; // 山腰最密:带通 + 缓冲 f@density = smooth(0.05, 0.25, h) * (1 - smooth(0.6, 0.85, h)); f@density *= fit(slope, 10, 40, 0, 1); f@density *= 0.5 + 0.5 * noise(@P * 1.7); // 簇状变化,避免均匀抹开
density,目标点数给 20000。注意 Scatter 的 Force Total Count 与密度属性相乘决定最终分布——密度是相对权重,不是绝对值;密度解决"哪里多",分布算法解决"怎么摆"。同一密度下三种算法的质感差异显著:
| 算法 | 做法 | 视觉特征 | 代价 |
|---|---|---|---|
| 纯随机 | 每点独立掷骰 | 有聚块和空洞,真实但偶有"扎堆" | 最便宜 |
| 抖动网格 | 规则网格+格内随机偏移 | 均匀中带随机,最常用 | 便宜 |
| 泊松盘/最小间距 | 拒绝过近样本 | 严格不重叠(树、岩石) | 较贵,点数大时明显 |
Houdini 的 Scatter 自带思路二的网格抖动与思路三的最小间距参数;要完全控制时也可以用 Point Relax 节点把已撒的点推开到最小距离——它就是迭代式的最近点排斥,2.1 的邻域代码手写版即其原理。
💡 关键直觉:自然界的分布几乎都是簇状的(种子落在哪里,哪里就长出一片)。纯均匀反而假,纯随机偶尔扎堆。密度场乘一层噪声(实验第 3 行)是性价比最高的"簇感"来源。
真实散布还要处理"打架":大树不许长在路上,营火周围要有空地。两层手段:
density *= 1 - roadmask; 一行解决;// 第二轮散布前的排斥:离大树越近密度越低 int near[] = nearpoints(1, @P, 2.0); // 输入1=大树点云 if (len(near) > 0) f@density *= 0.05;
2 万个点直接 Copy 真模型会很重。两条正路:Copy 到低模代理,渲染时换回高模;或直接输出实例化属性(取向量乘 pscale 打包成 instance 矩阵),交给渲染器/引擎实例化——第 7 章与引擎对接时这是标准出口。
⚠️ 常见坑:Scatter 在三角面上撒点时,长条三角形会比正三角形"吸"更多点(按面积加权对随机是公平的,但视觉上长条区域密度失真)。先 Remesh 匀化网格再散布,分布才可信。
散布系统里反复出现的 noise 到底是什么?下一节把它讲透。
高级散布的关键约束是"既随机又不重叠",泊松盘是标准答案。Scatter SOP 内置(Relax 选项即是),但手工实现一遍能让你获得修改它的能力,核心是拒绝采样加松弛:
// Attribute Wrangle (Run Over: Detail) —— 迭代式松弛散点 int pts[] = expandpointgroup(0, ""); for (int iter = 0; iter < chi("iters"); iter++) { foreach (int p; pts) { vector pos = point(0, "P", p); int nbs[] = nearpoints(0, pos, ch("min_dist") * 2); vector push = {0,0,0}; foreach (int nb; nbs) { if (nb == p) continue; vector d = pos - point(0, "P", nb); float len = length(d); if (len < ch("min_dist") && len > 1e-5) push += normalize(d) * (ch("min_dist") - len); } setpointattrib(0, "P", p, pos + push * 0.5, "set"); } }
验证标准:最小点间距直方图(measure 出 min distance 属性后画 histogram)应紧贴 min_dist 而无长尾。同样的松弛框架换个"力"就是曲面上的颗粒堆积、苔藓群聚——散布算法的通用骨架比具体节点更值钱。
密度场合成清单 坡度图(slope) + 海拔带(fit@P.y) + 噪声扰动(偏差) + 艺术遮罩(画笔mask) => 乘法合成: 只有多因子相乘才能制造"同时满足"的 AND 语义 => 验证: Scatter 的密度可视化 + 数量统计随参数单调 陷阱: 各因子未归一化到0-1就相乘, 密度会坍缩为0