PCG Basics: Your First Procedural Scatter System in UE5
UE5’s PCG framework lets you define rules instead of positions — and it’s way easier than you’d expect.
You’ve got a landscape. You’ve got a folder full of rock and grass meshes. Now you need to scatter thousands of them across the terrain in a way that looks natural.
You could spend an afternoon dragging individual assets around the viewport, adjusting each transform by hand. Or you could spend 20 minutes building a PCG graph that does it for you — and regenerates instantly every time you change a setting.
This tutorial walks you through building your first procedural scatter system using Unreal Engine 5’s Procedural Content Generation (PCG) framework. We’ll go from an empty graph to a landscape covered in randomized rocks and grass, with density controls, slope filtering, and proper performance setup.
Prerequisites: You’ve used UE5 before — you know the editor, the Content Browser, and basic level design. No PCG experience needed.
Engine version: UE 5.4 or later. PCG was experimental in 5.2–5.3, reached beta in 5.4, and became fully production-ready in UE 5.7 (with nearly 2× the performance over 5.5). Everything in this tutorial works across those versions, but 5.5+ is recommended for stability.
What PCG actually is
The Procedural Content Generation framework is a node-based system built into Unreal Engine for generating content through rules instead of manual placement. If you’ve used the Material Editor or Niagara, the workflow will feel familiar: you connect nodes in a graph, data flows left to right, and the output updates in real time in your viewport.
Epic’s documentation describes it as “a toolset for creating your own procedural content and tools inside Unreal Engine,” covering everything from asset scattering to entire world generation. But for today, we’re keeping it simple.
PCG graphs work in three stages:
- Generate points — sample positions on a surface, along a spline, or within a volume
- Filter and modify — remove points by density, slope, or overlap; randomize their transforms
- Spawn assets — place static meshes (or actors) at the surviving points
Instead of telling the engine where to put each rock, you tell it how rocks should be distributed. “Place them on flat areas, scale them randomly between 0.8 and 1.5, keep them apart from each other.” PCG handles the math.
When PCG makes sense: Populating environments with hundreds or thousands of foliage instances, rocks, debris, or props. Iterating quickly on layout — change one density value and the whole scatter regenerates. Runtime generation for roguelikes or procedural worlds.
When manual placement is better: Hero assets and hand-crafted setpieces. Small indoor scenes with deliberate art direction. Fewer than ~50 placements where hand-tuning is just faster.
Step 1: Enable the plugin and prep your assets
Open Edit → Plugins and search for “Procedural Content Generation.” Enable the Procedural Content Generation Framework plugin and restart the editor. This plugin isn’t enabled by default in most project templates, so don’t skip this step. If you want to sample points on static meshes later (not just landscapes), also enable the Procedural Content Generation Framework Geometry Script Interop plugin — you won’t need it today, but it’s good to have.
For this tutorial you’ll need a Landscape in your level (any size — the default works fine) and at least one rock mesh and one grass mesh. Quixel Bridge or Fab has plenty of free options if you need assets to test with.

Step 2: Create the graph and add a PCG Volume
Right-click in the Content Browser → Create Advanced Asset → PCG → PCG Graph. Name it
PCG_RockGrassScatter.Now you need something in the level to execute this graph. The simplest approach is a PCG Volume. Open the Place Actors panel, search for “PCG Volume,” and drag one into your level. Scale it to cover the area of your landscape you want to populate.
Select the PCG Volume, find the PCG Component in the Details panel, and set the Graph property to your
PCG_RockGrassScatter asset.Double-click the graph asset to open the editor. You’ll see an empty canvas with default Input and Output connection points — similar to the Material Editor’s setup. Everything we build goes between these.

Step 3: Sample points on the landscape
The Surface Sampler is the foundational node for landscape scattering. It generates a grid of sample points distributed across a surface.
Right-click the canvas, add a Surface Sampler, and connect the Landscape output from the Input node to the Surface Sampler’s Surface input. This tells the sampler which surface to project onto.
Tip: Missing this connection is the single most common beginner mistake. No surface connected = zero points generated = nothing happens and you’ll wonder what you broke.
Now let’s visualize. Select the Surface Sampler and press D on your keyboard to toggle debug mode. Grayscale boxes appear on your landscape in the viewport. Each box is a point, and its shade represents the density attribute — white means density 1.0 (will spawn), black means 0.0 (will be filtered out). Get used to pressing D constantly — it’s your best friend in PCG.
Key settings to configure:
- Points Per Squared Meter: Start at
0.05for rocks (one point per 20m²). You can always increase later — resist the urge to go high during setup. - Point Extents: The half-size bounding box per point. Default of
100 × 100 × 100is fine for large objects like boulders. - Looseness: How much randomness to add to the grid spacing. Set this to
1.0so your scatter doesn't look like a grid. A value of 0 = perfect grid, 1.0 = natural variation.

Step 4: Filter points by slope
Raw sampler output places points everywhere, including cliff faces where rocks would look absurd. Let’s fix that.
Add a Normal to Density node after the Surface Sampler. This reads each point’s surface normal and converts it to a density value — a perfectly flat surface gets density 1.0, a vertical cliff gets 0.0, and slopes get values in between.
Follow it with a Density Filter node. Set Lower Bound to
0.5 and Upper Bound to 1.0. This keeps only points on surfaces flatter than roughly 60° from horizontal — exactly where loose rocks would naturally accumulate. Steeper points get discarded.For organic variation, add a Density Noise node between Normal to Density and the Density Filter. This applies procedural noise to the density values, breaking uniform coverage into natural-looking clusters and gaps. Without this, your scatter will feel too even. A Cell Size around
5000 gives broad, believable variation.
Step 5: Randomize transforms
Add a Transform Points node after the Density Filter. This is where your scatter goes from “procedural grid” to “natural environment.”
Rotation: Set Rotation Min to
(0, 0, 0) and Rotation Max to (0, 0, 360). This randomizes yaw (horizontal rotation) so every rock faces a different direction. For rocks specifically, you can add slight pitch and roll variation too — (-10, -10, 0) to (10, 10, 360) — to prevent the "placed on a shelf" look.Scale: Set Scale Min to
(0.7, 0.7, 0.7) and Scale Max to (1.4, 1.4, 1.4) for uniform random scaling. Keep all three axes matching for proportional results — otherwise you'll get stretched meshes.Offset (optional): If you want to partially bury rocks, enable Absolute Offset and set both Offset Min and Max Z to something like
-15. This sinks every mesh slightly into the ground for a more grounded look.
Step 6: Spawn the rocks
Add a Static Mesh Spawner at the end of the chain. In the Details panel, expand the Mesh Entries array, click +, and assign your rock mesh.
If you have multiple rock variants, add more entries. The spawner does weighted random selection — each entry has a Weight property (default 1). A mesh with weight 2 appears twice as often as one with weight 1. This is a free win for visual variety.
To prevent rocks from overlapping each other, insert a Bounds Modifier and then a Self-Pruning node before the Static Mesh Spawner. The Bounds Modifier sets each point’s bounding box to match your mesh’s footprint. Self-Pruning then compares all points and removes overlapping ones. Set the pruning type to Randomized so it doesn’t create visible patterns.
Click Generate on the PCG Volume and your rocks appear.
Step 7: Add a grass layer
One graph can handle multiple asset types. Duplicate your node chain (select all, Ctrl+C, Ctrl+V) and modify the copy for grass.
Changes for the grass branch:
- Points Per Squared Meter: Increase to
1.0–5.0(grass is much denser than rocks) - Point Extents: Reduce to
30 × 30 × 30 - Scale: Smaller range —
(0.5, 0.5, 0.5)to(1.0, 1.0, 1.0) - Density Filter: Widen to Lower Bound
0.3so grass creeps onto moderate slopes - Static Mesh Spawner: Assign your grass mesh(es)
Preventing grass from spawning inside rocks
Add a Difference node. Connect your rock point data to the Differences input and your grass point data to the Source input. The Difference node removes any grass points that overlap with rock bounds, carving out clean exclusion zones around each boulder.
Performance: what will actually wreck your framerate
PCG is fast to set up but easy to make slow. The graph itself runs in the editor — it’s the spawned meshes that affect runtime performance. Here’s what matters:
Start with low density during development. Build your graph at 0.01–0.05 Points Per Squared Meter. Get the logic right first, then scale up. Community members have crashed the editor by generating millions of instances at full density before their graph was ready.
Set cull distances on every spawned mesh. The Static Mesh Spawner has an Instance End Cull Distance setting — use it. Something like
5000 for grass, 15000 for rocks. Without cull distances, an Unbounded scatter renders every single instance regardless of camera distance.Disable “Affect Distance Field Lighting” in each Static Mesh Spawner’s advanced settings. Forum posts consistently identify this checkbox as responsible for massive FPS drops — 60 to 15 FPS in some cases.
Consider the Foliage tool for dense grass. Multiple developers have reported that PCG-spawned grass underperforms the built-in Foliage paint tool for very dense ground cover. The Foliage system has optimized volume-based culling that PCG’s HISM components don’t fully replicate. Use PCG for larger assets (rocks, trees, props) where the rule-based approach adds real value, and consider the Foliage tool or Landscape Grass Types for carpet-level ground cover.
Set explicit random seeds. Without fixed seeds, regenerating your graph produces different results each time. Set a Seed value on your PCG Component for deterministic, reproducible output. This matters for level design iteration and debugging.
Collapse reusable logic into subgraphs. Select a chain of nodes, right-click → Collapse into Subgraph, and you get a reusable module with custom input pins. This keeps complex graphs manageable and lets you share patterns across multiple PCG setups.
Common beginner mistakes
No landscape connection. The Surface Sampler produces nothing without its Surface input wired to the Input node’s Landscape pin. Always connect this first.
Skipping debug mode. Press D on any node to see its output as colored points. Without this, you’re working blind. Debug each node in sequence when something looks wrong.
Full density during iteration. Start at 10% of your target density. Finalize the graph logic, then crank it up. Regenerating a dense graph after every node change is painfully slow.
Overlapping volumes. Multiple PCG Volumes covering the same area will double-spawn assets in the overlap zone. Use Difference nodes or careful volume boundaries.
Stacked meshes. Without Bounds Modifier → Self-Pruning, points with overlapping footprints will spawn meshes directly on top of each other. This causes visual artifacts and overdraw.
Where to go from here
This scatter system covers the fundamentals, but PCG goes much deeper:
- Spline-based scattering lets you populate paths, rivers, and fences. UE 5.7’s new PCG Editor Mode includes a dedicated Draw Spline tool for painting splines directly in the viewport.
- Mesh Sampling (via the Geometry Script Interop plugin) scatters points on any Static Mesh surface — moss on boulders, barnacles on ship hulls, mushrooms on logs.
- Attribute-driven spawning uses custom data on each point to control mesh selection, enabling biome systems where terrain type drives vegetation.
- Runtime generation allows PCG to create content at play time — Epic used it for LEGO Fortnite’s procedurally generated islands.
For a production-scale reference, download the Electric Dreams sample project (free on Fab). It’s a 4km × 4km jungle environment built almost entirely with PCG, complete with detailed documentation on every technique used.
PCG transforms environment art from a placement problem into a rules problem. Start simple, debug everything, set your cull distances, and build complexity one node at a time.