Feature Overview
rv is built around a simple idea: keep scene generation in Python, but reuse Blender's modeling, shading, geometry nodes, physics, and rendering stack for the heavy lifting.
The examples in examples/ cover the main workflows you will use in practice. This page summarizes those features and points to the corresponding example scripts.
Build scenes with a small Python API
At the core of rv is a Scene class. You create objects, materials, lights, and cameras directly from Python:
import rv
class BasicScene(rv.Scene):
def generate(self):
self.get_world().set_params(sun_intensity=0.03)
cube = (
self.create_cube()
.set_location((1, 0, 0.5))
.set_scale(0.5)
.set_tags("cube")
)
sphere = (
self.create_sphere()
.set_location((-1, 0, 1))
.set_shading("smooth")
.set_tags("sphere")
)
plane = self.create_plane(size=1000)
empty = self.create_empty().set_location((0, 0, 1))
cam = (
self.get_camera()
.set_location((7, 7, 3))
.point_at(empty)
)This keeps scene generation compact while still giving you access to Blender-native behavior. See examples/1_primitives/scene.py.
IDE support
Use rv python install command to add rv to the current active python virtual environement. This will add code completion for rv into your IDE of choice. It is recommended to create an empty virtual environment from scartch. It will not be used in the runtime, it is meant for IDE support only.
Live preview
rv preview watches your scene script, reloads it on change, and gives you a fast iteration loop before running a full render.
The default mode opens a Blender window:
rv preview examples/1_primitives/scene.pyUse this when you want the normal interactive Blender view while editing geometry, materials, lighting, or camera placement.
If you want rendered preview outputs written to disk on every change, enable preview files:
rv preview examples/1_primitives/scene.py --preview-filesThis combined mode does both: it keeps the Blender window open and also writes a single preview sample into ./preview_out by default. You can change the output folder with --preview-out, set the image size with --resolution WIDTH,HEIGHT, and limit render time with --time-limit.
For a headless workflow, add --no-window together with --preview-files:
rv preview examples/1_primitives/scene.py --preview-files --no-windowThis mode does not open Blender. Instead, it continuously refreshes the preview files on disk, which is useful for remote environments or when you only want image outputs.
TLDR; Live-view workflows are:
- Default: Blender window only.
- Headless:
--preview-files --no-window. - Combined:
--preview-filesfor Blender window plus rendered preview files on disk.
Import reusable assets from .blend files
When geometry is more complex than a few primitives, design it in Blender and import from Python. rv loads named objects from a .blend file and returns an ObjectLoader:
rock_loader = self.load_object("./rock.blend", "Rock")
rock = rock_loader.create_instance()This is the recommended workflow for artist-made assets, procedural Blender setups, and scenes you want to reuse across multiple dataset scripts. See examples/2_properties/scene.py.
Drive Blender node setups with object properties
Synthetic data usually needs variability. In rv, the preferred way to expose that variability is to keep procedural logic in Blender and drive it from Python using object properties.
Material nodes
To control a material from Python:
- Add an
Attributenode inside the material node graph.
- Set the attribute name, for example
color_base. - Set Attribute Type to Object.
- Read that value in the shader and assign it with
set_property(...).
obj.set_property("color_base", (0.93, 0.92, 0.91))Modifiers
For procedural object generation, a good pattern is to keep the modeling logic inside a Geometry Nodes modifier and parameterize it from Python.
To control a Geometry Nodes modifier from Python:
- Add a Geometry Nodes modifier to the object in Blender.
- Expose the inputs you want to randomize on the modifier interface.
- Change those inputs from Python with
set_modifier_input(...).
Minimal Python side:
obj.set_modifier_input("seed", 123.4)If the object has multiple Geometry Nodes modifiers, pass modifier_name as well:
obj.set_modifier_input("seed", 123.4, modifier_name="RockGenerator")This keeps procedural modeling inside Blender, while Python only supplies the randomized parameters that drive the modifier.
Note that this workflow is not limited to Geometry Nodes modifiers and can by applied to other modifiers as well.
Scatter many objects without manual placement
rv includes multiple geometry-based scattering workflows for filling a domain with many objects while keeping them separated.
Create a domain:
domain = rv.Domain.ellipse(center=(0, 0), radii=(12, 6), z=0.0)Scatter simple instances:
self.scatter_by_sphere(source=object_loader, count=350, domain=domain, min_gap=0.15)Scatter with mesh-aware placement:
self.scatter_by_bvh(source=object_loader, count=300, domain=domain, min_gap=0.2)Scatter procedural instances with per-object parameters:
self.scatter_parametric(source=source, count=30, domain=domain, strategy="bvh")The available example scenes show three useful patterns:
examples/3_scattering/ellipse_2d.py: fast planar scattering inside an ellipse.examples/3_scattering/hull_3d.py: fill a 3D convex hull volume.examples/3_scattering/parametric_scatter.py: vary each placed instance with a sampler/applier pair.
For many synthetic scenes this is enough. If you need physically plausible final resting positions, use rigid body simulation after or instead of geometric scattering.
Export semantic material masks with shader AOVs
Object tags are useful for instance-level labeling, but many datasets also need masks for material regions such as rust, dirt, paint, or wear. rv supports this through Blender shader AOVs.
On the Blender side, write your mask into an AOV Output node named <channel>:
rust
Enable the same channel in Python:
self.enable_semantic_channels("rust", "clean_metal")Optionally control binarization:
self.set_semantic_mask_threshold(0.5)When rendered, rv exports semantic masks such as Mask_rust*.png and Mask_clean_metal*.png. See examples/4_semantic_aov/scene.py and examples/4_semantic_aov/README.md.
Use Blender rigid body physics when placement must look real
For piles, collisions, toppling objects, or any scene where contact matters, rv lets you set up rigid bodies and run Blender physics directly from the script.
Add rigid bodies:
plane.add_rigidbody(mode="box", body_type="PASSIVE", friction=0.9)cube.add_rigidbody(mode="box", body_type="ACTIVE", mass=0.2)Run the simulation:
rv.simulate_physics(frames=120, substeps=10, time_scale=1.0)This is especially useful for generating non-intersecting object piles and impact scenes. The physics examples include:
examples/5_physics/simple.py: a minimal falling-object setup.examples/5_physics/scatter.py: drop many randomized cubes into a box.examples/5_physics/wall_break.py: simulate an impact that breaks a wall.
Export generated scenes and reuse them later
Some scenes are expensive to build, especially if they depend on simulation. rv can save the generated result as a .blend file and reuse it in another script.
Export a scene:
rv export examples/6_export/export.py -o examples/6_export/exported.blend --freeze-physicsLoad the saved objects later:
loaders = self.load_objects(str(EXPORTED_BLEND), import_names=CUBE_NAMES)Instantiate them as many times as needed:
obj = loader.create_instance()This is useful when you want to simulate once and then render many camera or lighting variations from the saved result. See examples/6_export/export.py, examples/6_export/import.py, and examples/6_export/README.md.
Preview textures
Exported depth and index masks are not comprehendable by human eye. This rv exports additional preview masks alongside them.


Typical workflow
In practice, many dataset scripts follow the same pattern:
- Build or import assets.
- Randomize object properties that drive Blender nodes.
- Place objects manually or with scattering or physics.
- Add tags, semantic channels, and render passes.
- Render with
rv renderor save an intermediate scene withrv export.
Start with the small examples in examples/, then use the API reference when you need the full method signatures.