RetroArch/gfx/drivers/vulkan_shaders/hdr.frag
2023-02-23 13:15:14 +01:00

109 lines
3.9 KiB
GLSL

#version 310 es
precision highp float;
layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 2) uniform highp sampler2D Source;
layout(std140, set = 0, binding = 0) uniform UBO
{
mat4 MVP;
float contrast; /* 2.0f; */
float paper_white_nits; /* 200.0f; */
float max_nits; /* 1000.0f; */
float expand_gamut; /* 1.0f; */
float inverse_tonemap;
float hdr10;
} global;
/* Inverse Tonemap section */
#define kMaxNitsFor2084 10000.0f
#define kEpsilon 0.0001f
#define kLumaChannelRatio 0.25f
vec3 InverseTonemap(vec3 sdr)
{
vec3 hdr;
if (global.inverse_tonemap > 0.0f)
{
sdr = pow(abs(sdr), vec3(global.contrast / 2.2f)); /* Display Gamma - needs to be determined by calibration screen */
float luma = dot(sdr, vec3(0.2126, 0.7152, 0.0722)); /* Rec BT.709 luma coefficients - https://en.wikipedia.org/wiki/Luma_(video) */
/* Inverse reinhard tonemap */
float maxValue = (global.max_nits / global.paper_white_nits) + kEpsilon;
float elbow = maxValue / (maxValue - 1.0f);
float offset = 1.0f - ((0.5f * elbow) / (elbow - 0.5f));
float hdrLumaInvTonemap = offset + ((luma * elbow) / (elbow - luma));
float sdrLumaInvTonemap = luma / ((1.0f + kEpsilon) - luma); /* Convert the srd < 0.5 to 0.0 -> 1.0 range */
float lumaInvTonemap = (luma > 0.5f) ? hdrLumaInvTonemap : sdrLumaInvTonemap;
vec3 perLuma = sdr / (luma + kEpsilon) * lumaInvTonemap;
vec3 hdrInvTonemap = offset + ((sdr * elbow) / (elbow - sdr));
vec3 sdrInvTonemap = sdr / ((1.0f + kEpsilon) - sdr); /* Convert the srd < 0.5 to 0.0 -> 1.0 range */
vec3 perChannel = vec3(sdr.x > 0.5f ? hdrInvTonemap.x : sdrInvTonemap.x,
sdr.y > 0.5f ? hdrInvTonemap.y : sdrInvTonemap.y,
sdr.z > 0.5f ? hdrInvTonemap.z : sdrInvTonemap.z);
hdr = mix(perLuma, perChannel, vec3(kLumaChannelRatio));
}
else
{
hdr = sdr;
}
return hdr;
}
/* HDR10 section */
#define kMaxNitsFor2084 10000.0f
const mat3 k709to2020 = mat3 (
0.6274040f, 0.3292820f, 0.0433136f,
0.0690970f, 0.9195400f, 0.0113612f,
0.0163916f, 0.0880132f, 0.8955950f);
/* START Converted from (Copyright (c) Microsoft Corporation - Licensed under the MIT License.) https://github.com/microsoft/Xbox-ATG-Samples/tree/master/Kits/ATGTK/HDR */
const mat3 kExpanded709to2020 = mat3 (
0.6274040f, 0.3292820f, 0.0433136f,
0.0457456, 0.941777, 0.0124772,
-0.00121055, 0.0176041, 0.983607);
vec3 LinearToST2084(vec3 normalizedLinearValue)
{
vec3 ST2084 = pow((0.8359375f + 18.8515625f * pow(abs(normalizedLinearValue), vec3(0.1593017578f))) / (1.0f + 18.6875f * pow(abs(normalizedLinearValue), vec3(0.1593017578f))), vec3(78.84375f));
return ST2084; /* Don't clamp between [0..1], so we can still perform operations on scene values higher than 10,000 nits */
}
/* END Converted from (Copyright (c) Microsoft Corporation - Licensed under the MIT License.) https://github.com/microsoft/Xbox-ATG-Samples/tree/master/Kits/ATGTK/HDR */
vec3 Hdr10(vec3 hdr)
{
vec3 hdr10;
if (global.hdr10 > 0.0f)
{
/* Now convert into HDR10 */
vec3 rec2020 = hdr * k709to2020;
if (global.expand_gamut > 0.0f)
rec2020 = hdr * kExpanded709to2020;
vec3 linearColour = rec2020 * (global.paper_white_nits / kMaxNitsFor2084);
hdr10 = LinearToST2084(linearColour);
}
else
hdr10 = hdr;
return hdr10;
}
void main()
{
vec4 sdr = texture(Source, vTexCoord);
vec4 hdr = vec4(InverseTonemap(sdr.rgb), sdr.a);
FragColor = vec4(Hdr10(hdr.rgb), hdr.a);
}