⚠️ Beta State
PyBevy is in an early and experimental stage. The API is incomplete, subject to breaking changes without notice, and you should expect bugs. Many features are still under development.
Post-Processing Effects
Add bloom, tonemapping, and color grading to your scenes.
Introduction
Post-processing effects are applied to the camera's rendered image to enhance
visual quality. pybevy supports several effects as components that you attach
to your camera entity:
- Bloom: Makes bright surfaces glow.
- Tonemapping: Converts HDR to displayable colors.
- ColorGrading: Adjusts exposure, saturation, contrast, and more.
from pybevy.prelude import *
from pybevy.camera import Bloom, Tonemapping, ColorGrading, ColorGradingGlobal, ColorGradingSectionSetting Up a Scene with Emissive Materials
Bloom works best with emissive materials — surfaces that emit light. We'll create a dark scene with glowing objects.
def setup(
commands: Commands,
meshes: ResMut[Assets[Mesh]],
materials: ResMut[Assets[StandardMaterial]],
):
# Camera with post-processing effects
commands.spawn(
Camera3d(),
Transform.from_xyz(0.0, 3.0, 10.0).looking_at(Vec3.ZERO, Vec3.Y),
# Bloom makes bright things glow
Bloom(intensity=0.3),
# Tonemapping converts HDR colors to displayable range
Tonemapping.TONY_MC_MAPFACE,
# Color grading for cinematic look
ColorGrading(
global_=ColorGradingGlobal(
exposure=0.5,
post_saturation=1.2,
),
shadows=ColorGradingSection(
saturation=0.8,
),
),
)
# Dark ground
commands.spawn(
Mesh3d(meshes.add(Plane3d(Vec3.Y, Vec2(20.0, 20.0)))),
MeshMaterial3d(materials.add(StandardMaterial(
base_color=Color.srgb(0.05, 0.05, 0.05),
))),
)
# Glowing spheres with emissive materials
colors = [
Color.srgb(1.0, 0.2, 0.2), # Red
Color.srgb(0.2, 1.0, 0.2), # Green
Color.srgb(0.2, 0.2, 1.0), # Blue
Color.srgb(1.0, 0.8, 0.0), # Yellow
Color.srgb(1.0, 0.2, 1.0), # Magenta
]
sphere = meshes.add(Sphere(0.5))
for i, color in enumerate(colors):
x = (i - 2) * 2.5
commands.spawn(
Mesh3d(sphere),
MeshMaterial3d(materials.add(StandardMaterial(
base_color=color,
# Emissive makes the surface glow — the key to visible bloom
emissive=color.to_linear() * 5.0,
))),
Transform.from_xyz(x, 0.5, 0.0),
)
# A dim light so non-emissive surfaces are still slightly visible
commands.spawn(
PointLight(intensity=2000.0, range=30.0),
Transform.from_xyz(0.0, 5.0, 5.0),
)Adjusting Effects at Runtime
You can query and modify post-processing components like any other component.
def adjust_bloom(
keyboard: Res[ButtonInput],
query: Query[Mut[Bloom], With[Camera3d]],
):
for bloom in query:
if keyboard.just_pressed(KeyCode.ArrowUp):
bloom.intensity = min(bloom.intensity + 0.05, 1.0)
print(f"Bloom intensity: {bloom.intensity:.2f}")
if keyboard.just_pressed(KeyCode.ArrowDown):
bloom.intensity = max(bloom.intensity - 0.05, 0.0)
print(f"Bloom intensity: {bloom.intensity:.2f}")Tonemapping Algorithms
Different tonemapping algorithms produce different visual styles:
TONY_MC_MAPFACE: Good default, preserves hue.ACES_FITTED: Cinematic, used in film industry.AGX: Balanced, good for games.REINHARD: Classic algorithm, can wash out colors.NONE: No tonemapping (raw HDR values clamped).
Running the App
@entrypoint
def main(app: App) -> App:
return (
app
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, adjust_bloom)
)
if __name__ == "__main__":
main().run()