Skip to content

Commit

Permalink
Separate tonemapping config from hdr. Disable tonemapping for sprites…
Browse files Browse the repository at this point in the history
… by default
  • Loading branch information
cart committed Jun 27, 2022
1 parent 2cae409 commit 723f692
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 85 deletions.
4 changes: 3 additions & 1 deletion crates/bevy_core_pipeline/src/core_2d/camera_2d.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::clear_color::ClearColorConfig;
use crate::{clear_color::ClearColorConfig, tonemapping::Tonemapping};
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_reflect::Reflect;
use bevy_render::{
Expand Down Expand Up @@ -36,6 +36,7 @@ pub struct Camera2dBundle {
pub transform: Transform,
pub global_transform: GlobalTransform,
pub camera_2d: Camera2d,
pub tonemapping: Tonemapping,
}

impl Default for Camera2dBundle {
Expand Down Expand Up @@ -77,6 +78,7 @@ impl Camera2dBundle {
global_transform: Default::default(),
camera: Camera::default(),
camera_2d: Camera2d::default(),
tonemapping: Tonemapping { is_enabled: false },
}
}
}
4 changes: 3 additions & 1 deletion crates/bevy_core_pipeline/src/core_3d/camera_3d.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::clear_color::ClearColorConfig;
use crate::{clear_color::ClearColorConfig, tonemapping::Tonemapping};
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
use bevy_render::{
Expand Down Expand Up @@ -66,13 +66,15 @@ pub struct Camera3dBundle {
pub transform: Transform,
pub global_transform: GlobalTransform,
pub camera_3d: Camera3d,
pub tonemapping: Tonemapping,
}

// NOTE: ideally Perspective and Orthographic defaults can share the same impl, but sadly it breaks rust's type inference
impl Default for Camera3dBundle {
fn default() -> Self {
Self {
camera_render_graph: CameraRenderGraph::new(crate::core_3d::graph::NAME),
tonemapping: Tonemapping { is_enabled: true },
camera: Default::default(),
projection: Default::default(),
visible_entities: Default::default(),
Expand Down
11 changes: 11 additions & 0 deletions crates/bevy_core_pipeline/src/tonemapping/blit.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#import bevy_core_pipeline::fullscreen_vertex_shader

[[group(0), binding(0)]]
var texture: texture_2d<f32>;
[[group(0), binding(1)]]
var texture_sampler: sampler;

[[stage(fragment)]]
fn fs_main(in: FullscreenVertexOutput) -> [[location(0)]] vec4<f32> {
return textureSample(texture, texture_sampler, in.uv);
}
56 changes: 50 additions & 6 deletions crates/bevy_core_pipeline/src/tonemapping/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
mod node;

use bevy_ecs::query::QueryItem;
use bevy_render::camera::Camera;
use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin};
pub use node::TonemappingNode;

use bevy_app::prelude::*;
Expand All @@ -19,6 +22,9 @@ const TONEMAPPING_SHADER_HANDLE: HandleUntyped =
const TONEMAPPING_SHARED_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2499430578245347910);

const BLIT_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2982361071241723543);

pub struct TonemappingPlugin;

impl Plugin for TonemappingPlugin {
Expand All @@ -35,6 +41,9 @@ impl Plugin for TonemappingPlugin {
"tonemapping_shared.wgsl",
Shader::from_wgsl
);
load_internal_asset!(app, BLIT_SHADER_HANDLE, "blit.wgsl", Shader::from_wgsl);

app.add_plugin(ExtractComponentPlugin::<Tonemapping>::default());

let render_app = match app.get_sub_app_mut(RenderApp) {
Ok(render_app) => render_app,
Expand All @@ -47,12 +56,13 @@ impl Plugin for TonemappingPlugin {

pub struct TonemappingPipeline {
hdr_texture_bind_group: BindGroupLayout,
pipeline_id: CachedRenderPipelineId,
tonemapping_pipeline_id: CachedRenderPipelineId,
blit_pipeline_id: CachedRenderPipelineId,
}

impl FromWorld for TonemappingPipeline {
fn from_world(render_world: &mut World) -> Self {
let hdr_texture_bind_group = render_world
let tonemap_texture_bind_group = render_world
.resource::<RenderDevice>()
.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: Some("tonemapping_hdr_texture_bind_group_layout"),
Expand All @@ -76,9 +86,9 @@ impl FromWorld for TonemappingPipeline {
],
});

let descriptor = RenderPipelineDescriptor {
let tonemap_descriptor = RenderPipelineDescriptor {
label: Some("tonemapping pipeline".into()),
layout: Some(vec![hdr_texture_bind_group.clone()]),
layout: Some(vec![tonemap_texture_bind_group.clone()]),
vertex: fullscreen_shader_vertex_state(),
fragment: Some(FragmentState {
shader: TONEMAPPING_SHADER_HANDLE.typed(),
Expand All @@ -94,10 +104,44 @@ impl FromWorld for TonemappingPipeline {
depth_stencil: None,
multisample: MultisampleState::default(),
};

let blit_descriptor = RenderPipelineDescriptor {
label: Some("blit pipeline".into()),
layout: Some(vec![tonemap_texture_bind_group.clone()]),
vertex: fullscreen_shader_vertex_state(),
fragment: Some(FragmentState {
shader: BLIT_SHADER_HANDLE.typed(),
shader_defs: vec![],
entry_point: "fs_main".into(),
targets: vec![ColorTargetState {
format: TextureFormat::bevy_default(),
blend: None,
write_mask: ColorWrites::ALL,
}],
}),
primitive: PrimitiveState::default(),
depth_stencil: None,
multisample: MultisampleState::default(),
};
let mut cache = render_world.resource_mut::<PipelineCache>();
TonemappingPipeline {
hdr_texture_bind_group,
pipeline_id: cache.queue_render_pipeline(descriptor),
hdr_texture_bind_group: tonemap_texture_bind_group,
tonemapping_pipeline_id: cache.queue_render_pipeline(tonemap_descriptor),
blit_pipeline_id: cache.queue_render_pipeline(blit_descriptor),
}
}
}

#[derive(Component, Clone)]
pub struct Tonemapping {
pub is_enabled: bool,
}

impl ExtractComponent for Tonemapping {
type Query = &'static Self;
type Filter = With<Camera>;

fn extract_component(item: QueryItem<Self::Query>) -> Self {
item.clone()
}
}
26 changes: 16 additions & 10 deletions crates/bevy_core_pipeline/src/tonemapping/node.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::sync::Mutex;

use crate::tonemapping::{Tonemapping, TonemappingPipeline};
use bevy_ecs::prelude::*;
use bevy_ecs::query::QueryState;
use bevy_render::{
Expand All @@ -13,10 +14,8 @@ use bevy_render::{
view::{ExtractedView, ViewMainTexture, ViewTarget},
};

use super::TonemappingPipeline;

pub struct TonemappingNode {
query: QueryState<&'static ViewTarget, With<ExtractedView>>,
query: QueryState<(&'static ViewTarget, Option<&'static Tonemapping>), With<ExtractedView>>,
cached_texture_bind_group: Mutex<Option<(TextureViewId, BindGroup)>>,
}

Expand Down Expand Up @@ -50,16 +49,11 @@ impl Node for TonemappingNode {
let pipeline_cache = world.resource::<PipelineCache>();
let tonemapping_pipeline = world.resource::<TonemappingPipeline>();

let target = match self.query.get_manual(world, view_entity) {
Ok(query) => query,
let (target, tonemapping) = match self.query.get_manual(world, view_entity) {
Ok(result) => result,
Err(_) => return Ok(()),
};

let pipeline = match pipeline_cache.get_render_pipeline(tonemapping_pipeline.pipeline_id) {
Some(pipeline) => pipeline,
None => return Ok(()),
};

let ldr_texture = match &target.main_texture {
ViewMainTexture::Hdr { ldr_texture, .. } => ldr_texture,
ViewMainTexture::Sdr { .. } => {
Expand All @@ -68,6 +62,18 @@ impl Node for TonemappingNode {
}
};

let tonemapping_enabled = tonemapping.map_or(false, |t| t.is_enabled);
let pipeline_id = if tonemapping_enabled {
tonemapping_pipeline.tonemapping_pipeline_id
} else {
tonemapping_pipeline.blit_pipeline_id
};

let pipeline = match pipeline_cache.get_render_pipeline(pipeline_id) {
Some(pipeline) => pipeline,
None => return Ok(()),
};

let main_texture = target.main_texture.texture();

let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap();
Expand Down
23 changes: 19 additions & 4 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use crate::{
};
use bevy_app::{App, Plugin};
use bevy_asset::{AddAsset, Asset, AssetServer, Handle};
use bevy_core_pipeline::core_3d::{AlphaMask3d, Opaque3d, Transparent3d};
use bevy_core_pipeline::{
core_3d::{AlphaMask3d, Opaque3d, Transparent3d},
tonemapping::Tonemapping,
};
use bevy_ecs::{
entity::Entity,
prelude::World,
Expand Down Expand Up @@ -338,13 +341,20 @@ pub fn queue_material_meshes<M: SpecializedMaterial>(
mut views: Query<(
&ExtractedView,
&VisibleEntities,
Option<&Tonemapping>,
&mut RenderPhase<Opaque3d>,
&mut RenderPhase<AlphaMask3d>,
&mut RenderPhase<Transparent3d>,
)>,
) {
for (view, visible_entities, mut opaque_phase, mut alpha_mask_phase, mut transparent_phase) in
views.iter_mut()
for (
view,
visible_entities,
tonemapping,
mut opaque_phase,
mut alpha_mask_phase,
mut transparent_phase,
) in views.iter_mut()
{
let draw_opaque_pbr = opaque_draw_functions
.read()
Expand All @@ -362,8 +372,13 @@ pub fn queue_material_meshes<M: SpecializedMaterial>(
let inverse_view_matrix = view.transform.compute_matrix().inverse();
let inverse_view_row_2 = inverse_view_matrix.row(2);

let view_key =
let mut view_key =
MeshPipelineKey::from_msaa_samples(msaa.samples) | MeshPipelineKey::from_hdr(view.hdr);
if let Some(tonemapping) = tonemapping {
if tonemapping.is_enabled && !view.hdr {
view_key |= MeshPipelineKey::TONEMAP_IN_SHADER;
}
}

for visible_entity in &visible_entities.entities {
if let Ok((material_handle, mesh_handle, mesh_uniform)) =
Expand Down
7 changes: 4 additions & 3 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,8 @@ bitflags::bitflags! {
pub struct MeshPipelineKey: u32 {
const NONE = 0;
const TRANSPARENT_MAIN_PASS = (1 << 0);
const HDR = (1 << 2);
const HDR = (1 << 1);
const TONEMAP_IN_SHADER = (1 << 2);
const MSAA_RESERVED_BITS = MeshPipelineKey::MSAA_MASK_BITS << MeshPipelineKey::MSAA_SHIFT_BITS;
const PRIMITIVE_TOPOLOGY_RESERVED_BITS = MeshPipelineKey::PRIMITIVE_TOPOLOGY_MASK_BITS << MeshPipelineKey::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
}
Expand Down Expand Up @@ -629,8 +630,8 @@ impl SpecializedMeshPipeline for MeshPipeline {
depth_write_enabled = true;
}

if !key.contains(MeshPipelineKey::HDR) {
shader_defs.push("TONEMAPPING_IN_PBR_SHADER".to_string());
if key.contains(MeshPipelineKey::TONEMAP_IN_SHADER) {
shader_defs.push("TONEMAP_IN_SHADER".to_string());
}

let format = match key.contains(MeshPipelineKey::HDR) {
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_pbr/src/render/pbr.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#import bevy_pbr::pbr_bindings
#import bevy_pbr::mesh_bindings

#ifdef TONEMAPPING_IN_PBR_SHADER
#ifdef TONEMAP_IN_SHADER
#import bevy_core_pipeline::tonemapping
#endif

Expand Down Expand Up @@ -92,7 +92,7 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
output_color = pbr(pbr_input);
}

#ifdef TONEMAPPING_IN_PBR_SHADER
#ifdef TONEMAP_IN_SHADER
output_color = vec4<f32>(reinhard_luminance(output_color.rgb), output_color.a);
#endif

Expand Down
9 changes: 0 additions & 9 deletions crates/bevy_pbr/src/render/pbr_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,3 @@ fn pbr(

return output_color;
}

fn tone_mapping(in: vec4<f32>) -> vec4<f32> {
// tone_mapping
return vec4<f32>(reinhard_luminance(in.rgb), in.a);

// Gamma correction.
// Not needed with sRGB buffer
// output_color.rgb = pow(output_color.rgb, vec3(1.0 / 2.2));
}
35 changes: 0 additions & 35 deletions crates/bevy_pbr/src/render/pbr_lighting.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -149,41 +149,6 @@ fn perceptualRoughnessToRoughness(perceptualRoughness: f32) -> f32 {
return clampedPerceptualRoughness * clampedPerceptualRoughness;
}

// from https://64.github.io/tonemapping/
// reinhard on RGB oversaturates colors
fn reinhard(color: vec3<f32>) -> vec3<f32> {
return color / (1.0 + color);
}

fn reinhard_extended(color: vec3<f32>, max_white: f32) -> vec3<f32> {
let numerator = color * (1.0 + (color / vec3<f32>(max_white * max_white)));
return numerator / (1.0 + color);
}

// luminance coefficients from Rec. 709.
// https://en.wikipedia.org/wiki/Rec._709
fn luminance(v: vec3<f32>) -> f32 {
return dot(v, vec3<f32>(0.2126, 0.7152, 0.0722));
}

fn change_luminance(c_in: vec3<f32>, l_out: f32) -> vec3<f32> {
let l_in = luminance(c_in);
return c_in * (l_out / l_in);
}

fn reinhard_luminance(color: vec3<f32>) -> vec3<f32> {
let l_old = luminance(color);
let l_new = l_old / (1.0 + l_old);
return change_luminance(color, l_new);
}

fn reinhard_extended_luminance(color: vec3<f32>, max_white_l: f32) -> vec3<f32> {
let l_old = luminance(color);
let numerator = l_old * (1.0 + (l_old / (max_white_l * max_white_l)));
let l_new = numerator / (1.0 + l_old);
return change_luminance(color, l_new);
}

fn point_light(
world_position: vec3<f32>, light: PointLight, roughness: f32, NdotV: f32, N: vec3<f32>, V: vec3<f32>,
R: vec3<f32>, F0: vec3<f32>, diffuseColor: vec3<f32>
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_render/src/camera/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub struct Camera {
/// The "target" that this camera will render to.
#[reflect(ignore)]
pub target: RenderTarget,
/// If this is set to true, the camera will use an intermediate "high dynamic range" render texture.
pub hdr: bool,
}

Expand Down

0 comments on commit 723f692

Please sign in to comment.