Skip to content

Commit

Permalink
box-shadow clipping fix (#16790)
Browse files Browse the repository at this point in the history
# Objective

Instead of clipping the non-visable sections of box-shadows, the shadow
is scaled to fit into the remaining area after clipping because the
normalized coordinates that are meant to border the unclipped subsection
of the shadow are always set to `[Vec2::ZERO, Vec2::X, Vec2::ONE,
Vec2::Y]`,

## Solution

Calculate the coordinates for the corners of the visible area.

## Testing

Test app:

```rust
use bevy::color::palettes::css::RED;
use bevy::color::palettes::css::WHITE;
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d);
    commands
        .spawn(Node {
            ..Default::default()
        })
        .with_children(|commands| {
            commands
                .spawn((
                    Node {
                        width: Val::Px(100.),
                        height: Val::Px(100.),
                        margin: UiRect {
                            left: Val::Px(100.),
                            top: Val::Px(300.),
                            ..Default::default()
                        },
                        overflow: Overflow::clip(),
                        ..Default::default()
                    },
                    BackgroundColor(WHITE.into()),
                ))
                .with_children(|commands| {
                    commands.spawn((
                        Node {
                            position_type: PositionType::Absolute,
                            left: Val::Px(50.),
                            top: Val::Px(50.),
                            width: Val::Px(100.),
                            height: Val::Px(100.),
                            ..Default::default()
                        },
                        BackgroundColor(RED.into()),
                        BoxShadow::from(ShadowStyle {
                            x_offset: Val::ZERO,
                            y_offset: Val::ZERO,
                            spread_radius: Val::Px(50.),
                            blur_radius: Val::Px(6.),
                            ..Default::default()
                        }),
                    ));
                });
        });
}
```

Main:
<img width="103" alt="bad_shadow"
src="https://github.com/user-attachments/assets/6f7ade0e-959f-4d18-92e8-903630eb8cd3"
/>

This PR:
<img width="98" alt="clipped_shadow"
src="https://github.com/user-attachments/assets/7f576c94-908c-4fe6-abaa-f18fefe05207"
/>
  • Loading branch information
ickshonpe authored Dec 13, 2024
1 parent 4ba47ed commit 30bd641
Showing 1 changed file with 20 additions and 9 deletions.
29 changes: 20 additions & 9 deletions crates/bevy_ui/src/render/box_shadow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ impl SpecializedRenderPipeline for BoxShadowPipeline {
pub struct ExtractedBoxShadow {
pub stack_index: u32,
pub transform: Mat4,
pub rect: Rect,
pub bounds: Vec2,
pub clip: Option<Rect>,
pub camera_entity: Entity,
pub color: LinearRgba,
Expand Down Expand Up @@ -323,10 +323,7 @@ pub fn extract_shadows(
transform: transform.compute_matrix()
* Mat4::from_translation(offset.extend(0.)),
color: drop_shadow.color.into(),
rect: Rect {
min: Vec2::ZERO,
max: shadow_size + 6. * blur_radius,
},
bounds: shadow_size + 6. * blur_radius,
clip: clip.map(|clip| clip.clip),
camera_entity,
radius,
Expand Down Expand Up @@ -415,9 +412,7 @@ pub fn prepare_shadows(
while item_index < ui_phase.items.len() {
let item = &mut ui_phase.items[item_index];
if let Some(box_shadow) = extracted_shadows.box_shadows.get(item.entity()) {
let uinode_rect = box_shadow.rect;

let rect_size = uinode_rect.size().extend(1.0);
let rect_size = box_shadow.bounds.extend(1.0);

// Specify the corners of the node
let positions = QUAD_VERTEX_POSITIONS
Expand Down Expand Up @@ -479,7 +474,23 @@ pub fn prepare_shadows(
box_shadow.radius.bottom_left,
];

let uvs = [Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y];
let uvs = [
Vec2::new(positions_diff[0].x, positions_diff[0].y),
Vec2::new(
box_shadow.bounds.x + positions_diff[1].x,
positions_diff[1].y,
),
Vec2::new(
box_shadow.bounds.x + positions_diff[2].x,
box_shadow.bounds.y + positions_diff[2].y,
),
Vec2::new(
positions_diff[3].x,
box_shadow.bounds.y + positions_diff[3].y,
),
]
.map(|pos| pos / box_shadow.bounds);

for i in 0..4 {
ui_meta.vertices.push(BoxShadowVertex {
position: positions_clipped[i].into(),
Expand Down

0 comments on commit 30bd641

Please sign in to comment.