In the atmosphere example we can change between Earth and Mars, but the
size of the sun does not change. This somewhat bothered me from an
astronomical accuarcy.
This commit introduces a new `SunDisk::MARS` constant to accurately
represent the sun’s apparent size when viewed from Mars. The
implementation updates the documentation and adds the corresponding
constant with an angular size of ~21 arcminutes (0.00615 radians).
Key modifications:
- Added `SunDisk::MARS` constant in `directional_light.rs`
- Updated documentation to reference the new Mars preset
- Modified `atmosphere` example to switch sun disk alongside atmosphere
presets
I tested this manually.
---------
Co-authored-by: Máté Homolya <mate.homolya@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
`PbrNeutral` is poorly named as it implies it's a good default neutral
tonemapper when it really isn't. The docs also don't really describe how
it looks visually unlike the doc comments on the other tonemappers.
## Solution
Rename `PbrNeutral` to `KhronosPbrNeutral` to make it more clear that
it's specifically the Khronos Pbr Neutral tonemapper and not a generic
neutral pbr tonemapper, and improve the docs to better describe it and
essentially recommend against it.
# Objective
Fix#19101. (Kinda, this is not the exact solution proposed in that
issue, but it still shortens most of the examples)
## Solution
Most of the images created in examples are for use in render targets.
`Image::new_target_texture` is made for exactly that and significantly
shortens the image creation process.
## Testing
I tested all the examples I changed and they seem to work fine.
---
btw for some reason most of the examples use
`TextureFormat::Bgra8UnormSrgb` while the documentation for
`Image::new_target_texture` recommends `TextureFormat::Rgba8UnormSrgb`
for SDR images. What's up with that?
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
# Objective
- Allow rendering objects with `AlphaMode::Premultiplied` and
`AlphaMode::Add` while using Order Independent Transparency.
- Fixes#20373.
## Solution
- Store Premultiplied color in the OIT buffer and premultiply in the
case of `AlphaMode::Blend`, this shouldn't have any downsides since
blending was done with premultiplied alpha anyway.
- It might be useful to actively break the `oit_draw` signature by
adding a `premultiply` bool to ensure that shader authors are aware of
the changed meaning of the `color` argument.
## Testing
- These changes have been tested by running the modified
`order_independent_transparency` example, an older version of these
changes has been in use in a project i work on.
---
## Showcase
Here the Red Sphere uses `AlphaMode::Blend`, the Blue Sphere
`AlphaMode::Add` and the Green Sphere `AlphaMode::Premultiplied`.
<img width="1270" height="708" alt="bevy-oit-example"
src="https://github.com/user-attachments/assets/282d7083-458f-4ecd-b098-20b3539de796"
/>
---------
Signed-off-by: Schmarni <marnistromer@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
Expose the SSAO sampling radius as a public configuration option on
ScreenSpaceAmbientOcclusion.
Right now Bevy exposes SSAO quality and constant object thickness, but
the effect
radius is still hardcoded internally in the SSAO shader. That makes it
impossible for
users to tune the size of the occlusion effect from app code.
## Solution
Add a radius: f32 field to ScreenSpaceAmbientOcclusion and preserve
existing behavior
by defaulting it to the shader’s previous hardcoded value.
To wire that through cleanly, the SSAO render path now uploads a small
SSAO settings
uniform containing both radius and constant_object_thickness, and the
SSAO shader
reads settings.radius instead of a hardcoded constant.
The SSAO example was also updated to expose the new setting
interactively so the
feature is discoverable and easy to validate.
## Testing
I tested this by:
- Running cargo check -p bevy_pbr --quiet
- Building and running cargo build --example ssao
- Launching target/debug/examples/ssao and verifying the example starts
successfully
- Verifying that the new radius control can be adjusted interactively in
the example
with Left / Right
Parts that could use more testing:
- Visual validation across a wider range of scenes and camera scales
- Broader GPU/platform coverage, since I only tested locally
How reviewers can test this:
1. Run cargo build --example ssao
2. Launch target/debug/examples/ssao
3. Use Left / Right to decrease/increase SSAO radius
4. Confirm that larger values produce broader occlusion while the
default matches
existing behavior
Platform tested:
- Linux (EndeavourOS)
Platforms not tested:
- Windows
- macOS
- Web targets
# Objective
The keys for switching between earth and mars atmospheres in the
atmosphere example currently do nothing. This change restores the
functionality.
I'm guessing the issue is from the Atmosphere component changing from
using `Transform` to `GlobalTransform` directly in
`Atmosphere::set_default_transform`. The example is expecting to iterate
over a `Transform`, never finds one, and thus never changes the
atmosphere.
## Solution
Change the example to modify `GlobalTransform` instead of `Transform`.
It may be preferable to modify the example to insert a `Transform` in
set-up. Let me know if this is the case and i'll update the PR.
## Testing
- Tested by running the atmosphere example, hitting the 3 and 4 keys and
seeing sufficiently earth and mars like atmospheres, as well as messages
in the console.
This PR undoes the revert of #23115 that was done in #24252. As part of
doing so, it makes GPU-driven visibility range evaluation use the same
semantics, introduced in #24289, as CPU-driven visibility range
evaluation.
The first commit in this PR is the un-revert, and the second commit is
the change to use the new machinery #24289. This means that you can
simply review the second commit (which is very short).
To test, run the `visibility_range` example with `--no-cpu-culling`, and
use the WASD keys to move behind the flight helmet and zoom out, while
closely watching the shadow. Note that the shadow now properly reflects
the LOD of the model.
This reverts commit ebfbc3f5c8.
# Objective
- A third PR that fixes#23991
## Solution
- Reverts the commit that causes the issue.
- This can be considered if the other solutions (#24197 & #24133) I’ve
put up are not satisfactory, and we need more time to come up with a
good solution (i.e. post 0.19 rc) that:
- Implements PointLight and SpotLight shadow view behavior for gpu vis
range culling that is agreed upon and looks good
- Finalizes an appropriate way of sending this camera view world
position data to the gpu (Immediates vs ViewUniformBuffer or other
Uniform)
- Note: Meshes that are tagged with `NoCpuCulling` will **not** undergo
cpu visibility range culling. That was implemented in a separate PR:
https://github.com/bevyengine/bevy/pull/23107/changes#diff-fec33d34072b7b4be336571ceecf2c10fc9bafd8366c1b29531089b7e3ef621cL745-L748.
If you want to use `VisibilityRange` culling, you will still need to
have CPU culling enabled for the mesh.
## Testing
- `cargo run --example visibility_range` works normal now.
- Tested some other 3d examples make sure the pipeline is ok —
atmosphere, pccm
This is my first bevy PR, please tell me if I'm doing anything wrong.
# Objective
Contribute to #22695.
Showcase the preferred coding style in all examples.
## Solution
Replace Val:: constructors with the more ergonomic shorthand functions.
Change their float literals to integer literals if they are integral.
Exceptions:
- const contexts (the shorthand functions are not const)
- inside bsn! macros (these are new and presumably know what they are
doing)
- in testbed (these are not really examples)
- Val::ZERO (no helper function)
## Testing
Ran the changed examples before and after, except the library example
`widgets` where I just checked that it still builds.
## Context
There was PR #22765 that fixed the same thing but only in the UI
examples.
---------
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
# Objective
Remove the `new_with_` prefixes from the `TextLayout` constuctor
functions. Generally, the "new" part is redundant and "with" is used by
fluent APIs.
## Solution
Just delete the prefixes, shorten the names (all on `TextLayout`).
* `new_with_justify` -> `justify`
* `new_with_linebreak` -> `linebreak`
* `new_with_no_wrap` -> `no_wrap`
# Objective
WebGPU-only fix extracted from #22554 and #22555. Fixes#20459
## Solution
Fix normal + motion vector prepass by setting write mask to empty. This
is a firefox/wgpu bug that it's not validated
https://github.com/gfx-rs/wgpu/issues/9147, but chromium does.
Fix dof with bokeh mode. I guess it's `naga_oil` bug but I'm not sure.
## Testing
I tested dof and skybox examples on web.
# Objective
Temporary simple / suboptimal solution to #23822
It would be very nice to be able to define assets inline in BSN.
## Solution
- Add a new `HandleTemplate::Value` variant, which contains an
`Arc<Mutex<AssetOrHandle<T>>>`
- This template, when first applied, will lock the mutex, insert the
asset value as a new asset, cache the handle in the
`Arc<Mutex<HandleOrTemplate>` and return the handle. Future calls will
lock the mutex and return the cached handle.
- Add `asset_value(SOME_ASSET)` function to improve ergonomics.
- Port `3d_scene` to illustrate
Doing a lock on every spawn is obviously suboptimal. The long term plan
for "inline assets in BSN" is defined in #23822 and will not use this
locking approach.
# Objective
- In 0.18, we had 10 different functions that load assets (I'm not even
counting `load_folder`).
- In 0.19, we've even added `load_erased` - but it unfortunately doesn't
support all the features that the other variants support.
- We apparently needed `load_acquire_with_settings_override` which 1)
loads the asset, 2) uses the settings provided, 3) allows reading
unapproved asset paths, and 4) drops a guard once the load completes.
- That's fine if that's necessary. But we needed to create an explicit
variant for that.
- We need fewer load paths!
## Solution
- Create a builder.
- Store all these options dynamically instead of statically handling
each case.
- Have the caller choose a particular "kind" of load when they are
ready: `load`, `load_erased`, `load_untyped`, or `load_untyped_async`.
- I intentionally didn't provide a `load_async` or `load_erased_async`,
since those can be replicated using `load`/`load_erased` +
`AssetServer::wait_for_asset_id` to get the exact same effect.
I am also intentionally leaving `NestedLoader` untouched in this PR, but
a followup will duplicate this API for `NestedLoader`, which should make
it easier to understand.
Unlike the `NestedLoader` API, we aren't doing any type-state craziness,
so the docs are much more clear: users don't need to understand how
type-state stuff works, they just call the handful of methods on the
type. The "cost" here is we now need to be careful about including the
cross product of loads between static asset type, runtime asset type, or
dynamic asset type, crossed with deferred or async. In theory, if we
added more kinds on either side, we would need to expand this cross
product a lot. In practice though, it seems unlikely there will be any
more variants there. (maybe there could be a blocking variant? I don't
think this is a popular opinion though).
A big con here is some somewhat common calls are now more verbose.
Specifically, `asset_server.load_with_settings()` has become
`asset_server.load_builder().with_settings().load()`. I am not really
concerned about this though, since it really isn't that painful.
## Testing
- Tests all pass!
---
## Showcase
Now instead of:
```rust
asset_server.load_acquire_with_settings_override("some_path", |settings: &mut GltfLoaderSettings| { ... }, my_lock_guard);
```
You can instead do:
```rust
asset_server.load_builder()
.with_guard(my_lock_guard)
.with_settings(|settings: &mut GltfLoaderSettings| { ... })
.override_unapproved()
.load("some_path");
```
We also now cover more variants! For example, you can now load an asset
untyped with a guard, or with override_unapproved, etc.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- Partially addresses #23688.
- Prevents use of `Skybox::default()` from causing errors.
## Solution
`Skybox::default()` is problematic because it contains an `Image` that
is not a valid skybox. ~~This change removes the `Default`
implementation and instead provides a `new()` function which takes the
image as a parameter (and also the brightness, which is practically
required).~~ This change makes the `image` field optional so that the
default `None` renders nothing.
Things we could do instead of this:
* Make `Skybox` not implement `Default`. I am informed this is a bad
idea.
* Create a default cubemap image for `default()` to use.
## Testing
Ran the `skybox` and `irradiance_volumes` examples.
# Objective
Fixes https://github.com/bevyengine/bevy/issues/23526
## Solution
- Added UVs to `ConvexPolygonMeshBuilder`
- Whilst testing I noticed that normals were also not generated for the
mesh so i've added these as well.
## Testing
- Added `test_convex_polygon` to bevy_mesh which imitates a different
test but for regular polygons.
- Added `Extrusion<ConvexPolygon>` to the 3d_shapes example - you can
use this example to validate UVs and Normals are working as expected.
# Objective
**Anecdotal feedback:**
- no floating origin support for implementing large-scale worlds, forked
Bevy's atmosphere at
https://github.com/philpax/veldera/blob/main/crates/bevy_pbr_atmosphere_planet/NOTICE.md
- no custom up axis support, resorted to using a custom sky shader for
flight simulator with Z up coordinate system. Bevy's atmosphere appears
tilted at a 90 degree angle with no way of changing it.
## Solution
- Atmosphere component can be spawned stand-alone
- AtmosphereSettings remains on camera
- A closest-to-camera heuristic is used to pick the primary atmosphere
to render. Deliberately no multi-atmosphere support to keep the scope of
this PR small and self contained. See
https://github.com/mate-h/bevy/pull/19 at an attempt.
- `scene_units_to_m` removed in favor of using `Transform`
- Z up now possible by offsetting the viewer position to the equator
- Floating origin systems now possible
- Simplify the `AtmosphereBuffer` / `AtmosphereData` structs to just use
the plain extracted `GpuAtmosphere` struct. this reduces the complexity
of the struct in the mesh view bindings. Since atmosphere settings is
coupled with the rendering pipeline of the atmosphere this makes sense
architecturally.
- We no longer hard code the offset to the north pole from the planet
center in places.
**Why not multi atmosphere:**
The atmosphere uses multiple LUTs (lookup textures) to accelerate the
rendering performance. Some of them are not view dependent:
- Transmittance LUT
- Multiple scattering LUT
- Scattering / density LUTs
These can be coupled and rendered for each atmosphere individually.
However the remainder of the pipeline is view dependent:
- Aerial View LUT
- Sky View LUT
- Render Sky pass
In raymarched rendering mode, these LUTs can be skipped and only the
render sky pass runs sampling on all of the atmospheres with a raymarch
in screen space.
Further, the Sky View LUT uses a local reference frame to concentrate
texel density along the horizon's local up axis. This in turn means it's
coupled with both a _specific_ atmosphere's local coordinates as well as
the view's transform matrix. We cannot consider rendering both
atmospheres into a single LUT for this reason. So it has to be unique
for each pair of (view, atmosphere). Given two views and two atmospheres
we would need 4 of these Sky View LUTs and at some point, raymarched
rendering will become the less expensive option.
Lastly the Render Sky pass needs to happen once per view, we cannot
realistically composite them in sequence with simple dual-source
blending as we do with the scene, this would result in incorrect
scattering integration. This in turn means we need to bind ALL of the
luts calculated previously so a single render sky pass and render aerial
view lut - perhaps making use of array textures. Rely on unified
volumetric ingegration in the raymarching loop: for each light,for each
atmosphere, attenuate inscattering and transmittance along the path
integral. It is suffice to say this change is overall _too complex_ for
the time being and is likely the reason Unreal Engine also do not
support multiple atmospheres. For context: our research is based heavily
on Sebastian Hillarie's work, one of the Unreal graphics engineers.
That being said about multiple atmospheres - I am thinking of this PR as
a segway into unified volumetrics in Bevy. that is: Render the FogVolume
and Atmosphere in a single pass! Making use of the frustum aligned voxel
grid "froxel" approach to accelerate the rendering. This would
drastically increase the performance for scenes wanting to make use of
both the atmosphere and local fog volumes.
## Testing
- Ran the `examples/3d/atmosphere.rs` example.
---
## Showcase
(example screenshot unchanged compared to main.)
```rs
// Spawn earth atmosphere
commands.spawn(Atmosphere::earth(earth_medium));
commands.spawn((
Camera3d::default(),
// Can be adjusted to change the rendering quality
AtmosphereSettings::default(),
));
```
---------
Co-authored-by: Emerson Coskey <emerson@coskey.dev>
Part 2 of #23619
In **Bevy 0.19** we are landing a subset of Bevy's Next Generation Scene
system (often known as BSN), which now lives in the `bevy_scene` /
`bevy::scene` crate. However the old `bevy_scene` system still needs to
stick around for a bit longer, as it provides some features that Bevy's
Next Generation Scene system doesn't (yet!):
1. It is not _yet_ possible to write a World _to_ BSN, so the old system
is still necessary for "round trip World serialization".
2. The GLTF scene loader has not yet been ported to BSN, so the old
system is still necessary to spawn GLTF scenes in Bevy.
For this reason, we have renamed the old `bevy_scene` crate to
`bevy_world_serialization`. If you were referencing `bevy_scene::*` or
`bevy::scene::*` types, rename those paths to
`bevy_world_serialization::*` and `bevy::world_serialization::*`
respectively.
Additionally, to avoid confusion / conflicts with the new scene system,
all "scene" terminology / types have been reframed as "world
serialization":
- `Scene` -> `WorldAsset` (as this was always just a World wrapper)
- `SceneRoot` -> `WorldAssetRoot`
- `DynamicScene` -> `DynamicWorld`
- `DynamicScene::from_scene` -> `DynamicWorld::from_world_asset`
- `DynamicSceneBuilder` -> `DynamicWorldBuilder`
- `DynamicSceneRoot` -> `DynamicWorldRoot`
- `SceneInstanceReady` -> `WorldInstanceReady`
- `SceneLoader` -> `WorldAssetLoader`
- `ScenePlugin` -> `WorldSerializationPlugin`
- `SceneRootTemplate` -> `WorldAssetRootTemplate`
- `SceneSpawner` -> `WorldInstanceSpawner`
- `SceneFilter` -> `WorldFilter`
- `SceneLoaderError` -> `WorldAssetLoaderError`
- `SceneSpawnError` -> `WorldInstanceSpawnError`
Note that I went with `bevy_world_serialization` over
`bevy_ecs_serialization`, as that is what all of the internal features
described themselves as. I think it is both more specific and does a
better job of making itself decoupled from `bevy_ecs` proper.
# Objective
Fixes#19148.
Should fix#19177, too, because wgpu webgl2 just doesn't support
sampling depth or creating multisampled texture with `TEXTURE_BINDING`.
## Solution
`feature="webgl"` and `feature="webgpu"` can enable together(webgpu
overrides webgl), so we should bind multisampled depth texture even
`webgl` feature is enabled.
## Testing
Run `decal` example
# Objective
Quite a few comments in the rendering code link to the filament spec.
The address changed from https://google.github.io/filament/Filament.html
to https://google.github.io/filament/Filament.md.html at some point.
There is an automated redirect set up, but fragment identifiers are
dropped during the redirect so section links no longer work.
## Solution
Find and replace :)
I also fixed two of the links that were broken.
* Handle non-metals correctly (specular lobe _layered_ over diffuse,
which means two fresnel weighings at the boundary for enter and exit
rays)
* Throw away invalid GGX VNDF samples
* Make sure mirror lobe evaluates to 0 and INF for eval and pdf when not
aligned with mirror direction, and make balance/power heuristic MIS code
handle INF
* Pathtracer now picks a lobe based on fresnel when importance sampling
the BRDF
* Stop taking specular tint into account (making reflectance now a f32
instead of vec3), as it does not work with deferred rendering
* Fresnel is no longer imported from bevy, as it does this weird f90
thing for ambient occlusion
We're still not passing the white furnace test, but oh well.
Many thanks to @devshgraphicsprogramming for the help!
<img width="3206" height="1875" alt="image"
src="https://github.com/user-attachments/assets/e500cc04-9e3e-48bb-9650-0ef282018d79"
/>
<img width="3206" height="1875" alt="image"
src="https://github.com/user-attachments/assets/eb7a1789-a470-43f2-b6ff-4a78b8c269d6"
/>
# Objective
Depends on #22187. Fixes#17794. ~For platform consistency I think it’s
reasonable to enable primitive restart by default.~ wgpu will force
primitive restart after https://github.com/gfx-rs/wgpu/pull/8850.
## Solution
Add index format to MeshPipelineKey, replace
`MeshPipelineKey::from_primitive_topology` with
`MeshPipelineKey::from_primitive_topology_and_index`, and enable
`strip_index_format` in render pipeline.
## Testing
I modified the `lines` example to demonstrate primitive restart.
## Showcase
<details>
<summary>Click to view showcase</summary>
<img width="1550" height="852" alt="屏幕截图_20251218_210849"
src="https://github.com/user-attachments/assets/a7c41943-f22b-415a-8132-98455f21735d"
/>
</details>
People generally expect GPU-driven renderers to perform LOD selection on
the GPU. Visibility ranges constitute our LOD feature, but they're
currently checked on the CPU. This commit moves that to the GPU and
avoids checking on CPU if `NoCpuCulling` is present.
Note that, even with this patch, LOD buffers (i.e. the on-GPU buffers
that store the visibility ranges) are still built afresh on the CPU
every frame. This will probably be a bottleneck eventually, but that's
for a follow-up.
A `--no-cpu-culling` feature has been added to the `visibility_range`
example, for testing.
# Objective
- Fix meshlets
## Solution
- Fix loop conditions being inverted (typo bug)
- Fix instance unpack logic being flipped around
- Fix non-existent shaderdef usage to actually match FIRST_CULLING_PASS
-> MESHLET_FIRST_CULLING_PASS (it would be interesting to come up with a
way to fix this class of bug with wesl, but im not really sure how)
- Fix the view projections being flipped inside that shaderdef that
didnt exist
- FIX A HORRIBLE METAL BUG FROM HELL 😭😱😫😵😵💫😹🫣😬🤬👺🫠 this cost me a
significant chunk of sanity please appreciate it
https://github.com/gfx-rs/wgpu/pull/9185 (hopefully lands in wgpu 29 and
then its in for bevy 19 once we migrate)
## Testing
- also adds the BunnyWiggler to be able to verify moving instances are
correctly handled with prev transform logic.
- run meshlet example
- It finally works fine again! After 2 releases of it being broken!
# Objective
I noticed that changing the `AlphaMode::Mask` threshold value had no
effect on my material. Looking into the PBR shaders, I found out it was
only reading the `alpha_cutoff` for lit materials, alongside
lighting-related properties.
For unlit materials, it was fallbacking to the [0.5
default](https://github.com/bevyengine/bevy/blob/main/crates/bevy_pbr/src/render/pbr_types.wgsl#L86).
## Solution
Moved the `alpha_cutoff` read out of the lit-only block.
## Testing
The transparency_3d example has an unlit sphere with `AlphaMode::Mask`.
I changed its threshold from 0.5 to 0.1 so the difference is visible
(see below).
_I wasn't sure if I should include the example changes, but I think it
might be good to show off the Mask mode in general._
---
## Showcase
Both examples below have the left unlit sphere with
`AlphaMose::Mask(0.1)`. Before the fix, the unlit sphere appears at the
same time at the lit sphere (because it's fallbacking to 0.5). After the
fix, the unlit sphere appears much earlier.
**Before:**
https://github.com/user-attachments/assets/6a8cb76f-507f-45e2-aa79-72ab7e019760
**After:**
https://github.com/user-attachments/assets/875d6587-40ae-4eda-b245-eda1e94656ce
---------
Co-authored-by: François Mockers <francois.mockers@vleue.com>
# Objective
- The atmosphere example had no reflections since PR #22379 , I wanted
to fix this
- Got feedback from pcwalton@ that the spheres need to be removed. These
are no longer needed to test generated environment map lighting since
that also shows up in the water.
- Placed the camera lower such that the volumetric lighting renders
correctly and you can see more of the reflection.
## Solution
- Updated atmosphere example.
## Testing
- Ran atmosphere example with the proper feature flags
(bluenoise_texture)
---
## Showcase
Before: no reflection, spheres, no "orange glow" in the volumetric fog
towards the top face of the fog volume cuboid. This is likely a bug in
the fog volume rendering depending on the camera angle.
<img width="1280" height="721" alt="Screenshot 2026-02-23 at 10 29
34 AM"
src="https://github.com/user-attachments/assets/4322e38a-7957-48ad-84fd-852ba8c712a8"
/>
After
<img width="1281" height="723" alt="Screenshot 2026-02-23 at 10 29
58 AM"
src="https://github.com/user-attachments/assets/f6777459-b555-4714-a1d0-2df4297fb8f1"
/>
# Objective
- `glam`, `hexasphere` & `rand` have released their latest versions,
update Bevy to support them.
## Solution
- The above have been updated to their compatible versions. `rand_distr`
updated as well to match `rand` v0.10 support.
- `rand_chacha` is soft deprecated and no longer used by `rand`, so its
usage has been changed to `chacha20` to match `rand` dep tree.
- `uuid` is in the process of updating to `getrandom` v0.4, which `rand`
v0.10 supports. This PR remains in draft until a new `uuid` release hits
crates.io.
- `RngCore` is now `Rng`, and `Rng` is now `RngExt`, so this required
updating across many files.
- `choose_multiple` method is deprecated, changed to `sample`.
## Testing
- Chase all compiler errors, since this should not regress any already
existing behaviour.
- This must pass CI without regressions.
## Additional Notes
`getrandom` v0.4 doesn't add anything new for Web WASM support, so the
same `wasm_js` feature is used.
Adds support for line width (stroke) and quad topology for wireframes.
The triangle line fast path stays the same, but we add a new "wide" draw
function path that uses a specialized vertex shader in order to do the
following via vertex pulling from the appropriate mesh slab:
- Compute screen-space altitudes for our edges.
- Find and suppress diagonals when quad topology is enabled.
Doing this in the vertex shader allows us to avoid needing to de-dupe on
the CPU or spend additional bandwidth uploading a mesh copy at the
expense of a bit of extra shading.
Right now, we have to break batches on the mesh when in the "wide" path,
meaning we don't get full advantage of MDI, but this is mostly because
we don't have a great way to support per-draw data. This could be fixed
in the future.
## Examples:
<img width="1924" height="1126" alt="image"
src="https://github.com/user-attachments/assets/cd73eb9d-f215-413d-ba28-bddc6bd980b0"
/>
<img width="1924" height="1127" alt="Screenshot 2026-02-16 160350"
src="https://github.com/user-attachments/assets/901f5dbb-c900-43b3-bd6b-ce5c9eece82f"
/>
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: atlv <email@atlasdostal.com>
# Objective
- Fixes#16751
## Solution
- `Assets::get_mut` now returns a wrapper `AssetMut` type instead of
`&mut impl Asset`.
- `AssetMut` implements `Deref` and `DerefMut`.
- `DerefMut` marks assets as changed.
- when dropped `AssetMut` will add `AssetEvent::Modified` event to a
queue only in case asset was marked as changed.
## Testing
- Did you test these changes? If so, how?
- No unit tests were added, change is pretty straightforward.
- Test project: https://github.com/MatrixDev/bevy-feature-16751-test.
- With change: ~100 fps.
- Without change: ~15 fps.
- Are there any parts that need more testing?
- I don't really see how this can break anything or add a measurable
overhead.
- `AssetEvent::Modified` will now be sent after the asset was modified
instead of before. It should not affect anything but still worth noting.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- Have a big amount of entities that constantly update their materials.
- Properties of those materials should be animated in a stepped maned
(like changing color every 0.1 seconds).
- Update material only if value has actually changed:
```rust
if material.base_color != new_color {
material.base_color = new_color;
}
```
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
- tested on macos (Mackbook M1)
- not a platform-specific issue
PS: This is my first PR, so please don’t judge.
# Objective
- Recover from rendering errors.
- Another step towards render recovery after #22714#22759 and #16481
## Solution
- Use `wgpu::Device::set_device_lost_callback` and
`wgpu::Device::on_uncaptured_error` to listen for errors.
- Add a state machine for the renderer
- Update it on error
- Add a `RenderErrorHandler` to let users specify behavior on error by
returning a specific `RenderErrorPolicy`
- This lets us for example ignore validation errors, delete responsible
entities, or reload the renderer if the device was lost.
## Testing
- #22757 with any of
```rs
.insert_resource(bevy_render::error_handler::RenderErrorHandler(|_, _, _| {
bevy_render::error_handler::RenderErrorPolicy::StopRendering
}))
```
```rs
.insert_resource(bevy_render::error_handler::RenderErrorHandler(|_, _, _| {
bevy_render::error_handler::RenderErrorPolicy::Recover(default())
}))
```
Note: no release note yet, as recovery does not exactly work well: this
PR gets us to the point of being able to care about it, but we currently
instantly crash on recover due to gpu resources not existing anymore. We
need to build more resilience before publicizing imo.
---------
Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com>
Currently, if a fragment overlaps multiple reflection probes and/or
irradiance volumes, Bevy arbitrarily chooses one to provide diffuse
and/or specular light. This is unsightly. The standard approach is to
accumulate radiance and irradiance as a weighted sum. In most engines,
light probes have an artist-controllable *falloff* range, which causes
the weight of each probe to diminish gradually from the center of the
probe.
This PR implements both falloff and blending for light probes.
Reflection probes and irradiance volumes are blended using a weighted
sum. In the case of reflection probes, if the weights sum to less than
1.0, and an environment map is present on the camera, than the
environment map receives the remaining weight necessary to bring the
total weight up to 1.0. This is useful for reflection probes that
correspond to building interiors, to allow smooth transitions between
the indoor building and an exterior environment map when exiting the
building.
Falloff is specified as a fraction of the *interior* of each light probe
that applies gradual falloff, instead of specifying a distance *outside*
the light probe over which the influence diminishes. (See the
documentation comments in `LightProbe` for more detail.) The reason why
I chose to do it this way is that the voxel contents of an irradiance
volume would be ill-defined within the falloff range otherwise. Clamping
to the edge of the 3D voxel cube inside the falloff region (i.e.
extending the edge voxels out) is likely to be incorrect, and extending
the voxel region to encompass the falloff range plus the interior range
would complicate the calculations in the performance-critical PBR
shader.
A new example, `light_probe_blending`, has been added. This example
shows a reflective sphere that moves between two rooms, each of which
has a reflection probe with a falloff range, so the sphere smoothly
blends between the two. The user can pan and zoom the camera.
<img width="2564" height="1500" alt="Screenshot 2026-01-25 215214"
src="https://github.com/user-attachments/assets/67098769-8082-47c3-ae96-4124732b73f6"
/>
# Objective
Add responsive font sizes supporting rem and viewport units to
`bevy_text` with minimal changes to the APIs and systems.
## Solution
Introduce a new `FontSize` enum:
```rust
pub enum FontSize {
/// Font Size in logical pixels.
Px(f32),
/// Font size as a percentage of the viewport width.
Vw(f32),
/// Font size as a percentage of the viewport height.
Vh(f32),
/// Font size as a percentage of the smaller of the viewport width and height.
VMin(f32),
/// Font size as a percentage of the larger of the viewport width and height.
VMax(f32),
/// Font Size relative to the value of the `RemSize` resource.
Rem(f32),
}
```
This replaces the `f32` value of `TextFont`'s `font_size` field.
The viewport variants work the same way as their respective `Val`
counterparts.
`Rem` values are multiplied by the value of the `RemSize` resource
(which newtypes an `f32`).
`FontSize` provides an `eval` method that takes a logical viewport size
and rem base size and returns an `f32` logical font size. The resolved
logical font size is then written into the `Attributes` passed to Cosmic
Text by `TextPipeline::update_buffer`.
Any text implementation using `bevy_text` must now provide viewport and
rem base values when calling `TextPipeline::update_buffer` or
`create_measure`.
`Text2d` uses the size of the primary window to resolve viewport values
(or `Vec2::splat(1000)` if no primary window is found). This is a
deliberate compromise, a single `Text2d` can be rendered to multiple
viewports using `RenderLayers`, so it's difficult to find a rule for
which viewport size should be chosen.
### Change detection
`ComputedTextBlock` has two new fields: `uses_viewport_sizes` and
`uses_rem_sizes`, which are set to true in `TextPipeline::update_buffer`
iff any text section in the block uses viewport or rem font sizes,
respectively.
The `ComputedTextBlock::needs_rerender` method has been modified to take
take two bool parameters:
```rust
pub fn needs_rerender(
&self,
is_viewport_size_changed: bool,
is_rem_size_changed: bool,
) -> bool {
self.needs_rerender
|| (is_viewport_size_changed && self.uses_viewport_sizes)
|| (is_rem_size_changed && self.uses_rem_sizes)
}
```
This ensures that text reupdates will also be scheduled if one of the text section's uses a viewport font size and the local viewport size changed, or if one of the text section's uses a rem font size and the rem size changed.
#### Limitations
There are some limitations because we don't have any sort of font style inheritance yet:
* "rem" units aren't proper rem units, and just based on the value of a resource.
* "em" units are resolved based on inherited font size, so can't be implemented without inheritance support.
#### Notes
* This PR is quite small and not very technical. Reviewers don't need to be especially familiar with `bevy_text`. Most of the changes are to the examples.
* We could consider using `Val` instead of `FontSize`, then we could use `Val`'s constructor functions which would be much nicer, but some variants might not have sensible interpretations in both UI and Text2d contexts. Also we'd have to make `Val` accessible to `bevy_text`.
## Testing
The changes to the text systems are relatively trivial and easy to understand. I already added a minor change to the `text` example to use `Vh` font size for the "hello bevy" text in the bottom right corner. If you change the size of the window, you should see the text change size in response. The text initially flickers before it updates because of some unrelated asset/image changes that mean that font textures aren't ready until the frame after the text update that changes the font size.
Most of the example migrations were automated using regular expressions, and there are bound to be mistakes in those changes. It's infeasible to check every single example thoroughly, but it's early enough in the release cycle that I don't think we should be too worried if a few bugs slip in.
---------
Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
# render-graph-as-systems
> [!NOTE]
> Remember to check hide whitespace in diff view options when reviewing
this PR
This PR removes the `RenderGraph` in favor of using systems.
## Motivation
The `RenderGraph` API was originally created when the ECS was
significantly more immature. It was also created with the intention of
supporting an input/output based slot system for managing resources that
has never been used. While resource management is an important potential
use of a render graph, current rendering code doesn't make use of any
patterns relating to it.
Since the ECS has improved, the functionality of `Schedule` has
basically become co-extensive with what the `RenderGraph` API is doing,
i.e. ordering bits of system-like logic relative to one another and
executing them in a big chunk. Additionally, while there's still desire
for more advanced techniques like resource management in the graph, it's
desirable to implement those in ECS terms rather than creating more
`RenderGraph` specific abstraction.
In short, this sets us up to iterate on a more ECS based approach, while
deleting ~3k lines of mostly unused code.
## Implementation
At a high level: We use `Schedule` as our "sub-graph." Rather than
running the graph, we run a schedule. Systems can be ordered relative to
one another.
The render system uses a `RenderGraph` schedule to define the "root" of
the graph. `core_pipeline` adds a `camera_driver` system that runs the
per-camera schedules. This top level schedule provides an extension
point for apps that may want to do custom rendering, or non-camera
rendering.
### `CurrentView` / `ViewQuery`
When running schedules per-camera in the `camera_driver` system, we
insert a `CurrentView` resource that's used to mark the currently
iterating view. We also add a new param `ViewQuery` that internally uses
this resource to execute the query and skip the system if it doesn't
match as a convenience.
### `RenderContext`
The `RenderContext` is now a system param that wraps a `Deferred` for
tracking the state of the current command encoder and queued buffers.
### `SystemBuffer`
We use an system buffer impl to track command encoders in the render
context and rely on apply deferred in order to encode them all.
Currently, this encodes them in series. There are likely opportunities
here to make this more efficient.
## Benchmarks
### Bistro
<img width="1635" height="825" alt="Screenshot 2026-01-15 at 7 57 40 PM"
src="https://github.com/user-attachments/assets/8e55a959-89a3-4947-bfc5-c04780f82e7b"
/>
### Caldera
<img width="1631" height="828" alt="Screenshot 2026-01-15 at 8 13 06 PM"
src="https://github.com/user-attachments/assets/e7e8ae0d-41c3-430f-8b4d-9099b3d922a0"
/>
## Future steps
There are a number of exciting potential changes that could follow here:
- We can explore adding something like a read-only schedule to pick up
some more potential parallelism in graph execution.
- We can use more things like run conditions in order to prevent systems
from running at all in the first place.
- We can explore things like automating resource creation via system
params.
## TODO:
- [x] Make sure 100% of everything still works.
- [x] Benchmark to make sure we don't regress performance
- [x] Re-add docs
---------
Co-authored-by: atlas dostal <rodol@rivalrebels.com>
# Objective
- Another day, another step towards scene description outside of render
crates
## Solution
- Move Atmosphere to bevy_light.
## Testing
- atmosphere example looks good.
---------
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective
- It makes no sense for bevy_camera::Camera3d to talk about transmission
quality settings
- transmission is not a core thing its a purely pbr thing, why is in
bevy_core_pipelines
- the implementation is generally scattered all over the place
- ScreenSpaceTransmissionQuality is a resource for no reason at all
## Solution
- split out a struct for transmission stuff
- consolidate stuff in bevy_pbr
- make ScreenSpaceTransmissionQuality not a resource
## Testing
transmission example looks good
# Objective
- Skybox is a main world component for scene definition, it shouldnt be
in a rendering crate
## Solution
- Move it to bevy_light, alongside EnvironmentMapLight component sibling
which lets it influence lighting.
## Testing
- skybox example works
# Objective
- whether a Camera needs to draw Hdr content or not is an aspect of
scene description
- as such, it should live in a non-rendering crate
## Solution
- move it to bevy_camera
- dont extract it to the render world anymore. audited all usages of it
and none are in the render world queries, instead `ExtractedCamera::hdr`
is used.
## Testing
- manual spot check of a few examples
- example runner regression test
Timestamp queries have never worked for DLSS because I setup them up
wrong when I initially wrote the code. This fixes it (and adds DLSS-RR
time to the Solari example).
Basically we need to put the timestamp start on the command buffer
before DLSS, and then the timestamp end on the command buffer right
after DLSS.
Debug groups had to be removed because I can't use them across different
command encoders.
With #22603 landed, all known issues that could cause Bevy to cull
meshes that shouldn't have been culled are fixed, so there now seems to
be consensus that we can remove occlusion culling from the
`experimental` namespace. This patch does that (and in fact removes the
`experimental` module from `bevy_render` entirely, as it's now empty).
# Objective
- Adopts and closes#22288
## Solution
- change the example to make it easier to tell if the reflection is
actually matching
- fix the assets because the reflection doesnt actually match
- throw the assets into the asset repo
https://github.com/bevyengine/bevy_asset_files/pull/7
## Testing
- running the example
---------
Co-authored-by: Patrick Walton <pcwalton@mimiga.net>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>