-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add UI Materials #9506
Add UI Materials #9506
Conversation
Adds a new `MaterialNodeBundle` that uses the new `UiMaterial` to render Nodes with a certain Shader and Parameters
Welcome, new contributor! Please make sure you've read our contributing guide and we look forward to reviewing your pull request shortly ✨ |
Could you add a simple example showing how to use this? I can figure it out fairly easily but I believe this feature deserves a standalone example. |
I just added a simple example showcasing the feature. (its called Next up I do some renaming and documenting |
Looks really good. Maybe the material module would be better split up, with |
I did now remove the usage of |
@@ -326,3 +326,49 @@ impl Default for ButtonBundle { | |||
} | |||
} | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah noticed a snag, ideally we want some sort of MaterialImageBundle
as well:
/// A Ui Node that renders a shader
#[derive(Bundle, Debug)]
pub struct MaterialImageBundle<M: UiMaterial> {
/// Describes the logical size of the node
pub node: Node,
/// Styles which control the layout (size and position) of the node and it's children
/// In some cases these styles also affect how the node drawn/painted.
pub style: Style,
/// The Material of the Node.
pub material: Handle<M>,
/// Whether this node should block interaction with lower nodes
pub focus_policy: FocusPolicy,
/// The transform of the node
///
/// This field is automatically managed by the UI layout system.
/// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component.
pub transform: Transform,
/// The global transform of the node
///
/// This field is automatically managed by the UI layout system.
/// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component.
pub global_transform: GlobalTransform,
/// Describes the visibility properties of the node
pub visibility: Visibility,
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
pub computed_visibility: ComputedVisibility,
/// Indicates the depth at which the node should appear in the UI
pub z_index: ZIndex,
/// The size of the image in pixels
///
/// This field is set automatically
pub image_size: UiImageSize,
/// The calculated size based on the given image
pub content_size: ContentSize,
}
impl<M: UiMaterial> Default for MaterialImageBundle<M> {
fn default() -> Self {
Self {
node: Default::default(),
style: Default::default(),
material: Default::default(),
focus_policy: Default::default(),
transform: Default::default(),
global_transform: Default::default(),
visibility: Default::default(),
computed_visibility: Default::default(),
z_index: Default::default(),
content_size: Default::default(),
}
}
}
The ContentSize
component contains a measure func that is used to negotiate with the layout algorithm to find an optimal size for a node's content given that content's properties (like its aspect ratio and pixel density) and the space constraints of the UI layout.
ImageBundle
has a matching system update_image_content_size_system
that retrieves the size of the image from its texture asset and sets a measure func that preserves its aspect ratio. But we can't do that here because UiMaterial
doesn't expose its texture asset publically (if it has one).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be fine not to worry about measurefuncs for this PR though, the implementation looks great, really nice and clean.
It's not essential as users can create their own system to add a measurefunc, which isn't very hard. We do need a custom measurefunc example, it would make sense to base one around a UiMaterial
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great suggestion, I agree with you there, I wasn't aware of ContentSize
I'd like to get this PR done and would be happy to take on the MaterialImageBundle
as a followup once it is ready
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn’t the image just be a member of the material type? @ickshonpe as well as other data needed - the image size.
due to this change the `UiMaterialPipeline` also has to use a custom shader.
Co-authored-by: ickshonpe <[email protected]>
I did Change some of the names like @ickshonpe suggested. I am thinking that it would be a good addition to add the bevy_render::globals into the default shader bindings. |
Another problem this time with batching and draw ordering, I modified the use bevy::prelude::*;
use bevy_internal::{
reflect::{TypePath, TypeUuid},
render::render_resource::*,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(UiMaterialPlugin::<CustomUiMaterial>::default())
.add_systems(Startup, setup)
.add_systems(Update, update)
.run();
}
fn update(time: Res<Time>, mut ui_materials: ResMut<Assets<CustomUiMaterial>>) {
for (_, material) in ui_materials.iter_mut() {
material.fill_amount = time.elapsed_seconds() % 1.0;
let new_color = Color::WHITE;
material.color = new_color.into();
}
}
fn setup(mut commands: Commands, mut ui_materials: ResMut<Assets<CustomUiMaterial>>) {
// Camera so we can see UI
commands.spawn(Camera2dBundle::default());
commands
.spawn(NodeBundle {
style: Style {
width: Val::Percent(100.0),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
background_color: Color::BLACK.into(),
..default()
})
.with_children(|parent| {
parent.spawn(NodeBundle {
style: Style {
width: Val::Px(500.0),
height: Val::Px(500.0),
border: UiRect::all(Val::Px(10.)),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..Default::default()
},
border_color: BorderColor(Color::WHITE),
..Default::default()
}).with_children(|parent| {
parent.spawn(NodeBundle {
style: Style {
position_type: PositionType::Absolute,
width: Val::Px(500.0),
height: Val::Px(25.0),
..Default::default()
},
background_color: BackgroundColor(Color::GREEN),
..Default::default()
});
parent.spawn(MaterialNodeBundle {
style: Style {
position_type: PositionType::Absolute,
width: Val::Px(250.0),
height: Val::Px(250.0),
..default()
},
material: ui_materials.add(CustomUiMaterial {
fill_amount: 0.0,
color: Color::WHITE.into(),
}),
..default()
});
parent.spawn(NodeBundle {
style: Style {
position_type: PositionType::Absolute,
width: Val::Px(25.0),
height: Val::Px(500.0),
..Default::default()
},
background_color: BackgroundColor(Color::RED),
..Default::default()
});
});
});
}
#[derive(AsBindGroup, TypeUuid, TypePath, Debug, Clone)]
#[uuid = "7b5569c8-36d4-4c9d-acb7-d1754b385ab1"]
struct CustomUiMaterial {
#[uniform(0)]
fill_amount: f32,
#[uniform(0)]
color: Vec4,
}
impl UiMaterial for CustomUiMaterial {
fn fragment_shader() -> ShaderRef {
"shaders/circle_shader.wgsl".into()
}
} The circular section should be drawn in front of the green bar and behind the red bar, but it's drawn on top instead: I managed to force it to render correctly by disabling batching completely and merging #9062 which is a fix for some UI draw order bugs. The issue is that
|
I am not sure if there is anything that can be done here. Disabling all batching is sub-optimal to say the least. |
I’ll resolve these as soon as possible (in about one hour or so) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks generally fine, although I'm not familiar with any of the UI stuff. Some small doc nits.
I'm less sleep-deprived now and feeling comfortable re-reviewing this. I'm happy with the quality of code, the docs are great, and this is useful abstraction. Merging for 0.12 :) |
Fix branding inconsistencies don't Implement `Display` for `Val` (bevyengine#10345) - Revert bevyengine#10296 - Avoid implementing `Display` without a justification - `Display` implementation is a guarantee without a direct use, takes additional time to compile and require work to maintain - `Debug`, `Reflect` or `Serialize` should cover all needs Combine visibility queries in check_visibility_system (bevyengine#10196) Alternative to bevyengine#7310 Implemented the suggestion from bevyengine#7310 (comment) I am guessing that these were originally split as an optimization, but I am not sure since I believe the original author of the code is the one speculating about combining them up there. I ran three benchmarks to compare main, this PR, and the approach from ([updated](https://github.com/rparrett/bevy/commits/rebased-parallel-check-visibility) to the same commit on main). This seems to perform slightly better than main in scenarios where most entities have AABBs, and a bit worse when they don't (`many_lights`). That seems to make sense to me. Either way, the difference is ~-20 microseconds in the more common scenarios or ~+100 microseconds in the less common scenario. I would speculate that this might perform **very slightly** worse in single-threaded scenarios. Benches were run in release mode for 2000 frames while capturing a trace with tracy. | bench | commit | check_visibility_system mean μs | | -- | -- | -- | | many_cubes | main | 929.5 | | many_cubes | this | 914.0 | | many_cubes | 7310 | 1003.5 | | | | | many_foxes | main | 191.6 | | many_foxes | this | 173.2 | | many_foxes | 7310 | 167.9 | | | | | many_lights | main | 619.3 | | many_lights | this | 703.7 | | many_lights | 7310 | 842.5 | Technically this behaves slightly differently -- prior to this PR, view visibility was determined even for entities without `GlobalTransform`. I don't think this has any practical impact though. IMO, I don't think we need to do this. But I opened a PR because it seemed like the handiest way to share the code / benchmarks. I have done some rudimentary testing with the examples above, but I can do some screenshot diffing if it seems like we want to do this. Make VERTEX_COLORS usable in prepass shader, if available (bevyengine#10341) I was working with forward rendering prepass fragment shaders and ran into an issue of not being able to access vertex colors in the prepass. I was able to access vertex colors in regular fragment shaders as well as in deferred shaders. It seems like this `if` was nested unintentionally as moving it outside of the `deferred` block works. --- Enable vertex colors in forward rendering prepass fragment shaders allow DeferredPrepass to work without other prepass markers (bevyengine#10223) fix crash / misbehaviour when `DeferredPrepass` is used without `DepthPrepass`. - Deferred lighting requires the depth prepass texture to be present, so that the depth texture is available for binding. without it the deferred lighting pass will use 0 for depth of all meshes. - When `DeferredPrepass` is used without other prepass markers, and with any materials that use `OpaqueRenderMode::Forward`, those entities will try to queue to the `Opaque3dPrepass` render phase, which doesn't exist, causing a crash. - check if the prepass phases exist before queueing - generate prepass textures if `Opaque3dDeferred` is present - add a note to the DeferredPrepass marker to note that DepthPrepass is also required by the default deferred lighting pass - also changed some `With<T>.is_some()`s to `Has<T>`s UI batching Fix (bevyengine#9610) Reimplement bevyengine#8793 on top of the recent rendering changes. The batch creation logic is quite convoluted, but I tested it on enough examples to convince myself that it works. The initial value of `batch_image_handle` is changed from `HandleId::Id(Uuid::nil(), u64::MAX)` to `DEFAULT_IMAGE_HANDLE.id()`, which allowed me to make the if-block simpler I think. The default image from `DEFAULT_IMAGE_HANDLE` is always inserted into `UiImageBindGroups` even if it's not used. I tried to add a check so that it would be only inserted when there is only one batch using the default image but this crashed. --- `prepare_uinodes` * Changed the initial value of `batch_image_handle` to `DEFAULT_IMAGE_HANDLE.id()`. * The default image is added to the UI image bind groups before assembling the batches. * A new `UiBatch` isn't created when the next `ExtractedUiNode`s image is set to `DEFAULT_IMAGE_HANDLE` (unless it is the first item in the UI phase items list). Increase default normal bias to avoid common artifacts (bevyengine#10346) Bevy's default bias values for directional and spot lights currently cause significant artifacts. We should fix that so shadows look good by default! This is a less controversial/invasive alternative to bevyengine#10188, which might enable us to keep the default bias value low, but also has its own sets of concerns and caveats that make it a risky choice for Bevy 0.12. Bump the default normal bias from `0.6` to `1.8`. There is precedent for values in this general area as Godot has a default normal bias of `2.0`. ![image](https://github.com/superdump/bevy/assets/2694663/a5828011-33fc-4427-90ed-f093d7389053) ![image](https://github.com/bevyengine/bevy/assets/2694663/0f2b16b0-c116-41ab-9886-1ace9e00efd6) The default `shadow_normal_bias` value for `DirectionalLight` and `SpotLight` has changed to accommodate artifacts introduced with the new shadow PCF changes. It is unlikely (especially given the new PCF shadow behaviors with these values), but you might need to manually tweak this value if your scene requires a lower bias and it relied on the previous default value. Make `DirectionalLight` `Cascades` computation generic over `CameraProjection` (bevyengine#9226) Fixes bevyengine#9077 (see this issue for motivations) Implement 1 and 2 of the "How to fix it" section of bevyengine#9077 `update_directional_light_cascades` is split into `clear_directional_light_cascades` and a generic `build_directional_light_cascades`, to clear once and potentially insert many times. --- `DirectionalLight`'s computation is now generic over `CameraProjection` and can work with custom camera projections. If you have a component `MyCustomProjection` that implements `CameraProjection`: - You need to implement a new required associated method, `get_frustum_corners`, returning an array of the corners of a subset of the frustum with given `z_near` and `z_far`, in local camera space. - You can now add the `build_directional_light_cascades::<MyCustomProjection>` system in `SimulationLightSystems::UpdateDirectionalLightCascades` after `clear_directional_light_cascades` for your projection to work with directional lights. --------- Co-authored-by: Carter Anderson <[email protected]> Update default `ClearColor` to better match Bevy's branding (bevyengine#10339) - Changes the default clear color to match the code block color on Bevy's website. - Changed the clear color, updated text in examples to ensure adequate contrast. Inconsistent usage of white text color set to use the default color instead, which is already white. - Additionally, updated the `3d_scene` example to make it look a bit better, and use bevy's branding colors. ![image](https://github.com/bevyengine/bevy/assets/2632925/540a22c0-826c-4c33-89aa-34905e3e313a) Corrected incorrect doc comment on read_asset_bytes (bevyengine#10352) Fixes bevyengine#10302 - Removed the incorrect comment. Allow AccessKit to react to WindowEvents before they reach the engine (bevyengine#10356) - Adopt bevyengine#10239 to get it in time for the release - Fix accessibility on macOS and linux - call `on_event` from AcccessKit adapter on winit events --------- Co-authored-by: Nolan Darilek <[email protected]> Co-authored-by: Alice Cecile <[email protected]> Co-authored-by: Alice Cecile <[email protected]> Fix typo in window.rs (bevyengine#10358) Fixes a small typo in `bevy_window/src/window.rs` Change `Should be used instead 'scale_factor' when set.` to `Should be used instead of 'scale_factor' when set.` Add UI Materials (bevyengine#9506) - Add Ui Materials so that UI can render more complex and animated widgets. - Fixes bevyengine#5607 - Create a UiMaterial trait for specifying a Shader Asset and Bind Group Layout/Data. - Create a pipeline for rendering these Materials inside the Ui layout/tree. - Create a MaterialNodeBundle for simple spawning. - Created a `UiMaterial` trait for specifying a Shader asset and Bind Group. - Created a `UiMaterialPipeline` for rendering said Materials. - Added Example [`ui_material` ](https://github.com/MarkusTheOrt/bevy/blob/ui_material/examples/ui/ui_material.rs) for example usage. - Created [`UiVertexOutput`](https://github.com/MarkusTheOrt/bevy/blob/ui_material/crates/bevy_ui/src/render/ui_vertex_output.wgsl) export as VertexData for shaders. - Created [`material_ui`](https://github.com/MarkusTheOrt/bevy/blob/ui_material/crates/bevy_ui/src/render/ui_material.wgsl) shader as default for both Vertex and Fragment shaders. --------- Co-authored-by: ickshonpe <[email protected]> Co-authored-by: François <[email protected]> support file operations in single threaded context (bevyengine#10312) - Fixes bevyengine#10209 - Assets should work in single threaded - In single threaded mode, don't use `async_fs` but fallback on `std::fs` with a thin layer to mimic the async API - file `file_asset.rs` is the async imps from `mod.rs` - file `sync_file_asset.rs` is the same with `async_fs` APIs replaced by `std::fs` - which module is used depends on the `multi-threaded` feature --------- Co-authored-by: Carter Anderson <[email protected]> Fix gizmo crash when prepass enabled (bevyengine#10360) - Fix gizmo crash when prepass enabled - Add the prepass to the view key Fixes: bevyengine#10347
# Objective - Add Ui Materials so that UI can render more complex and animated widgets. - Fixes bevyengine#5607 ## Solution - Create a UiMaterial trait for specifying a Shader Asset and Bind Group Layout/Data. - Create a pipeline for rendering these Materials inside the Ui layout/tree. - Create a MaterialNodeBundle for simple spawning. ## Changelog - Created a `UiMaterial` trait for specifying a Shader asset and Bind Group. - Created a `UiMaterialPipeline` for rendering said Materials. - Added Example [`ui_material` ](https://github.com/MarkusTheOrt/bevy/blob/ui_material/examples/ui/ui_material.rs) for example usage. - Created [`UiVertexOutput`](https://github.com/MarkusTheOrt/bevy/blob/ui_material/crates/bevy_ui/src/render/ui_vertex_output.wgsl) export as VertexData for shaders. - Created [`material_ui`](https://github.com/MarkusTheOrt/bevy/blob/ui_material/crates/bevy_ui/src/render/ui_material.wgsl) shader as default for both Vertex and Fragment shaders. --------- Co-authored-by: ickshonpe <[email protected]> Co-authored-by: François <[email protected]>
# Objective - Add Ui Materials so that UI can render more complex and animated widgets. - Fixes bevyengine#5607 ## Solution - Create a UiMaterial trait for specifying a Shader Asset and Bind Group Layout/Data. - Create a pipeline for rendering these Materials inside the Ui layout/tree. - Create a MaterialNodeBundle for simple spawning. ## Changelog - Created a `UiMaterial` trait for specifying a Shader asset and Bind Group. - Created a `UiMaterialPipeline` for rendering said Materials. - Added Example [`ui_material` ](https://github.com/MarkusTheOrt/bevy/blob/ui_material/examples/ui/ui_material.rs) for example usage. - Created [`UiVertexOutput`](https://github.com/MarkusTheOrt/bevy/blob/ui_material/crates/bevy_ui/src/render/ui_vertex_output.wgsl) export as VertexData for shaders. - Created [`material_ui`](https://github.com/MarkusTheOrt/bevy/blob/ui_material/crates/bevy_ui/src/render/ui_material.wgsl) shader as default for both Vertex and Fragment shaders. --------- Co-authored-by: ickshonpe <[email protected]> Co-authored-by: François <[email protected]>
Objective
Solution
Changelog
UiMaterial
trait for specifying a Shader asset and Bind Group.UiMaterialPipeline
for rendering said Materials.ui_material
for example usage.UiVertexOutput
export as VertexData for shaders.material_ui
shader as default for both Vertex and Fragment shaders.