Skip to content

Conversation

@aevyrie
Copy link
Member

@aevyrie aevyrie commented Jan 17, 2026

Objective

  • HDR support. Extra bright pixels.

Solution

  • Is it good? Probably not.
  • Is it passable? Who can say, really.
  • Everything I've read suggests this is really, really hard to do correctly. It might be nice to have an okay implementation we can iterate on to start.

Testing

  • cargo run --example hdr_calibration
Screen.Recording.2026-01-16.at.11.23.42.PM.mov
Screen.Recording.2026-01-17.at.1.02.37.AM.mov

@aevyrie aevyrie marked this pull request as draft January 17, 2026 07:25
@alice-i-cecile alice-i-cecile added A-Rendering Drawing game state to the screen M-Release-Note Work that should be called out in the blog due to impact S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Jan 17, 2026
@alice-i-cecile alice-i-cecile added the C-Feature A new feature, making something new possible label Jan 17, 2026
@github-actions
Copy link
Contributor

It looks like your PR has been selected for a highlight in the next release blog post, but you didn't provide a release note.

Please review the instructions for writing release notes, then expand or revise the content in the release notes directory to showcase your changes.

return tonemapping_change_luminance(color, l_new);
}

fn tonemapping_pq(color: vec3<f32>, color_grading: ColorGrading) -> vec3<f32> {
Copy link
Member Author

@aevyrie aevyrie Jan 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I played with this a bit, needs love. Linear seems to just work ™️ on macOS.

@aevyrie aevyrie marked this pull request as ready for review January 17, 2026 07:34
@github-actions
Copy link
Contributor

Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke!
You can review it at https://pixel-eagle.com/project/B04F67C0-C054-4A6F-92EC-F599FEC2FD1D?filter=PR-22563

If it's expected, please add the M-Deliberate-Rendering-Change label.

If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it.

@johansigfrids
Copy link

johansigfrids commented Jan 17, 2026

out_color = min(out_color, vec3(max_luminance_nits / 80.0));

Does this clip each channel independently?

For example having (1500, 1200, 500) clipped to max 1000 gives (1000, 1000, 500).

That would shift the hue since the color proportions change.

@JMS55 JMS55 self-requested a review January 17, 2026 15:04
@github-actions
Copy link
Contributor

Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke!
You can review it at https://pixel-eagle.com/project/B04F67C0-C054-4A6F-92EC-F599FEC2FD1D?filter=PR-22563

If it's expected, please add the M-Deliberate-Rendering-Change label.

If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it.

@aevyrie aevyrie added M-Deliberate-Rendering-Change An intentional change to how tests and examples are rendered and removed M-Deliberate-Rendering-Change An intentional change to how tests and examples are rendered labels Jan 21, 2026
@aevyrie
Copy link
Member Author

aevyrie commented Jan 21, 2026

Copy link
Contributor

@jasmine-nominal jasmine-nominal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initial review.

Some stuff still TODO for me:

  • Read up on HDR stuff again, it's been a while
  • Check if Pq is a good tonemapping method
  • Try across various displays, especially on windows/linux (linux HDR support is a little rough iirc)
  • Figure out how this interacts with color spaces/gamut
  • Figure out if we need anything from wgpu for this
  • Figure out how this work on WebGPU

Also question: How can users check at runtime if HDR is supported or not? Can you figure that out, and then add some text to the example for when HDR is not supported?

let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
| MeshPipelineKey::from_hdr(view.hdr);

if view.hdr_output {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need the view key stuff in a lot more places? E.g. meshlet shading passes.

for (camera, mut uniforms, bloom) in &mut views {
uniforms.hdr_output = camera.hdr_output as u32;

if camera.hdr_output && bloom.prefilter.threshold == 0.0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for?

if key.hdr {
ViewTarget::TEXTURE_FORMAT_HDR
} else {
TextureFormat::bevy_default()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait why does bloom support non-floating point (HDR) internal textures now?

Also I would really like to rename HDR now...

format = available_format;
break;

if window.hdr_output {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please abstract this out into a get_surface_format function.

if window.hdr_output {
for available_format in &formats {
if *available_format == TextureFormat::Rgba16Float {
format = *available_format;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about non-Rgba16Float HDR swapchain formats? Iirc some platforms/monitors/whatever only support other formats.

#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct BoxShadowPipelineKey {
pub hdr: bool,
/// Number of samples, a higher value results in better quality shadows.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment was deleted.

] }
bevy_transform = { path = "../bevy_transform", version = "0.19.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.19.0-dev" }
bevy_window = { path = "../bevy_window", version = "0.19.0-dev" }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@atlv24 is this ok?

@jasmine-nominal
Copy link
Contributor

I don't really understand how to use the calibration example (am I supposed to google the values for my monitor?

Would it be better to implement something like this?
image

@jasmine-nominal
Copy link
Contributor

Also, does putting the settings on ColorGrading make sense? Shouldn't they be per monitor somehow?

@jasmine-nominal
Copy link
Contributor

The max luminance left/right controls in the demo seem inverted to how I expect.

@jasmine-nominal
Copy link
Contributor

Is max luminance supposed to be sustained, or peak?

@aevyrie
Copy link
Member Author

aevyrie commented Jan 21, 2026

The max luminance is just max luminance, it's up to the display if it is able to maintain that or clip. The user can also adjust the display brightness, which will override the actual luminance. You can see this if you open the example in a dark room on a macbook and play with the display brightness. As far as I understand it, the luminance values are for the case where you have a fully calibrated reference hardware pipeline, in reality there are a bunch of things that happen between the rendered f16 output and the pixels lighting up on the display that will cause that to differ.

I don't really understand how to use the calibration example (am I supposed to google the values for my monitor?

You're just tweaking how bright paper white should be in a scene and how bright you want peak brightness to be. You can "calibrate" it, but most games let you tweak this yourself in game at runtime.

The max luminance left/right controls in the demo seem inverted to how I expect.

Right increases, left decreases. It's also increasing the range so the fact that paper white level (red) moves the other direction might be confusing you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Rendering Drawing game state to the screen C-Feature A new feature, making something new possible M-Release-Note Work that should be called out in the blog due to impact S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

4 participants