bevyengine / bevyengine/bevy

`bevy_image::TextureAtlasBuilder`: Add image extrusion support

Open
#22,775 4 comments 0 reactions 0 assignees View on GitHub
A-Rendering C-Bug C-Feature D-Modest S-Ready-For-Implementation
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 16h
Merged PRs (30d)
171

Description

## What problem does this solve or what need does it fill?

`TextureAtlasBuilder` currently packs images edge-to-edge, with support to optionally add padding between each image.

Padding works by inserting N (set using the `TextureAtlasBuilder::padding`, can be 2) transparent pixels to the right and bottom of each image in the atlas texture, increasing the spacing between atlas regions.

Padding is intended to reduce sampling artifacts but it's sometimes insufficient when used with:
* linear filtering
* scaled, rotated, or transformed sprites
* mipmaps
* sampling near boundaries

## What solution would you like?

Add support for image extrusion to `TextureAtlasBuilder` and `DynamicTextureAtlasBuilder`.

Extrusion is where the border pixels of each image added to the atlas are duplicated outward by N pixels, creating a border that matches the edge color of the image. Corner regions are filled by duplicating the nearest corner pixel of the image. Then sampling across the boundary of the image will only read texels belonging to the image.

### Implementation

The changes should be relatively straightforward and don't require any specialist rendering knowledge. Extrusion is not too different from the current padding fill except using the edge colors instead of transparency. The only awkwardish part is that padding is added only to the bottom and right edges of each sub-texture, whereas the extruded border needs to added on all four edges.

#### Basic example comparing padding and extrusion

```rust
use bevy::prelude::*;
use bevy::render::render_resource::{Extent3d, TextureDimension, TextureFormat};
use bevy::window::WindowResolution;
use bevy_asset::RenderAssetUsages;
use std::f32::consts::PI;

fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
resolution: WindowResolution::new(800, 450).with_scale_factor_override(1.0),
..default()
}),
..default()
}))
.add_systems(Startup, setup)
.run();
}

fn setup(mut commands: Commands, mut images: ResMut>) {
commands.spawn(Camera2d);

// Generate a 64x64 image with 8 pixels of transparent padding along each edge
let width: u32 = 64;
let height: u32 = 64;
let border: u32 = 8;

let mut data = vec![0u8; (width * height * 4) as usize];
for y in border..(height - border) {
for x in border..(width - border) {
let i = ((y * width + x) * 4) as usize;
data[i + 0] = 255; // R
data[i + 1] = 255; // G
data[i + 2] = 255; // B
data[i + 3] = 255; // A
}
}

let image = Image::new(
Extent3d {
width,
height,
depth_or_array_layers: 1,
},
TextureDimension::D2,
data,
TextureFormat::Rgba8UnormSrgb,
RenderAssetUsages::all(),
);

let image = images.add(image);

commands.spawn((
Sprite {
image,
// Draw a region of the sprite to emulate padding and extrusion.
// The top and left edges of the region are at the transparent border boundary.
// The bottom and right edges of the region are well inside the white interior of the image.
rect: Some(Rect {
min: Vec2::splat(border as f32),
max: Vec2::splat(border as f32 + 16.),
}),
..default()
},
// Transform the sprite to maximise haloing
Transform {
translation: Vec3::new(-0.5, -0.5, 0.),
scale: Vec3::new(10., 10., 0.),
rotation: Quat::from_rotation_z(PI / 8.),
..default()
},
));
}
```

This creates a white image with a transparent boundary. To emulate a padded texture atlas image, the image's top and left edges of the region are at the transparent border boundary. And to emulate an extruded texture atlas image, its bottom and right edges of the region are well inside the white interior of the image. Then the image is displayed using a sprite with its transform set to maximize the observed artifacts:

Image

## What alternative(s) have you considered?

You can manipulate the UVs or clamp them in the shader, but it's significantly more complicated and error prone.

Contributor guide

Open the contributing guide

Research direction

Start by locating the implementations of TextureAtlasBuilder and DynamicTextureAtlasBuilder, then read how padding currently fills atlas regions. Add the requested extrusion behavior for all four edges and corners while preserving existing padding behavior; done means both builders produce edge-colored borders that reduce boundary sampling artifacts.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
computer-graphics
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.