Skip to content

Commit

Permalink
make hdr configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
jakobhellermann committed Dec 30, 2021
1 parent 0de50d8 commit 11c326a
Show file tree
Hide file tree
Showing 18 changed files with 269 additions and 102 deletions.
11 changes: 8 additions & 3 deletions crates/bevy_core_pipeline/src/main_pass_2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use bevy_render::{
render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass},
render_resource::{LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor},
renderer::RenderContext,
view::{ExtractedView, ViewTarget},
view::{ExtractedView, ViewMainTexture, ViewTarget},
};

pub struct MainPass2dNode {
Expand Down Expand Up @@ -52,10 +52,15 @@ impl Node for MainPass2dNode {
.get_manual(world, view_entity)
.expect("view entity should exist");

let view = match &target.main_texture {
ViewMainTexture::Hdr { hdr_texture, .. } => hdr_texture,
ViewMainTexture::NoHdr { texture, .. } => texture,
};

let pass_descriptor = RenderPassDescriptor {
label: Some("main_pass_2d"),
color_attachments: &[RenderPassColorAttachment {
view: &target.hdr_texture,
view,
resolve_target: None,
ops: Operations {
load: LoadOp::Load,
Expand All @@ -81,7 +86,7 @@ impl Node for MainPass2dNode {
}

graph
.set_output(MainPass2dNode::OUT_TEXTURE, target.hdr_texture.clone())
.set_output(MainPass2dNode::OUT_TEXTURE, view.clone())
.unwrap();

Ok(())
Expand Down
5 changes: 4 additions & 1 deletion crates/bevy_core_pipeline/src/main_pass_3d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,10 @@ impl Node for MainPass3dNode {
}

graph
.set_output(MainPass3dNode::OUT_TEXTURE, target.hdr_texture.clone())
.set_output(
MainPass3dNode::OUT_TEXTURE,
target.main_texture.maybe_hdr_texture().clone(),
)
.unwrap();

Ok(())
Expand Down
8 changes: 8 additions & 0 deletions crates/bevy_core_pipeline/src/tonemapping/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use bevy_reflect::TypeUuid;
const TONEMAPPING_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 17015368199668024512);

const TONEMAPPING_SHARED_SHADER_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2499430578245347910);

pub struct TonemappingPlugin;

impl Plugin for TonemappingPlugin {
Expand All @@ -24,6 +27,11 @@ impl Plugin for TonemappingPlugin {
TONEMAPPING_SHADER_HANDLE,
Shader::from_wgsl(include_str!("tonemapping.wgsl")),
);
shaders.set_untracked(
TONEMAPPING_SHARED_SHADER_HANDLE,
Shader::from_wgsl(include_str!("tonemapping_shared.wgsl"))
.with_import_path("bevy_core_pipeline::tonemapping"),
);

app.sub_app_mut(RenderApp)
.init_resource::<TonemappingPipeline>()
Expand Down
41 changes: 28 additions & 13 deletions crates/bevy_core_pipeline/src/tonemapping/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use bevy_render::{
TextureViewId,
},
renderer::RenderContext,
view::{ExtractedView, ViewTarget},
view::{ExtractedView, ViewMainTexture, ViewTarget},
};

use super::{TonemappingPipeline, TonemappingTarget};
Expand Down Expand Up @@ -64,6 +64,31 @@ impl Node for TonemappingNode {
let render_pipeline_cache = world.get_resource::<RenderPipelineCache>().unwrap();
let tonemapping_pipeline = world.get_resource::<TonemappingPipeline>().unwrap();

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

let pipeline = match render_pipeline_cache.get(tonemapping_target.pipeline) {
Some(pipeline) => pipeline,
None => return Ok(()),
};

let ldr_texture = match &target.main_texture {
ViewMainTexture::Hdr { ldr_texture, .. } => ldr_texture,
ViewMainTexture::NoHdr { .. } => {
// non-hdr does tone mapping in the main pass node
let in_texture = in_texture.clone();
graph
.set_output(
TonemappingNode::OUT_TEXTURE,
SlotValue::TextureView(in_texture),
)
.unwrap();
return Ok(());
}
};

let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap();
let bind_group = match &mut *cached_bind_group {
Some((id, bind_group)) if in_texture.id() == *id => bind_group,
Expand Down Expand Up @@ -95,20 +120,10 @@ impl Node for TonemappingNode {
}
};

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

let pipeline = match render_pipeline_cache.get(tonemapping_target.pipeline) {
Some(pipeline) => pipeline,
None => return Ok(()),
};

let pass_descriptor = RenderPassDescriptor {
label: Some("tonemapping_pass"),
color_attachments: &[RenderPassColorAttachment {
view: &target.ldr_texture,
view: ldr_texture,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Default::default()), // TODO shouldn't need to be cleared
Expand All @@ -129,7 +144,7 @@ impl Node for TonemappingNode {
graph
.set_output(
TonemappingNode::OUT_TEXTURE,
SlotValue::TextureView(target.ldr_texture.clone()),
SlotValue::TextureView(ldr_texture.clone()),
)
.unwrap();

Expand Down
30 changes: 2 additions & 28 deletions crates/bevy_core_pipeline/src/tonemapping/tonemapping.wgsl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#import bevy_core_pipeline::tonemapping

struct VertexOutput {
[[builtin(position)]]
position: vec4<f32>;
Expand All @@ -18,34 +20,6 @@ var hdr_texture: texture_2d<f32>;
[[group(0), binding(1)]]
var hdr_sampler: sampler;

// 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);
}

[[stage(fragment)]]
fn fs_main(in: VertexOutput) -> [[location(0)]] vec4<f32> {
let hdr_color = textureSample(hdr_texture, hdr_sampler, in.uv);
Expand Down
27 changes: 27 additions & 0 deletions crates/bevy_core_pipeline/src/tonemapping/tonemapping_shared.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// from https://64.github.io/tonemapping/
// reinhard on RGB oversaturates colors
fn tonemapping_reinhard(color: vec3<f32>) -> vec3<f32> {
return color / (1.0 + color);
}

fn tonemapping_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 tonemapping_luminance(v: vec3<f32>) -> f32 {
return dot(v, vec3<f32>(0.2126, 0.7152, 0.0722));
}

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

fn reinhard_luminance(color: vec3<f32>) -> vec3<f32> {
let l_old = tonemapping_luminance(color);
let l_new = l_old / (1.0 + l_old);
return tonemapping_change_luminance(color, l_new);
}
3 changes: 2 additions & 1 deletion crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ 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 mesh_key = MeshPipelineKey::from_msaa_samples(msaa.samples);
let mesh_key =
MeshPipelineKey::from_msaa_samples(msaa.samples) | MeshPipelineKey::from_hdr(view.hdr);

for visible_entity in &visible_entities.entities {
if let Ok((material_handle, mesh_handle, mesh_uniform)) =
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,7 @@ pub fn prepare_lights(
projection: cube_face_projection,
near: POINT_LIGHT_NEAR_Z,
far: light.range,
hdr: false,
},
RenderPhase::<Shadow>::default(),
LightEntity::Point {
Expand Down Expand Up @@ -834,6 +835,7 @@ pub fn prepare_lights(
projection,
near: light.near,
far: light.far,
hdr: false,
},
RenderPhase::<Shadow>::default(),
LightEntity::Directional { light_entity },
Expand Down
20 changes: 19 additions & 1 deletion crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ bitflags::bitflags! {
const NONE = 0;
const VERTEX_TANGENTS = (1 << 0);
const TRANSPARENT_MAIN_PASS = (1 << 1);
const HDR = (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 All @@ -380,6 +381,14 @@ impl MeshPipelineKey {
MeshPipelineKey::from_bits(msaa_bits).unwrap()
}

pub fn from_hdr(hdr: bool) -> Self {
if hdr {
MeshPipelineKey::HDR
} else {
MeshPipelineKey::NONE
}
}

pub fn msaa_samples(&self) -> u32 {
((self.bits >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS) + 1
}
Expand Down Expand Up @@ -489,6 +498,15 @@ impl SpecializedPipeline for MeshPipeline {
#[cfg(feature = "webgl")]
shader_defs.push(String::from("NO_ARRAY_TEXTURES_SUPPORT"));

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

let format = match key.contains(MeshPipelineKey::HDR) {
true => ViewTarget::TEXTURE_FORMAT_HDR,
false => TextureFormat::bevy_default(),
};

RenderPipelineDescriptor {
vertex: VertexState {
shader: MESH_SHADER_HANDLE.typed::<Shader>(),
Expand All @@ -505,7 +523,7 @@ impl SpecializedPipeline for MeshPipeline {
shader_defs,
entry_point: "fragment".into(),
targets: vec![ColorTargetState {
format: ViewTarget::TEXTURE_FORMAT_HDR,
format,
blend,
write_mask: ColorWrites::ALL,
}],
Expand Down
22 changes: 18 additions & 4 deletions crates/bevy_pbr/src/render/pbr.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
#import bevy_pbr::mesh_view_bind_group
#import bevy_pbr::mesh_struct

#ifdef TONEMAPPING_IN_PBR_SHADER
#import bevy_core_pipeline::tonemapping
#endif

[[group(2), binding(0)]]
var<uniform> mesh: Mesh;

Expand Down Expand Up @@ -155,8 +159,7 @@ fn fresnel(f0: vec3<f32>, LoH: f32) -> vec3<f32> {

// Cook-Torrance approximation of the microfacet model integration using Fresnel law F to model f_m
// f_r(v,l) = { D(h,α) G(v,l,α) F(v,h,f0) } / { 4 (n⋅v) (n⋅l) }
fn specular(f0: vec3<f32>, roughness: f32, h: vec3<f32>, NoV: f32, NoL: f32,
NoH: f32, LoH: f32, specularIntensity: f32) -> vec3<f32> {
fn specular(f0: vec3<f32>, roughness: f32, h: vec3<f32>, NoV: f32, NoL: f32, NoH: f32, LoH: f32, specularIntensity: f32) -> vec3<f32> {
let D = D_GGX(roughness, NoH, h);
let V = V_SmithGGXCorrelated(roughness, NoV, NoL);
let F = fresnel(f0, LoH);
Expand Down Expand Up @@ -243,8 +246,15 @@ fn get_light_id(index: u32) -> u32 {
}

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>
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>,
) -> vec3<f32> {
let light_to_frag = light.position_radius.xyz - world_position.xyz;
let distance_square = dot(light_to_frag, light_to_frag);
Expand Down Expand Up @@ -592,5 +602,9 @@ fn fragment(in: FragmentInput) -> [[location(0)]] vec4<f32> {
#endif // CLUSTERED_FORWARD_DEBUG_CLUSTER_COHERENCY
}

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

return output_color;
}
3 changes: 3 additions & 0 deletions crates/bevy_render/src/camera/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ impl PerspectiveCameraBundle {
name: Some(name.to_string()),
near: perspective_projection.near,
far: perspective_projection.far,
hdr: true,
..Default::default()
},
perspective_projection,
Expand Down Expand Up @@ -98,6 +99,7 @@ impl OrthographicCameraBundle {
name: Some(CameraPlugin::CAMERA_2D.to_string()),
near: orthographic_projection.near,
far: orthographic_projection.far,
hdr: true,
..Default::default()
},
orthographic_projection,
Expand Down Expand Up @@ -126,6 +128,7 @@ impl OrthographicCameraBundle {
name: Some(CameraPlugin::CAMERA_3D.to_string()),
near: orthographic_projection.near,
far: orthographic_projection.far,
hdr: true,
..Default::default()
},
orthographic_projection,
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 @@ -25,6 +25,7 @@ pub struct Camera {
pub depth_calculation: DepthCalculation,
pub near: f32,
pub far: f32,
pub hdr: bool,
}

#[derive(Debug, Clone, Copy, Reflect, Serialize, Deserialize)]
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_render/src/camera/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ fn extract_cameras(
height: window.physical_height().max(1),
near: camera.near,
far: camera.far,
hdr: camera.hdr,
},
visible_entities.clone(),
));
Expand Down
Loading

0 comments on commit 11c326a

Please sign in to comment.