PCG Projection节点详解:让程序化生成物体完美贴合地形表面 做了这么多年 PCG程序化内容生成我见过太多朋友卡在同一个地方撒点没问题密度控制也没问题结果岩石一棵棵悬在半空树木笔直插在山体斜坡里路灯孤零零浮在路面上方。手动一个个去挪位置、转角度几百个物件调下来直接怀疑人生。PCG 里的Projection投射节点就是用来根治这类问题的——它能把散落在空间中的点整体“按”到目标表面上让物体和地形、墙面、路面贴合得严丝合缝。这篇文章我会从 Projection 的核心逻辑讲起拆开它的每个关键参数然后带你把一套完整的“撒点—投射—生成”链路搭建起来再聊几个只有实际跑过才会遇到的坑。无论你是刚接触 PCG 的新手还是已经在项目里用了很久但总觉得贴合效果不自然的老手这篇都值得看完。1. Projection 到底解决了什么从手动摆放到自动化贴合在 PCG 框架出现之前做地表植被、岩石散布这种活儿最常见的路子就是手动摆放加少量程序化散布。手动摆放精度高但效率极低而且一旦地形调整过所有物件都得重新摆一遍。后来有了地形碰撞捕捉这类功能可以把物体吸附到表面可它只解决单物件的问题遇到几十上百个物件组批处理就无能为力了。PCG 的思路完全不同。它把所有生成物都抽象成“点集”Points每个点带着位置、旋转、缩放、颜色、属性等信息在图表里流动。散布一棵树、一块石头、一盏路灯本质都是生成一堆点再把点转换成网格体实例Mesh Instance。这套流程的优势在于可重复、可参数化但同时也带出一个新问题点集在空间里是虚拟的它不天然知道哪里有表面、表面是什么朝向。比如你用 Surface Sampler 在地形上生成了一批点这些点本身是贴着地面的。可一旦你对点做了旋转偏移、随机位移或者把点从一个局部空间变换到世界空间这些点很可能就悬空了或者陷进山体里去了。这个时候Projection 节点就该上场了。它做的事情说大白话就是从每个点的位置出发沿指定方向发出一条投射线找到它与目标几何体的交点然后把点移动到交点上并根据交点的法线重新调整点的朝向。你可以理解成把一整盘豆子倒在一块起伏的布上然后再用手掌轻轻一拍每颗豆子都老老实实贴在布面上。这个机制的应用面非常广。最常见的场景是植被和岩石贴合地形稍加变化还能让贴花、碎石贴合墙壁让地面道具贴合路面甚至在关卡搭建阶段批量把道具精准放在复杂几何体表面。可以说只要你想让程序生成的点“长”在某些几何体上Projection 就是这个需求的基本盘。2. Projection 节点核心参数拆解每一项都配得上你的注意力Projection 节点在 PCG 图表里属于“点处理”类位置在节点面板搜Projection就能找到。它的输入输出很清晰**Source源点集**是你要做贴合处理的点**Target目标数据**是投射目标的几何体数据。你既可以把地形 Actor 拖进来当 Target也可以用 PCG 数据、静态网格体作为投射目标。下面我把节点的核心参数一个个拆开讲。2.1 Source 与 Target 的数据来源先说 Source。和大多数 PCG 节点一样Projection 会自动获取上游传入的点集作为 Source所以这个端口一般不需要手动连接除非你要在图表中间特意切换数据源。真正需要注意的是 Target。Target 支持的数据类型包括 Landscape 地形、静态网格体、Spline 样条线、其他 PCG 数据等等。我实际项目中最常用的两种接法如果场景里有单独的地形 Actor直接把地形拖到 Target 引脚上或者在地形上放一个 PCG Volume / PCG Data 组件把数据引过来。如果投射目标是你在 PCG 图表内生成的网格体数据比如程序生成的道路面片、岩石堆那就把上游数据连到 Target。这里有个非常容易踩的坑Target 如果接了地形但地形上没有有效的 PCG 数据接口节点会直接报错或者输出空数据。我建议在 Target 端优先使用 PCG Data 组件这样地形即使后续有调整只要刷新 PCG 就能同步更新不用每次重新指定。2.2 Projection Direction让点往哪个方向找表面Projection Direction投射方向是 Projection 最核心的参数之一它决定了点沿着哪个向量去寻找目标表面。默认值是(0, 0, -1)也就是竖直向下。对绝大多数地面植被场景来说这个默认值就够了。但如果你把植被撒在垂直的悬崖面上或者让碎石贴合一个坡度为 60 度的山坡固定向下投射就会出问题点会找到悬崖下方很远的平面而不是悬崖本身。这时候你就需要改成沿着自定义向量投射比如(0, 0, -1)改成(-1, 0, 0)让它向左投射或者直接传入一个旋转向量。PCG 的 Projection 节点还提供了一个特别实用的细节如果点集本身带有旋转信息你可以勾选相关选项让投射方向跟随每个点自身的朝向。这个功能在处理“沿着墙壁散布管道”“沿路径生成护栏”这类需求时尤其好用——点的朝向就是路径方向投射方向跟着走贴合效果会自然很多。不过要注意设置投射方向时要保证方向向量是归一化的否则投射距离和命中逻辑可能不符合预期。2.3 Projection Offset不想让物体陷进表面就得留偏移投射完成之后点会被精确移动到目标表面的交点处。可你真的把一棵树在交点处生成树根部分会直接插进地面里——因为树模型的锚点Pivot在模型底部插在表面上看起来就是陷入地面的效果。Projection Offset投射偏移就是用来解决这类问题的。它可以在投射结果的基础上再沿投射方向偏移一段距离。简单说偏移值是正数往外推是负数往里收。实际项目里这个值怎么配完全看模型锚点位置。以植被为例如果模型锚点在底部偏移值设 0 或略大于 0比如 10~30 个 Unreal 单位树根刚好贴在表面上。如果模型锚点在中心很多岩石模型是这样你要把偏移值设为模型高度的一半左右才能让岩石“坐”在表面上而不是半截虚悬。路标、路灯这类道具锚点通常也在底部偏移值按实际需要微调即可。这里我的经验是不要只调一个偏移值就以为万事大吉多建几个测试点在视角里旋转观察模型与地面的贴合缝。偏移补得太大物体会明显浮空补得太小根部又会穿模。“刚好贴住”和“微微陷入 5 个单位的视觉误差”之间往往就差一个很小的偏移增量建议用 5 或 10 为单位慢慢微调。2.4 Align to Surface让物体顺着表面法线站起来每个被投射到的交点都带有一个法线向量它垂直于目标表面。Align to Surface对齐到表面这个选项打开后PCG 会把点的旋转与交点法线对齐让物体的“向上方向”变成法线方向。这个开关的效果非常直观在一个起伏不平的山坡上关闭它所有树木都是竖直的打开它树木会按照山坡的倾斜角度自动“歪”过去看起来像是从坡上自然长出来的。对岩石、植被来说这个选项几乎是必开的对路面标志牌、路灯这种需要保持竖直的物件反而要关掉否则在斜坡上会出现东倒西歪的诡异效果。不过这里有一个前置条件需要注意Align to Surface 只会影响“对齐”的动作但不会保证物体的正面朝向也是合理的。换句话说投射后所有物体的 X 轴朝向可能一致看起来会出现“整齐划一”的奇怪阵列感。如果你要的是更自然的随机朝向可以再接一个 Transform Points 节点在 Z 轴上加随机旋转偏移。这个配合我个人认为是最实用的组合后面会专门讲。2.5 关于投射精度的相关设置Projection 节点还有一个常被忽略但很重要的设置最大投射距离Max Distance。它限制投射线的有效长度。默认值比较保守如果地形起伏很大、或者投射目标远离源点你可能需要把它调大否则点不会命中目标就像手电筒光照范围不够一样。我一般在山谷、悬崖比较多变的地形里会把最大投射距离调到 5000 以上如果是平缓地形2000 就足够了。但要注意最大投射距离调得越大性能开销也越大。因为投射检测的范围变大了PCG 需要计算更多的交检测。真正追求性能时我会尽量把投射距离控制在刚好覆盖地形最大高差的范围内。2.6 调试把投射过程可视化在 PCG 图表编辑器里有一个调试功能很值得养成习惯选中 Projection 节点在调试视图启用“显示点”Show Points和“显示方向”Show Directions。这样你能直接在视口里看到每个点的投射方向和命中结果。密密麻麻的箭头如果方向整齐、长短均匀说明投射行为正常如果出现大量飞出去的点那多半是最大距离不够或者 Target 数据没有正确接入。这个调试手段可以说是我排查 PCG 贴合问题时的第一步。所有参数问题的最终答案几乎都能在视口里一眼看出来。3. 完整流程搭建从撒点到投射贴合手把手做一套山体植被聊完参数我们直接上完整链路。这一节我会带你把 PCG 图从零搭建起来最终实现一个效果在一个起伏的山体地形上生成一批自然分布的树木和岩石所有物体都牢牢贴合地形表面、朝向自然、没有穿模。3.1 第一步准备地形和 PCG Volume在场景里准备一块 Landscape 地形形状不要太规整带点起伏最好。然后把一个PCG VolumePCG 体积放置在地形上方调整体积范围让它覆盖你要生成植被的区域。选中 PCG Volume在细节面板里创建一个 PCG 图表或者新建一个 PCG 资产双击进入图表编辑器。PCG Volume 的作用是提供一个空间范围PCG 图表会在这个范围内执行所有生成逻辑。没有它很多采样类节点会因为没有边界而无法判断生成范围。这一步不复杂但非常重要相当于给整个生成流程划定了“画布”。3.2 第二步用 Surface Sampler 撒点在图表里添加一个Surface Sampler表面采样器节点。这个节点的作用是在地形表面随机生成一批点默认会把点贴在地形表面。它的核心参数有两个Points Per Square Meter每平方米点数控制密度。一般植被场景里每平方米 0.01~0.05 就很密集了数值太大会导致性能爆炸。Sampling Seed采样种子控制随机分布模式可以理解成随机生成器的一个手柄改一下就能换一批布局。如果你要让树木和岩石出现在不同的区域可以用 Density Filter密度过滤器或者直接复制一条采样链给不同的物体类型。但这里为了讲清楚 Projection 的用法先用一个采样没问题。3.3 第三步Transform Points 做随机缩放与旋转采样出来的点位置是贴在地形上的但旋转和缩放都是默认值直接生成模型会非常呆板。所以我们加一个Transform Points变换点节点在它里面设置Scale缩放开启随机缩放Min 0.7、Max 1.5 左右。Rotation旋转在 Z 轴旋转上设置随机范围 0~360 度让每棵树的朝向各不相同。注意大部分情况下你不应该在 Transform Points 里随意偏移点的位置因为位置偏移会让点脱离地形表面。这就是 Projection 接下来要解决的问题。3.4 第四步Projection 把点按回地面加入Projection节点把 Surface Sampler或经过 Transform 后的点作为 Source把地形数据作为 Target接法前面讲过优先用地形上的 PCG Data 或直接把地形拖到 Target。参数我先给一组通用配置参数推荐值说明Projection Direction(0, 0, -1)向下投射适用于大多数地表场景Projection Offset0~30按模型微调补偿模型锚点避免穿模Align to Surface开启让植被顺地形法线倾斜Max Distance2000~5000根据地形高差调整这一步跑通之后你在视口里就能看到点已经不悬浮了而是整整齐齐贴在地形表面。此时如果是沿陡坡分布的点朝向也已经跟着坡面法线自然倾倒了。3.5 第五步Mesh Instance 生成网格体最后加一个Static Mesh Spawner静态网格体生成器或者新版里的Mesh Instance网格体实例节点。在它的设置里指定你要生成的静态网格体资产比如一棵树、一块岩石。此时 PCG 图表会自动在 PG Volume 范围内生成所有的实例。我的习惯是在生成前再接一个Mesh Randomizer网格体随机器节点准备一组不同形状的岩石或几棵树模型让它随机挑选这样生成出来的效果才不会像复制粘贴一样千篇一律。到这里一套最基本的“撒点—定向—投射—生成”流程就跑通了。如果你只是想解决“物体悬浮在地形上方”的问题这套链路已经足够用。3.6 为什么顺序是“先变换再投射”而不是“先投射再变换”这里有个很关键的顺序问题值得单独拿出来讲。我看过很多新人把 Transform Points 放在 Projection 后面发现生成的物体不仅没有贴合反而有些埋进地里、有些悬空原因就在变换点的位移把投射后的贴合结果又“打飞”了。正确的顺序一定是先做缩放旋转Transform再用 Projection 做位置贴合。原因很简单Projection 看到的是点的当前位置和当前朝向如果你的点在投射之后又发生了位移那投射结果就没有意义了。而同时如果你先随机旋转再投射投射后的点法线对齐是在随机旋转基础上进行的最终朝向依然合理。所以这个顺序要形成肌肉记忆Transform → Projection → 生成。4. 看似简单的投射细看全是坑贴合效果不自然的排查方向Projection 节点本身功能不算复杂但放到真实项目里总会出现一些让人挠头的情况。这一节我把这几年实打实踩过的坑做个汇总每个都给到排查思路和解决建议。4.1 坑一悬崖峭壁上植被方向彻底失控我最早在地势起伏很大的山体上撒植被时打开 Align to Surface 之后发现一个诡异现象部分悬崖面上的植物法线方向是对的但有些点朝向完全反了像是把植物种在了崖壁背面。排查后发现原因出在 Projection 的投射方式。固定向下投射时对于接近垂直的悬崖面投射线与表面的夹角非常小交点很难稳定命中有可能命中悬崖顶部平台的边缘、悬崖下方的地面或者奇奇怪怪的夹缝。法线自然也就跟着变了。解决办法有几个思路把投射方向改成点的局部朝向让它沿着表面外法线方向去找目标Projection 节点内可以设置使用点云的旋转。对悬崖区域单独处理用 Density Filter 过滤掉坡度太陡的点或者用不同的投射参数生成第二套植被。刻意避免在过陡区域生成植被借助 Slope Filter坡度过滤器限制生成坡度范围这个方法在实际项目里几乎必然用到。友情提醒地面上生成植被没问题不代表悬崖上也没问题。坡度超过 60 度再强开 Align to Surface 对齐大概率就是一场方向大乱斗。4.2 坑二投射穿过薄表面点飞到了山体另一侧还是地形场景我的投射距离设得很大比如 20000。结果发现有些点投射后落在了山的背部你好不容易撒的石头全跑到了视线看不到的地方。这个问题的本质是向下投射的射线只找一个交点而它不区分这个交点是从上往下命中还是穿了整整一座山之后從另一面命中。在薄壁、拱桥、悬挑结构非常多的场景里尤其明显。解决思路有两个方向。第一个是调小 Max Distance让投射只在很近的范围内生效减少穿透命中的概率。第二个更稳的办法是换用Ray Cast射线检测节点替代单点投射。Ray Cast 可以同时控制投射方向、检测距离还能基于命中点的法线做偏转功能比 Projection 更底层也更容易做边界控制。在复杂场景里我经常先用 Ray Cast 确保命中结果合理再用 Projection 做最终贴合。4.3 坑三洞穴、桥洞、隧道这些表面朝下的地方怎么处理向下投射在处理地面时很好用可一旦目标表面朝下比如洞穴顶部、桥洞下方向下投射就彻底找不到北了。你需要的是向上投射也就是把投射方向改成(0, 0, 1)。如果你既要上下投又要左右投可以给不同区域的点用不同的投射方向。做法通常是给点加属性标记比如用Attribute写一个标记点是在地面上还是天蓬下然后根据标记用不同的投射方向。这个办法听起来麻烦但在做一些多层空间、半开放场景时会特别省心。这里额外分享一个冷门技巧投射方向不一定非得是轴对齐方向。你可以传入一个旋转向量比如让投射方向跟随山体的坡向或者跟随样条曲线的切线方向。配合 PCG 里很常见的 Spline 数据可以让管道、扶手这类线性物体自然贴合路径表面。4.4 坑四投射后点靠得太近生成的模型互相穿插这个坑和 Projection 本身关系不大但会在 Projection 之后集中爆发。原因在于投射前点分布是随机撒的互相之间可以靠得很近。经过投射贴合之后点被移动到表面有些点在表面上的投影位置挨得非常近。如果是贴图类的植被十字交叉面片还好但换成大体积岩石或树木就会出现严重穿插。对策有几个在投射前接Spatial Noise或密度调节节点让点分布更均匀。在投射后接Reject或Density Filter把距离过近的点剔除。在投射后加一个Collision Constraint碰撞约束节点让点互相推开。这个节点在 PCG 生成井盖、路灯、石头这类需要间距的物件时非常好用。实际经验是为了让视觉效果好撒点密度宁少勿多穿插问题从源头控制比后期修复省力得多。4.5 坑五地形修改后 PCG 不更新贴合错位你生成完植被过几天美术调整了地形高度结果 PCG 生成的内容还停留在旧的高度信息上植被再次悬空或陷入地下。这个问题几乎人人都会遇到。原因大多是 Target 数据缓存没有刷新或者地形数据用的还是旧版本。解决方式在 PCG Volume 的细节面板里点一次Generate / Cleanup按钮强制刷新。在项目设置里打开 PCG 的自动刷新选项但要注意大场景下自动刷新可能引发性能问题建议还是手动刷新。确认 Target 数据路径没有被破坏比如地形 Actor 被重命名或替换过导致引用失效。这类“贴合错位”往往不是 Projection 本身的问题而是数据源更新不同步。建议团队里定个规矩改地形后统一刷新 PCG而不是让每个策划、美术各撞各的错。5. 再往前走一步Projection 与密度、旋转、属性控制的配合玩法Projection 节点单独用已经能解决很多问题但真正让它发挥全部价值的是和其他 PCG 节点打配合。这一节我会分享几个高频使用的组合玩法全部来自实际项目经验可以直接抄作业。5.1 配合 Transform Points让“自然随机”和“表面贴合”兼得前面章节已经提到 Transform Points 要放在 Projection 之前这里再讲一个进阶版。如果你想让生成的岩石在贴合表面的同时还能有更自然的角度变化可以在 Projection 之后再接一个轻量级 Transform Points 节点只调整旋转不调整位置。比如在原有旋转基础上加一个很小的 Pitch 和 Roll 随机范围比如正负 5 度让石头不是完美贴合表面而是稍微歪一点这样视觉效果反而更真实。这个思路的本质是先让 Projection 保证位置和大致朝向正确再用额外的随机扰动打破“过于完美”的秩序感。自然场景里的石头、树木从来不会整整齐齐但也不至于东倒西歪这中间的“度”就是这么调出来的。5.2 配合 Density Filter让投射算法只在自己需要的表面生效在实际场景里同一张 PCG 图表往往要处理多种表面路面、草地、岩石区域、水池边缘。你希望植被只长在草地上、岩石只出现在山坡上这时候就不能对全地形无脑投射。我的做法是先用Landscape Layer地形层或者属性判断比如Layer属性给点打标签再用 Density Filter 把符合条件的点放进 Projection不符合条件的点走另一条分支。这样每类物件的投射目标、偏移量、对齐方式都可以独立配置互不干扰。举个例子地形上有一条玩家行走的土路你要在路两边生成杂草但路面本身保持干净。你需要的是先拿样条线或地形层标记路面范围用 Density Filter 排除路面内的点再做投射贴合。这样生成的杂草只会出现在土路边缘而且自然贴合地势。5.3 配合 Collision Constraint解决大面积散布的穿插问题如果你想在整片山坡上生成几百块密集的碎石让我来给你一个比较实用的组合Surface Sampler 撒点 → Transform Points 随机缩放旋转 → Projection 贴合 → Collision Constraint 互相推开。Collision Constraint 节点会检测点与点之间的距离并把过近的点推开直到间距达到设定范围。注意它要放在投射之后因为投射后的点在表面上它才能基于真实的表面位置做间距计算。如果放在投射前点之间距离合理但投射之后位置可能发生变化约束就白做了。实测下来这套组合在生成大范围碎石堆时效果拔群。你只需要控制好 Collision Constraint 的迭代次数和半径很快就能得到一摊自然铺开、互不叠压的碎石。5.4 用 Points From 和 Scatter 做同一点集的多表面贴合还有一种少见的用法同一份点集要贴合两个完全不同的表面。比如一片区域里既有矮墙又有地面你希望植被既能长在墙顶、又能长在墙根。做法是首先用 Scatter 节点在目标面上生成两种点集把点集按属性拆开比如OnWall为 True 的和 False 的分别接两个 Projection一个投射方向是墙壁法线方向另一个是向下方向然后把两个结果 Merge 到一起再统一生成实例。这个玩法比较高级要点是用属性控制一套流程的分叉而不是复制出巨大的图表。PCG 的节点图越简洁越好维护分叉太多会让人头大。5.5 Projection 在运行时 PCG 的注意点如果你的项目用了 Runtime PCG运行时 PCG比如在游戏过程中动态生成道具、碎石、血迹之类的表面贴合效果Projection 同样适用。但有几个性能上的注意点Projection 的 Ray 检测开销在运行时会被放大尽量缩小 Target 范围不要让 PCG 对整张地图做投射。运行时动态生成要考虑 LOD 问题投射后实例过多会直接影响帧率建议运行时密度调低静态生成密度调高。如果你用的 Target 是运行时创建的临时网格体要注意它在生成前必须已经完成了碰撞体构建否则 Projection 无处可投。6. 写在最后的实操建议把贴合当成一个持续迭代的过程Projection 这个节点学起来很快但真正用好它需要在项目里反复打磨。我给刚上手的读者几条比较实在的建议先在地形上跑通最小链路确认投射基线没问题然后每次只调一个参数比如先调偏移、再调对齐调试时把 PCG 调试视图打开随时观察箭头的方向和点的位置最后再连到 Static Mesh Spawner避免在调试阶段就被一堆模型遮挡视线。还有一个小习惯我在多个项目里都验证过给所有带 Projection 的 PCG 图表加一个“投射距离”变量集中管理不要写死在节点上。因为同一个图表可能会复用到多个地形上不同地形的高差差异很大集中管理这个变量能让你少改十个节点。类似的投射偏移值也建议做成可配置变量方便后续根据模型锚点批量调整。PCG 这个工具链本质上就是把你对“场景中物体应该怎么摆放”的理解翻译成一张张节点图。Projection 解决的是其中“贴合表面”这个动作。多跑几个地形、多试试不同的 Target 类型、多观察投射后的法线方向你就会慢慢掌握它的脾气让程序生成的场景越来越接近手摆的效果。