Files
reliquary/plugins/bevy/skills/bevy-0.19/references/camera-rendering.md
T
g4borg c661027d19 🤖 Flesh out bevy-0.19 and bevy-upgrade skills from research
Replace scaffold placeholders with full skill content:
- bevy-0.19: ECS cheat sheet, critical traps table, architecture guide, and 7 reference files
- bevy-upgrade: version migration workflow, transition references for 0.15→0.19, and 3 per-bump reference files
2026-07-07 17:52:10 +02:00

3.7 KiB

Camera, Rendering, Assets

Camera setup

Camera3d is a marker with #[require(...)] pulling in Camera, DebandDither, CameraRenderGraph, Projection, Tonemapping, ColorGrading, Exposure. Spawning bare Camera3d yields a working 3D camera.

  • 0.18: RenderTarget is a separate required component, no longer a Camera field.
  • 0.19: Hdr moved from bevy_render to bevy_camera; screen-space specular transmission fields moved into a ScreenSpaceTransmission component.

Multiple cameras / ordering

  • Camera.order: isize — higher renders later (on top).
  • Every camera with is_active: true renders independently.
  • Overlay cameras need ClearColorConfig::None or they wipe the layer below.

Camera gotchas

  • Default far plane is 1000 units — anything beyond is silently culled.
  • Forward is -Z.
  • Viewport coordinates are physical pixels; cursor position is logical.

Coordinate system

Right-handed:

Axis Bevy GLTF models
Forward -Z (Transform::forward()) +Z (Transform::back())
Up +Y +Y
Right +X -X from camera perspective

GLTF fix: orient with look_to(-direction, Vec3::Y) or treat transform.back() as the model's visual forward.

0.18: GltfPlugin's use_model_forward_direction was replaced by convert_coordinates with rotate_scene_entity and rotate_meshes flags.

Meshes & materials

  • StandardMaterial needs normals; a mesh without them spawns fine and renders invisible. Programmatic meshes: .with_computed_flat_normals() (or insert ATTRIBUTE_NORMAL).
  • Back-face culling is on by default — wrong winding = invisible faces.
  • GLTF scenes spawn via scene labels ("model.glb#Scene0"), not the raw asset handle.
  • Visibility inherits: parents without Visibility/InheritedVisibility make children invisible — common with hand-built hierarchies around loaded scenes.
  • 0.18: mesh accessors became try_* returning Result<_, MeshAccessError>; MaterialPlugin fields (prepass_enabled, shadows_enabled) became Material trait methods.
  • 0.19: GLTF material loading returns GltfMaterial — use the #Material0/std label for a StandardMaterial. Assets::get_mut returns mutation-tracked AssetMut<A>. Scene/SceneRoot renamed to WorldAsset/WorldAssetRoot.

Custom material shaders — bind groups

Material bindings are @group(3) (0.18+), not @group(2):

Group Contents
0 view
1 globals/lights
2 mesh (storage buffer)
3 material (AsBindGroup)

@group(2) for material uniforms produces the confusing Storage class Storage doesn't match the shader Uniform error — group 2 is the mesh storage buffer.

Import: use bevy::shader::ShaderRef; (moved out of bevy::render::render_resource in 0.18).

Lighting

AmbientLight split (0.18):

  • GlobalAmbientLightresource, all cameras.
  • AmbientLightcomponent on a camera entity, per-camera.

Assets

  • Assets are not ready right after asset_server.load(). Gate on AssetEvent::LoadedWithDependencies or AssetServer::is_loaded_with_dependencies().
  • Cache poisoning: the server dedups by path. A load_with_settings that strips data (e.g. load_meshes = RenderAssetUsages::empty()) poisons the cache; a later load() of the same path returns the stripped asset — invisible models, zero errors. Never mix stripped and rendered loads of the same path in one process; stripped loads belong to headless tools only.
  • 0.18: LoadContext::path() returns AssetPath, not Path; image reinterpret_* return Result.
  • 0.19: AssetPath::resolve()/resolve_embed() take &AssetPath; the string-taking variants are resolve_str()/resolve_embed_str().