render: bright-pass material + camera (bloom stage [2])
Adds BrightPassMaterial + brightpass.wgsl: extracts luminance above a soft-knee threshold from the HDR offscreen into a half-res Rgba16Float target (bloom_0). Camera order -19 (after offscreen at -20). The brightpass output is not yet composited (Task 6); this commit only adds the pass and confirms it runs without crashing.
This commit is contained in:
parent
0f33d80fd3
commit
6ccd0a173b
23
assets/shaders/brightpass.wgsl
Normal file
23
assets/shaders/brightpass.wgsl
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#import bevy_sprite::mesh2d_vertex_output::VertexOutput
|
||||||
|
|
||||||
|
struct BrightPassUniform {
|
||||||
|
threshold: f32,
|
||||||
|
_pad0: f32,
|
||||||
|
_pad1: f32,
|
||||||
|
_pad2: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(#{MATERIAL_BIND_GROUP}) @binding(0) var<uniform> u: BrightPassUniform;
|
||||||
|
@group(#{MATERIAL_BIND_GROUP}) @binding(1) var tex: texture_2d<f32>;
|
||||||
|
@group(#{MATERIAL_BIND_GROUP}) @binding(2) var samp: sampler;
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fragment(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
let hdr = textureSample(tex, samp, in.uv).rgb;
|
||||||
|
let lum = dot(hdr, vec3<f32>(0.2126, 0.7152, 0.0722));
|
||||||
|
// Soft knee: smooth roll-off instead of a hard threshold cut.
|
||||||
|
// Fully-bright passes through; near-threshold tapers to zero.
|
||||||
|
let soft = max(lum - u.threshold, 0.0) / (lum + 0.0001);
|
||||||
|
let contribution = hdr * soft;
|
||||||
|
return vec4<f32>(contribution, 1.0);
|
||||||
|
}
|
||||||
@ -138,4 +138,20 @@ impl Material2d for UpscaleMaterial {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts luminance above a threshold from the HDR offscreen into a
|
||||||
|
/// half-res float texture (bloom stage [2]). Soft-knee, not hard cut.
|
||||||
|
#[derive(Asset, TypePath, AsBindGroup, Clone)]
|
||||||
|
pub struct BrightPassMaterial {
|
||||||
|
#[uniform(0)]
|
||||||
|
pub threshold: f32,
|
||||||
|
#[texture(1)]
|
||||||
|
#[sampler(2)]
|
||||||
|
pub source: Handle<Image>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Material2d for BrightPassMaterial {
|
||||||
|
fn fragment_shader() -> ShaderRef {
|
||||||
|
"shaders/brightpass.wgsl".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -49,6 +49,22 @@ pub struct QuadScaleFactor(pub f32, pub f32);
|
|||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
pub struct CompositeQuad;
|
pub struct CompositeQuad;
|
||||||
|
|
||||||
|
/// The half-res Image the bright-pass writes into (bloom stage [2]).
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct BloomTarget0(pub Handle<Image>);
|
||||||
|
|
||||||
|
/// Camera + quad markers for the bright-pass (for rebuild queries).
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct BrightPassCamera;
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct BrightPassQuad;
|
||||||
|
|
||||||
|
/// Camera + quad markers for the blur passes (for rebuild queries).
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct BlurCamera;
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct BlurQuad;
|
||||||
|
|
||||||
pub struct BlackHolePlugin;
|
pub struct BlackHolePlugin;
|
||||||
|
|
||||||
impl Plugin for BlackHolePlugin {
|
impl Plugin for BlackHolePlugin {
|
||||||
@ -58,6 +74,7 @@ impl Plugin for BlackHolePlugin {
|
|||||||
.init_resource::<crate::params::BlackHoleParams>()
|
.init_resource::<crate::params::BlackHoleParams>()
|
||||||
.add_plugins(Material2dPlugin::<BlackHoleMaterial>::default())
|
.add_plugins(Material2dPlugin::<BlackHoleMaterial>::default())
|
||||||
.add_plugins(Material2dPlugin::<crate::render::material::UpscaleMaterial>::default())
|
.add_plugins(Material2dPlugin::<crate::render::material::UpscaleMaterial>::default())
|
||||||
|
.add_plugins(Material2dPlugin::<crate::render::material::BrightPassMaterial>::default())
|
||||||
.add_plugins(bevy_egui::EguiPlugin::default())
|
.add_plugins(bevy_egui::EguiPlugin::default())
|
||||||
.add_systems(Startup, spawn_fullscreen_quad)
|
.add_systems(Startup, spawn_fullscreen_quad)
|
||||||
.add_systems(Startup, crate::scene::planets::spawn_default_planet)
|
.add_systems(Startup, crate::scene::planets::spawn_default_planet)
|
||||||
@ -83,6 +100,7 @@ fn spawn_fullscreen_quad(
|
|||||||
mut meshes: ResMut<Assets<Mesh>>,
|
mut meshes: ResMut<Assets<Mesh>>,
|
||||||
mut materials: ResMut<Assets<BlackHoleMaterial>>,
|
mut materials: ResMut<Assets<BlackHoleMaterial>>,
|
||||||
mut upscale_materials: ResMut<Assets<crate::render::material::UpscaleMaterial>>,
|
mut upscale_materials: ResMut<Assets<crate::render::material::UpscaleMaterial>>,
|
||||||
|
mut bp_materials: ResMut<Assets<crate::render::material::BrightPassMaterial>>,
|
||||||
mut buffers: ResMut<Assets<ShaderBuffer>>,
|
mut buffers: ResMut<Assets<ShaderBuffer>>,
|
||||||
mut images: ResMut<Assets<Image>>,
|
mut images: ResMut<Assets<Image>>,
|
||||||
window: Query<&Window>,
|
window: Query<&Window>,
|
||||||
@ -147,6 +165,38 @@ fn spawn_fullscreen_quad(
|
|||||||
RenderLayers::layer(0),
|
RenderLayers::layer(0),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// --- Bright-pass (bloom stage [2]): half-res float target ---
|
||||||
|
let bw = ((w as f32 * 0.5) as u32).max(1);
|
||||||
|
let bh = ((h as f32 * 0.5) as u32).max(1);
|
||||||
|
let bloom0 = images.add(Image::new_target_texture(
|
||||||
|
bw, bh, TextureFormat::Rgba16Float, None,
|
||||||
|
));
|
||||||
|
commands.spawn(BloomTarget0(bloom0.clone()));
|
||||||
|
commands.spawn((
|
||||||
|
Mesh2d(meshes.add(Rectangle::new(2.0, 2.0))),
|
||||||
|
MeshMaterial2d(bp_materials.add(crate::render::material::BrightPassMaterial {
|
||||||
|
threshold: 1.0,
|
||||||
|
source: offscreen.clone(),
|
||||||
|
})),
|
||||||
|
Transform::default().with_scale(Vec3::new(bw as f32 / 2.0, bh as f32 / 2.0, 1.0)),
|
||||||
|
BrightPassQuad,
|
||||||
|
QuadScaleFactor(0.5, 0.5),
|
||||||
|
RenderLayers::layer(2),
|
||||||
|
));
|
||||||
|
commands.spawn((
|
||||||
|
Camera2d,
|
||||||
|
Camera {
|
||||||
|
order: -19,
|
||||||
|
clear_color: ClearColorConfig::Custom(Color::srgb(0.0, 0.0, 0.0)),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
RenderTarget::Image(bloom0.clone().into()),
|
||||||
|
Msaa::Off,
|
||||||
|
BrightPassCamera,
|
||||||
|
Nudgable,
|
||||||
|
RenderLayers::layer(2),
|
||||||
|
));
|
||||||
|
|
||||||
// --- Upscale quad (draws offscreen Image to the window) ---
|
// --- Upscale quad (draws offscreen Image to the window) ---
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
Mesh2d(meshes.add(Rectangle::new(2.0, 2.0))),
|
Mesh2d(meshes.add(Rectangle::new(2.0, 2.0))),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user