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
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:
RenderTargetis a separate required component, no longer aCamerafield. - 0.19:
Hdrmoved frombevy_rendertobevy_camera; screen-space specular transmission fields moved into aScreenSpaceTransmissioncomponent.
Multiple cameras / ordering
Camera.order: isize— higher renders later (on top).- Every camera with
is_active: truerenders independently. - Overlay cameras need
ClearColorConfig::Noneor 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
StandardMaterialneeds normals; a mesh without them spawns fine and renders invisible. Programmatic meshes:.with_computed_flat_normals()(or insertATTRIBUTE_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/InheritedVisibilitymake children invisible — common with hand-built hierarchies around loaded scenes. - 0.18: mesh accessors became
try_*returningResult<_, MeshAccessError>;MaterialPluginfields (prepass_enabled,shadows_enabled) becameMaterialtrait methods. - 0.19: GLTF material loading returns
GltfMaterial— use the#Material0/stdlabel for aStandardMaterial.Assets::get_mutreturns mutation-trackedAssetMut<A>.Scene/SceneRootrenamed toWorldAsset/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 onAssetEvent::LoadedWithDependenciesorAssetServer::is_loaded_with_dependencies(). - Cache poisoning: the server dedups by path. A
load_with_settingsthat strips data (e.g.load_meshes = RenderAssetUsages::empty()) poisons the cache; a laterload()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()returnsAssetPath, notPath; imagereinterpret_*returnResult. - 0.19:
AssetPath::resolve()/resolve_embed()take&AssetPath; the string-taking variants areresolve_str()/resolve_embed_str().