RVT Landscape Blend¶
Summary: A reusable pattern for making world-placed meshes (rocks, cliffs, props) fade seamlessly into the landscape they sit on, using a Runtime Virtual Texture. The mesh samples the landscape's base color, normal and roughness near its base and blends toward its own material over a height-based fade window, dithered by procedural noise. First shipped for desert cliffs (2026-05-10) but the material stack is generic.
Table of Contents¶
- Why RVT Blend
- Pattern Overview
- Asset Set
- Parameters
- Critical Wiring Details
- Applying to New Meshes
- Tuning Guide
- Related Systems
- Recent Changes
Why RVT Blend¶
A hard color/normal cutoff where a mesh intersects the landscape reads badly, especially in a stylized/painterly art direction. A Runtime Virtual Texture lets the mesh read the landscape's shaded surface (base color, normal, roughness, world height) at its own world position and blend into it near the contact line, so the transition dissolves instead of cutting.
Pattern Overview¶
┌──────────────────────────┐ writes ┌────────────────────────┐
│ Landscape material │ ──────────────▶ │ RVT pages │
│ (…_RVTBlend variant with │ │ • BC + Normal + Rough │
│ RuntimeVirtualTextureOut)│ │ • WorldHeight │
└──────────────────────────┘ └───────────┬────────────┘
│ samples
┌───────────▼────────────┐
│ Mesh master (…_RVTBlend)│
│ height-fade + noise │
│ lerp( mesh, RVT ) │
│ static switch on/off │
└─────────────────────────┘
Three moving parts:
1. A landscape material variant that adds a RuntimeVirtualTextureOutput node, writing BC/Normal/Roughness (and a separate WorldHeight RVT).
2. RVT volume actors covering the landscape so the pages get populated.
3. A mesh master material that samples the RVT and lerps between its own surface and the landscape's, gated by a height-based fade window and procedural-noise dither, behind a static switch so blend-off meshes pay no cost.
Asset Set¶
The desert instance (paths illustrative of the pattern, under MaterialLibrary):
| Role | Asset |
|---|---|
| RVT (BC + Normal + Roughness) | /Game/MaterialLibrary/Landscape/RVT/RVT_Desert_BCNR |
| RVT (WorldHeight) | /Game/MaterialLibrary/Landscape/RVT/RVT_Desert_Height |
| Landscape material variant | /Game/MaterialLibrary/M_Ground_Layered_RVTBlend (clone of M_Ground_Layered + RuntimeVirtualTextureOutput) |
| Mesh master | /Game/MaterialLibrary/M_ORM_RVTBlend (clone of M_ORM + RVT blend stack + static switch) |
| Per-mesh MIs | MI_Cliff_{0B,0C,1C}_RVTBlend in …/Shared/Materials/Rocks/ |
| RVT volumes | 3× RVTVolume_* actors covering the landscape |
MI_Landscape_Desert is reparented to the landscape variant. To reuse the pattern in another biome, clone the two masters, author a matching pair of RVTs, and place RVT volumes over that landscape.
Parameters¶
Exposed on the mesh master (M_ORM_RVTBlend):
| Parameter | Default | Purpose |
|---|---|---|
bUseLandscapeBlend (static switch) |
— | Toggle blend on/off; off = pure mesh, zero added cost |
RVT_FadeStart |
5 | Height above landscape where the blend begins |
RVT_FadeRange |
200 | Fade thickness |
RVT_Tint (vector) |
white | Color bias on the blended result |
RVT_BlendNoiseTile |
0.005 | Procedural noise scale (lower = bigger blobs) |
RVT_BlendNoiseStrength |
0.6 | Dither chaos in the transition zone |
Occlusion (static switch) |
false | Toggle baked AO from M.R |
Roughness Value |
1 | Multiplier on M.G |
RVT_PDO_Amount existed but the PDO chain was removed (see wiring notes) — mild z-fighting at the intersection is accepted; fade range + dither carry the transition.
Critical Wiring Details¶
These are the parts that are easy to get wrong and produce dark/wrong-lit meshes:
- Material output:
tangent_space_normal = Falseon the mesh master, so the Normal pin takes a world-space vector directly. - Mesh normal → world space: put a
Transformnode (TANGENT → WORLD) on the mesh's tangent-space normal map before the lerp — the RVT normal is world-space, so both sides must be world-space before mixing. - Landscape RVT normal: the
LandscapeLayerBlendoutput is tangent-space too, so it also needs a Tangent → WorldTransformbefore feedingRuntimeVirtualTextureOutput.Normal. - After the normal lerp: a
Normalizenode before output — lerping two unit vectors yields a non-unit vector. - Noise gating: multiply the noise contribution by a
mask * (1 - mask) * 4window so noise only perturbs the transition band, not the whole mesh. - Procedural noise, not textures: use
MaterialExpressionNoise, not aTextureSample, for the dither — avoids visible tiling artifacts. - PDO: not used. Python's
MaterialPropertyenum doesn't exposeMP_PIXEL_DEPTH_OFFSETin 5.6 anyway (see UE Python Material API Reference); it would have to be dragged manually. The look was rejected regardless.
Applying to New Meshes¶
When adding new meshes that should fade into an RVT-enabled landscape:
1. Parent the mesh's MI to the mesh master (M_ORM_RVTBlend or a biome equivalent).
2. Set bUseLandscapeBlend = true.
3. Tune RVT_FadeStart / RVT_FadeRange / noise per-MI.
4. Confirm an RVT volume actually covers the mesh's footprint (no volume = no pages = no blend).
Tuning Guide¶
| Symptom | First thing to check |
|---|---|
| Mesh too dark in lit shading at its base | Buffer Visualization → Normal. Non-uniform → RVT.Normal pages are wrong; re-verify Tangent → World on the landscape side |
| Hard color seam remains | Increase RVT_FadeRange |
| Visible texture-tile artifacts in the blend | Ensure noise comes from MaterialExpressionNoise, not a texture sample |
| Mesh lit fine with blend off, dark with blend on | Suspect RVT pages contain wrong/zero data (a probe script may have stomped wires — see Probe scripts stomp wires) |
Related Systems¶
- Tonemapping — scene color/grading the blended result renders under
- UE Python Material API Reference — the material-scripting quirks (pin names, world-space normal, missing PDO enum) hit while authoring this stack
- Unreal MCP + Python Automation Guide — how the authoring scripts ran
Recent Changes¶
| Date | Change | Impact |
|---|---|---|
| 2026-07-03 | Documented the pattern reusably from the desert-cliff instance | Reference for future biomes |
| 2026-05-24 | One-shot authoring scripts deleted (recoverable from git before 5bc8f5b11) |
Material graph is now the source of truth, not the scripts |
| 2026-05-10 | PDO chain removed; z-fight accepted, fade + dither carry the transition | Simpler stack |