1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-11 13:10:58 +00:00

Always perform color correction and debanding on nonlinear sRGB values.

Fixes #107730

Co-authored-by: LuoZhihao <luo_zhihao@outlook.com>
This commit is contained in:
Allen Pestaluky
2025-07-07 16:38:17 -04:00
parent 4d1f26e1fd
commit a1591512f8
15 changed files with 82 additions and 20 deletions

View File

@@ -10,7 +10,6 @@ layout(push_constant, std140) uniform Pos {
float rotation_sin;
float rotation_cos;
vec2 pad;
vec2 eye_center;
float k1;
@@ -20,6 +19,8 @@ layout(push_constant, std140) uniform Pos {
float aspect_ratio;
uint layer;
bool convert_to_srgb;
bool use_debanding;
float pad;
}
data;
@@ -50,7 +51,6 @@ layout(push_constant, std140) uniform Pos {
float rotation_sin;
float rotation_cos;
vec2 pad;
vec2 eye_center;
float k1;
@@ -60,6 +60,8 @@ layout(push_constant, std140) uniform Pos {
float aspect_ratio;
uint layer;
bool convert_to_srgb;
bool use_debanding;
float pad;
}
data;
@@ -74,12 +76,27 @@ layout(binding = 0) uniform sampler2D src_rt;
#endif
vec3 linear_to_srgb(vec3 color) {
// If going to srgb, clamp from 0 to 1.
color = clamp(color, vec3(0.0), vec3(1.0));
const vec3 a = vec3(0.055f);
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
}
// From https://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf
// and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom)
// NOTE: `frag_coord` is in pixels (i.e. not normalized UV).
// This dithering must be applied after encoding changes (linear/nonlinear) have been applied
// as the final step before quantization from floating point to integer values.
vec3 screen_space_dither(vec2 frag_coord) {
// Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR.
// Removed the time component to avoid passing time into this shader.
vec3 dither = vec3(dot(vec2(171.0, 231.0), frag_coord));
dither.rgb = fract(dither.rgb / vec3(103.0, 71.0, 97.0));
// Subtract 0.5 to avoid slightly brightening the whole viewport.
// Use a dither strength of 100% rather than the 37.5% suggested by the original source.
// Divide by 255 to align to 8-bit quantization.
return (dither.rgb - 0.5) / 255.0;
}
void main() {
#ifdef APPLY_LENS_DISTORTION
vec2 coords = uv * 2.0 - 1.0;
@@ -118,5 +135,10 @@ void main() {
if (data.convert_to_srgb) {
color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
// When convert_to_srgb is true, debanding was skipped in tonemap.glsl.
if (data.use_debanding) {
color.rgb += screen_space_dither(gl_FragCoord.xy);
}
color.rgb = clamp(color.rgb, vec3(0.0), vec3(1.0));
}
}

View File

@@ -328,7 +328,8 @@ vec3 tonemap_agx(vec3 color) {
}
vec3 linear_to_srgb(vec3 color) {
//if going to srgb, clamp from 0 to 1.
// Clamping is not strictly necessary for floating point nonlinear sRGB encoding,
// but many cases that call this function need the result clamped.
color = clamp(color, vec3(0.0), vec3(1.0));
const vec3 a = vec3(0.055f);
return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
@@ -816,12 +817,17 @@ vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) {
// From https://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf
// and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom)
// NOTE: `frag_coord` is in pixels (i.e. not normalized UV).
// This dithering must be applied after encoding changes (linear/nonlinear) have been applied
// as the final step before quantization from floating point to integer values.
vec3 screen_space_dither(vec2 frag_coord) {
// Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR.
// Removed the time component to avoid passing time into this shader.
vec3 dither = vec3(dot(vec2(171.0, 231.0), frag_coord));
dither.rgb = fract(dither.rgb / vec3(103.0, 71.0, 97.0));
// Subtract 0.5 to avoid slightly brightening the whole viewport.
// Use a dither strength of 100% rather than the 37.5% suggested by the original source.
// Divide by 255 to align to 8-bit quantization.
return (dither.rgb - 0.5) / 255.0;
}
@@ -866,7 +872,8 @@ void main() {
color.rgb = apply_tonemapping(color.rgb, params.white);
if (bool(params.flags & FLAG_CONVERT_TO_SRGB)) {
bool convert_to_srgb = bool(params.flags & FLAG_CONVERT_TO_SRGB);
if (convert_to_srgb) {
color.rgb = linear_to_srgb(color.rgb); // Regular linear -> SRGB conversion.
}
#ifndef SUBPASS
@@ -879,7 +886,7 @@ void main() {
// high dynamic range -> SRGB
glow = apply_tonemapping(glow, params.white);
if (bool(params.flags & FLAG_CONVERT_TO_SRGB)) {
if (convert_to_srgb) {
glow = linear_to_srgb(glow);
}
@@ -894,7 +901,13 @@ void main() {
}
if (bool(params.flags & FLAG_USE_COLOR_CORRECTION)) {
// apply_color_correction requires nonlinear sRGB encoding
if (!convert_to_srgb) {
color.rgb = linear_to_srgb(color.rgb);
}
color.rgb = apply_color_correction(color.rgb);
// When convert_to_srgb is false, there is no need to convert back to
// linear because the color correction texture sampling does this for us.
}
if (bool(params.flags & FLAG_USE_DEBANDING)) {