-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
HDR Display Output #22563
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
HDR Display Output #22563
Changes from 5 commits
3d2c0a0
63896b0
15b5767
c607de6
6a45fe1
feb4cb9
69c693c
0def855
328c7c7
c8a35c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -51,7 +51,7 @@ fn rgb_to_ycbcr(col: vec3<f32>) -> vec3<f32> { | |
| fn tonemap_curve(v: f32) -> f32 { | ||
| #ifdef 0 | ||
| // Large linear part in the lows, but compresses highs. | ||
| float c = v + v * v + 0.5 * v * v * v; | ||
| let c = v + v * v + 0.5 * v * v * v; | ||
| return c / (1.0 + c); | ||
| #else | ||
| return 1.0 - exp(-v); | ||
|
|
@@ -251,6 +251,31 @@ fn tonemapping_reinhard_luminance(color: vec3<f32>) -> vec3<f32> { | |
| return tonemapping_change_luminance(color, l_new); | ||
| } | ||
|
|
||
| fn tonemapping_pq(color: vec3<f32>, color_grading: ColorGrading) -> vec3<f32> { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
| // PQ (Perceptual Quantizer) / Rec. 2100 HDR. | ||
| // We assume the input is linear Rec. 709 (sRGB primaries) and we want to output scRGB. | ||
| // scRGB is linear, but it uses sRGB primaries and is scaled such that 1.0 is 80 nits. | ||
| // However, modern HDR displays often expect 1.0 to be "paper white" or a specific nit value. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The display doesn't matter, it's what the OS wants (as the OS is going to convert scRGB to PQ when compositing). Paper white is also not a property of the display, it's just a simple way for the user to set average scene brightness. |
||
| // scRGB is often defined as 1.0 = 80 nits, but Bevy's PBR expects 1.0 = 1 lux (or similar physically based unit). | ||
| // The `paper_white` parameter allows us to calibrate what 1.0 in the shader means in nits. | ||
|
|
||
| // For now, let's implement a simple HDR pass-through that scales the linear color. | ||
| // If we want actual PQ encoding, we would need to convert to Rec.2020 and apply the PQ curve. | ||
| // But many HDR APIs (like Windows scRGB or macOS HDR) accept linear values. | ||
|
|
||
| // We scale the color based on the ratio between the calibrated paper white and the 80 nits | ||
| // that scRGB defines as 1.0. | ||
| let paper_white_nits = color_grading.paper_white; | ||
| let max_luminance_nits = color_grading.max_luminance; | ||
|
|
||
| var out_color = color * (paper_white_nits / 80.0); | ||
|
|
||
| // Hard clip at max luminance | ||
| out_color = min(out_color, vec3(max_luminance_nits / 80.0)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For that matter, scRGB doesn't really care about max luminance either, the OS will just clip. Max luminance only really matters if the tonemapper is doing some sort of 'highlight shaping' to fit the highlights within whatever dynamic range we have over paper white. |
||
|
|
||
| return out_color; | ||
| } | ||
|
|
||
| fn rgb_to_srgb_simple(color: vec3<f32>) -> vec3<f32> { | ||
| return pow(color, vec3<f32>(1.0 / 2.2)); | ||
| } | ||
|
|
@@ -357,6 +382,8 @@ fn tone_mapping(in: vec4<f32>, in_color_grading: ColorGrading) -> vec4<f32> { | |
| // tone_mapping | ||
| #ifdef TONEMAP_METHOD_NONE | ||
| color = color; | ||
| #else ifdef TONEMAP_METHOD_PQ | ||
| color = tonemapping_pq(color, color_grading); | ||
| #else ifdef TONEMAP_METHOD_REINHARD | ||
| color = tonemapping_reinhard(color.rgb); | ||
| #else ifdef TONEMAP_METHOD_REINHARD_LUMINANCE | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PQ is the display transfer function, which we aren't even using anywhere, so this should probably be named something like
HdrNone