# GPU Instanced Foliage Renderer

A GPU-driven, compute-shader-based instanced renderer for scattering large amounts of
foliage, grass, rocks, or any repeated mesh across a scene — with real terrain-aware
placement, natural distribution, and slope-based culling built in.

**This is a renderer, not a content pack.** No meshes or textures are included — you bring
your own mesh (grass, flowers, rocks, anything with geometry) and this asset handles
GPU-driven placement, culling, and instanced drawing at scale.

---

## Requirements

- **Unity 2022.3 LTS or newer** (developed and tested on 2022.3.62f LTS)
- **Built-in Render Pipeline (BiRP)** — this version does not support URP or HDRP
- A GPU with **Shader Model 5.0 / compute shader support** (any reasonably modern desktop
  GPU; not intended for mobile or WebGL targets)
- Your own mesh to scatter — not included

---

## Texture Support

The included shader reads exactly **two texture inputs**: an **Albedo/Diffuse** map and an
**Alpha Cutout Mask**. There's no normal mapping, roughness, AO, or any other PBR channel —
instances render flat-shaded with simple directional lighting.

That makes this a strong fit for **simple, single-material foliage**: grass, flowers, small
rocks, debris, and similar low-complexity meshes. It is **not** a fit for meshes that need
multiple materials or a full PBR texture set to read correctly — trees (separate bark/leaf
materials), or large hero rocks built from a Diffuse + Normal + Roughness/AO set (e.g.
Quixel Megascans-style `nor_gl`/`rough` maps). Assign a mesh like that and the extra maps
are simply ignored — you'll get a flat result missing the surface detail those channels add.

---

## Quick Start

1. Add the **GPU Forest Indirect** component (`Tessaractic/GPU Forest Indirect`) to an
   empty GameObject in your scene.
2. Assign the five **Required References**:
   - **Instance Mesh** — the mesh you want to scatter
   - **Instance Material** — a material using the included `InstancedIndirectLit` shader
   - **Instance Generator** — the included `ForestGenerator` compute shader
   - **Instance Culling** — the included `Forest_Culling` compute shader
   - **Player Camera** — your scene's camera (optional — falls back to `Camera.main` if
     left blank)
3. Set **Instance Count**, **Spacing**, and **Area Size** to roughly match your scene's
   scale.
4. If your mesh doesn't render upright, set **Mesh Rotation Offset Euler** under *Mesh
   Selection* to correct it (common with FBX exports where the "correct" orientation comes
   from a parent node rather than the mesh's own raw vertex data).
5. Press Play.

If you have a Unity `Terrain` in the scene, it's picked up automatically — no manual setup
needed. Without one, instances place on a flat plane at **Ground Y**.

---

## Features

- **Fully GPU-driven pipeline** — placement, culling, and drawing all happen on the GPU via
  compute shaders and `Graphics.RenderMeshIndirect`. No per-instance CPU overhead.
- **Real terrain-aware placement** — samples your active Unity `Terrain`'s actual heightmap
  directly. Detected automatically; no manual alignment required.
- **Slope-normal alignment** — instances lean to match the terrain's slope instead of
  always standing world-vertical, with a tunable blend between the two.
- **Slope-based placement filtering** — thin or fully cull instances on terrain that's too
  steep, with a soft per-instance gradient between the two thresholds rather than a hard,
  poppy cutoff.
- **Natural, organic distribution** — noise-driven density creates clumpy, irregular
  coverage instead of a uniform lawn, bounded by a real, adjustable placement area. A
  single Scatter Amount slider blends between that natural clumping and a perfectly even
  grid, for scenes that want less visual randomness.
- **GPU frustum culling**, every frame.
- **Dynamic wind system** — six built-in presets (Calm through Tornado) or fully custom
  direction/turbulence/gust tuning, switchable live at runtime via a public API.
- **Distance-based LOD for texture sampling** — a tunable near/far blur ramp keeps close-up
  detail sharp and distant, densely-packed instances free of aliasing/noise.
- **Alpha cutout support** for cards, billboards, or any cutout-style foliage texture.
- **Runtime instance-count changes** supported via a public `Reinitialize(int)` method, for
  streaming or density changes at runtime.
- **Works with any mesh you supply** — grass, flowers, rocks, debris, anything.

---

## Inspector Reference

### Required References
| Field | Description |
|---|---|
| Player Camera | Camera used for culling/LOD. Optional — falls back to `Camera.main`. |
| Instance Mesh | The mesh to scatter. |
| Instance Material | Material using the included shader. |
| Instance Generator | The included generator compute shader. |
| Instance Culling | The included culling compute shader. |

### Placement
| Field | Default | Description |
|---|---|---|
| Instance Count | 500 | Total instances to generate. |
| Seed | 1337 | Random seed — change for a different layout at the same settings. |
| Area Size | 512 | World-space side length of the square placement bounds, centered on origin. |
| Spacing | 2 | Base grid spacing between instances before jitter/density thinning. |
| Ground Y | 0 | Flat-ground height, used when no Terrain is found (or Terrain sampling is off). |

### Distribution
| Field | Default | Description |
|---|---|---|
| Patch Scale | 15 | World size of one density-noise cell. Larger = bigger, more gradual clumps. |
| Density Threshold | 0.35 | Instances in low-density noise cells are thinned out (soft-edged, not a hard cutoff). Higher = sparser overall coverage. |
| Scatter Amount | 1.0 | Blends between a uniform grid (0) and the full natural clumping above (1, default). Lower it for a more even, less clumpy spread without changing Patch Scale/Density Threshold. |

### Terrain
| Field | Default | Description |
|---|---|---|
| Sample Real Terrain Height | On | Samples the active Terrain's real heightmap. Falls back to Ground Y automatically if no Terrain exists. |
| Normal Align Strength | 1.0 | How far instances lean toward the terrain's slope. 0 = always vertical, 1 = fully follows the slope. |
| Normal Sample Offset | 2.0 | World-space sample distance used to compute terrain slope. Wider = smoother on rough terrain but blurs fine detail — this is the value to tune, not heightmap resolution. |

### Slope Filtering
| Field | Default | Description |
|---|---|---|
| Slope Filter Start Angle | 35° | Slopes shallower than this always keep their instances. |
| Slope Filter End Angle | 55° | Slopes steeper than this always cull. Between the two thresholds, instances are thinned progressively and stably (no flicker). |

### Mesh Selection
| Field | Default | Description |
|---|---|---|
| Mesh Submesh Index | 0 | Which submesh to draw, for multi-submesh meshes. |
| Mesh Rotation Offset Euler | (0,0,0) | Corrects meshes that don't render upright — common with FBX exports. Leave at zero unless instances render tilted or on their side. |
| Instance Scale | 1.0 | Uniform scale multiplier applied to every instance. Handy if your mesh is naturally very small/large, or for demo/showcase visibility. |

### Rendering
| Field | Default | Description |
|---|---|---|
| Shadow Casting | On | Standard Unity shadow-casting mode. |
| Receive Shadows | On | Whether instances receive shadows from other casters. |
| World Bounds Size | (2000, 500, 2000) | Bounding volume passed to the renderer for visibility/culling purposes. |

### Wind
| Field | Default | Description |
|---|---|---|
| Wind Preset | Breeze | Six built-in conditions (Calm, Breeze, Gale, Storm, Hurricane, Tornado) that set Global Sway/Turbulence/Gust below to sensible values in one click. Pick Custom to hand-tune every slider without a preset overwriting them. |
| Global Sway | 1.8 | Overall wind sway intensity. Set with a preset, or tune directly under Custom. |
| Wind Direction | (1, 0) | World-space XZ direction the wind blows toward. Not affected by presets — set once per scene. |
| Turbulence | 0.6 | Higher-frequency chaotic motion layered on the base sway. 0 = perfectly smooth. |
| Gust Strength | 0.8 | How much stronger sway gets during a gust peak, as a multiplier on the base sway. 0 = no gusting. |
| Gust Frequency | 0.5 | How often gust pulses occur. Higher = more frequent gusts. |

Presets can also be changed live at runtime from code via the public
`SetWindPreset(GPUForestWindPreset preset)` method — handy for a day/night or weather
system that should make foliage react automatically.

**Optional: Wind Preset Hotkeys.** Add the included `Wind Preset Hotkeys` component
(`Tessaractic/Wind Preset Hotkeys`) to any GameObject and assign your renderer(s) to its
Targets list, and you can press **1–6** during Play mode to instantly preview every wind
preset (Calm through Tornado) without touching the Inspector or restarting — a small
on-screen label confirms which preset is active. Purely a convenience for testing; remove
the component from your final build if you don't want the 1–6 hotkeys active for players.

---

## Material Properties

The included `InstancedIndirectLit` shader exposes the usual lighting/texture controls
(Albedo, Alpha Cutout Mask + Cutoff, Base Color, Sun Color/Intensity, Ambient) plus:

- **LOD Near/Far Dist + LOD Max Level** — controls the distance-based texture blur ramp.
- **Debug Mode** (0–3) — a diagnostic overlay for troubleshooting your own mesh/texture
  setup without touching any code: `1` highlights cutout clipping in red, `2` visualizes
  the alpha mask's channels, `3` visualizes raw UVs as color. Leave at `0` for normal
  rendering.

---

## Performance

Real numbers from a standalone Release build (not Editor Play mode, which is not
representative — see note below), on a **4GB legacy/budget GPU (NVIDIA Quadro M1200)**:

| Instance Count | Avg Frame Time | Avg FPS | Min | Max | P95 |
|---|---|---|---|---|---|
| 500 | 14.89 ms | 67 | 2.81 ms | 39.65 ms | 24.32 ms |
| 2,000 | 20.78 ms | 48 | 3.74 ms | 62.73 ms | 24.18 ms |
| 5,000 | 38.61 ms | 26 | 22.95 ms | 54.23 ms | 53.64 ms |
| 10,000 | 63.66 ms | 16 | 47.64 ms | 78.80 ms | 64.42 ms |

**Takeaway:** smooth, real-time performance up to ~2,000 instances even on legacy
hardware, scaling down gracefully to 10,000+ for scenes that can afford it.

> **Note:** Always benchmark in a real build. Unity's Editor Play mode reports
> misleadingly poor frame times due to present-wait/VSync overhead that isn't present in a
> built player — don't judge real-world performance from the Editor.

---

## Known Limitations

- **Built-in Render Pipeline only** — no URP or HDRP support in this version.
- **Two texture channels only (Albedo + Alpha Cutout)** — no normal mapping, roughness, AO,
  or other PBR maps. See *Texture Support* above for what this is and isn't a good fit for.
- **One mesh per component** — for a mix of foliage types (e.g. grass + flowers + rocks),
  add multiple `GPU Forest Indirect` components, one per mesh, each with its own settings.
- Your mesh must have **Read/Write Enabled** in its import settings.
- No meshes, textures, or content are included — this is a renderer only.

---

## Troubleshooting

**Nothing renders.**
Check that all five Required References are assigned, and check the Console for errors —
the component validates its setup on Awake and will log clearly if something's missing.

**Instances are tilted or lying on their side.**
Your mesh's raw vertex data likely isn't authored Y-up. Set **Mesh Rotation Offset Euler**
under *Mesh Selection* to correct it (a value of `(-90, 0, 0)` is a common fix for this
class of import).

**Instances sink into or float above the ground.**
The renderer automatically offsets by your mesh's own bounds, but double-check your mesh's
pivot/origin is reasonable. On terrain, also confirm **Sample Real Terrain Height** is
enabled and a `Terrain` object actually exists in the scene.

**Texture/UV looks wrong or a mesh renders as a flat color.**
Use **Debug Mode** on the material (see *Material Properties* above) to visualize raw UVs
and mask channels — this usually points straight at the issue without needing to touch any
code.

**Framerate looks worse in the Editor than expected.**
Normal — see the note under *Performance*. Test in a real build for accurate numbers.

---

## Support

Published by **TessaractLab**.
Contact: `TessaractLab@outlook.com` / `Tessaractlabs.help@outlook.com`

---

*Version 1.0 — Available now on Fab; Unity Asset Store submission pending review*
