# 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`. `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): - `GlobalAmbientLight` — **resource**, all cameras. - `AmbientLight` — **component** 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()`.