Skip to content

Instantly share code, notes, and snippets.

@bas-ie
Last active January 18, 2025 22:40
Show Gist options
  • Save bas-ie/abfe57a0fd136d7ff547e623527f63de to your computer and use it in GitHub Desktop.
Save bas-ie/abfe57a0fd136d7ff547e623527f63de to your computer and use it in GitHub Desktop.
Minimal repro of image render target issue
use bevy::{
prelude::*,
render::{
render_asset::RenderAssetUsages,
render_resource::{
AsBindGroup, Extent3d, ShaderRef, TextureDimension, TextureFormat, TextureUsages,
},
view::RenderLayers,
},
sprite::{Material2d, Material2dPlugin},
};
fn main() {
App::new()
.init_resource::<ImageRenderTarget>()
.add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Window {
resizable: false,
..default()
}
.into(),
..default()
}),
Material2dPlugin::<CustomMaterial>::default(),
))
.add_systems(Startup, init)
.add_systems(Update, check_image)
.run();
}
const SHADER_ASSET_PATH: &str = "color_shader.wgsl";
#[derive(Resource, Default)]
pub struct ImageRenderTarget {
pub texture: Handle<Image>,
}
#[derive(Asset, Default, TypePath, AsBindGroup, Debug, Clone)]
pub struct CustomMaterial {
#[uniform(0)]
pub color: Vec4,
#[texture(1)]
#[sampler(2)]
pub texture: Handle<Image>,
}
impl Material2d for CustomMaterial {
fn fragment_shader() -> ShaderRef {
SHADER_ASSET_PATH.into()
}
}
#[derive(Component)]
struct RenderTargetCamera;
#[derive(Component)]
struct MainCamera;
fn init(
asset_server: Res<AssetServer>,
mut commands: Commands,
mut materials: ResMut<Assets<CustomMaterial>>,
mut meshes: ResMut<Assets<Mesh>>,
mut images: ResMut<Assets<Image>>,
mut target_image: ResMut<ImageRenderTarget>,
) {
let size = Extent3d {
width: 256,
height: 256,
..default()
};
let mut image = Image::new_fill(
size,
TextureDimension::D2,
&[0, 0, 0, 0],
TextureFormat::Bgra8UnormSrgb,
RenderAssetUsages::default(),
);
image.texture_descriptor.usage =
TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST | TextureUsages::RENDER_ATTACHMENT;
target_image.texture = images.add(image);
// RenderTarget or "source" cam?
commands.spawn((
Name::new("ImageRenderTarget Camera"),
RenderTargetCamera,
Camera2d,
Camera {
order: -1,
target: bevy::render::camera::RenderTarget::Image(target_image.texture.clone()),
clear_color: ClearColorConfig::None,
..default()
},
));
// Source custom material
commands.spawn((
Name::new("Mesh With Custom Material"),
Mesh2d(meshes.add(Rectangle::new(256., 256.))),
MeshMaterial2d(materials.add(CustomMaterial {
color: Vec4::new(0., 0., 0., 1.),
texture: asset_server.load("green.png"),
})),
));
// Main or "destination" cam?
commands.spawn((
Name::new("Main Camera"),
MainCamera,
Camera2d,
RenderLayers::layer(1),
));
// Destination image (on a sprite)
commands.spawn((
Name::new("Debug ImageRenderTarget"),
RenderLayers::layer(1),
Sprite {
image: target_image.texture.clone(),
..Default::default()
},
));
}
fn check_image(images: Res<Assets<Image>>, render_target: Res<ImageRenderTarget>) {
let Some(img) = images.get(&render_target.texture) else {
return;
};
// I would expect this to be black (0, 0, 0, 1) due to the shader output of that colour, but
// it's still zero-d out from its initialisation (0, 0, 0, 0).
let Some(pixel) = img.pixel_bytes(UVec3::new(128, 128, 0)) else {
return;
};
dbg!(pixel);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment