Fix inverse tone mapping issues (#4437)

Bring color grading back into the Rec.709 color space to match
previous behaviors. This change also implements an exact inverse
tone map function for the "Filmic" operator.
This commit is contained in:
Romain Guy
2021-08-02 18:53:35 -07:00
committed by Benjamin Doherty
parent f190f03530
commit 1c51164e7b
7 changed files with 29 additions and 23 deletions

View File

@@ -2641,8 +2641,8 @@ The following APIs are only available from the fragment block:
<tr><td style="text-align:left"> <strong class="asterisk">getUV0()</strong> </td><td style="text-align:center"> float2 </td><td style="text-align:left"> First interpolated set of UV coordinates, only available if the uv0 attribute is required </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getUV1()</strong> </td><td style="text-align:center"> float2 </td><td style="text-align:left"> First interpolated set of UV coordinates, only available if the uv1 attribute is required </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getMaskThreshold()</strong> </td><td style="text-align:center"> float </td><td style="text-align:left"> Returns the mask threshold, only available when <code>blending</code> is set to <code>masked</code> </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">inverseTonemap(float3)</strong> </td><td style="text-align:center"> float3 </td><td style="text-align:left"> Applies the inverse tone mapping operator to the specified linear sRGB color and returns a linear sRGB color. This operation may be an approximation </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">inverseTonemapSRGB(float3)</strong> </td><td style="text-align:center"> float3 </td><td style="text-align:left"> Applies the inverse tone mapping operator to the specified non-linear sRGB color and returns a linear sRGB color. This operation may be an approximation </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">inverseTonemap(float3)</strong> </td><td style="text-align:center"> float3 </td><td style="text-align:left"> Applies the inverse tone mapping operator to the specified linear sRGB color and returns a linear sRGB color. This operation may be an approximation and works best with the “Filmic” tone mapping operator </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">inverseTonemapSRGB(float3)</strong> </td><td style="text-align:center"> float3 </td><td style="text-align:left"> Applies the inverse tone mapping operator to the specified non-linear sRGB color and returns a linear sRGB color. This operation may be an approximation and works best with the “Filmic” tone mapping operator </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">luminance(float3)</strong> </td><td style="text-align:center"> float </td><td style="text-align:left"> Computes the luminance of the specified linear sRGB color </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">ycbcrToRgb(float, float2)</strong> </td><td style="text-align:center"> float3 </td><td style="text-align:left"> Converts a luminance and CbCr pair to a sRGB color </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">uvToRenderTargetUV(float2)</strong> </td><td style="text-align:center"> float2 </td><td style="text-align:left"> Transforms a UV coordinate to allow sampling from a <code>RenderTarget</code> attachment </td></tr>

View File

@@ -2179,8 +2179,8 @@ The following APIs are only available from the fragment block:
**getUV0()** | float2 | First interpolated set of UV coordinates, only available if the uv0 attribute is required
**getUV1()** | float2 | First interpolated set of UV coordinates, only available if the uv1 attribute is required
**getMaskThreshold()** | float | Returns the mask threshold, only available when `blending` is set to `masked`
**inverseTonemap(float3)** | float3 | Applies the inverse tone mapping operator to the specified linear sRGB color and returns a linear sRGB color. This operation may be an approximation
**inverseTonemapSRGB(float3)** | float3 | Applies the inverse tone mapping operator to the specified non-linear sRGB color and returns a linear sRGB color. This operation may be an approximation
**inverseTonemap(float3)** | float3 | Applies the inverse tone mapping operator to the specified linear sRGB color and returns a linear sRGB color. This operation may be an approximation and works best with the "Filmic" tone mapping operator
**inverseTonemapSRGB(float3)** | float3 | Applies the inverse tone mapping operator to the specified non-linear sRGB color and returns a linear sRGB color. This operation may be an approximation and works best with the "Filmic" tone mapping operator
**luminance(float3)** | float | Computes the luminance of the specified linear sRGB color
**ycbcrToRgb(float, float2)** | float3 | Converts a luminance and CbCr pair to a sRGB color
**uvToRenderTargetUV(float2)** | float2 | Transforms a UV coordinate to allow sampling from a `RenderTarget` attachment

View File

@@ -517,8 +517,7 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
v = chromaticAdaptation(v, config.adaptationTransform);
}
// Convert to color grading color space
v = sRGB_to_REC2020 * v;
// TODO: We should convert to a color grading color space here
if (builder->hasAdjustments) {
// Kill negative values before the next transforms
@@ -528,7 +527,7 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
v = channelMixer(v, builder->outRed, builder->outGreen, builder->outBlue);
// Shadows/mid-tones/highlights
v = tonalRanges(v, LUMA_REC2020,
v = tonalRanges(v, LUMA_REC709,
builder->shadows, builder->midtones, builder->highlights,
builder->tonalRanges);
@@ -546,10 +545,10 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
v = LogC_to_linear(v);
// Vibrance in linear space
v = vibrance(v, LUMA_REC2020, builder->vibrance);
v = vibrance(v, LUMA_REC709, builder->vibrance);
// Saturation in linear space
v = saturation(v, LUMA_REC2020, builder->saturation);
v = saturation(v, LUMA_REC709, builder->saturation);
// Kill negative values before tone mapping
v = max(v, 0.0f);
@@ -566,9 +565,9 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
v = (*builder->toneMapper)(v);
}
// Convert to output color space
// TODO: allow to customize the output color space,
v = REC2020_to_sRGB * v;
// TODO: We should convert to the output color space if we use a working
// color space that's not sRGB
// TODO: Allow the user to customize the output color space
v = saturate(v);

View File

@@ -112,6 +112,18 @@ constexpr mat3f sRGB_to_AP1{
0.04737f, 0.01345f, 0.86961f
};
constexpr mat3f AP0_to_sRGB{
2.52169f, -0.27648f, -0.01538f,
-1.13413f, 1.37272f, -0.15298f,
-0.38756f, -0.09624f, 1.16835f
};
constexpr mat3f sRGB_to_AP0{
0.4397010f, 0.0897923f, 0.0175440f,
0.3829780f, 0.8134230f, 0.1115440f,
0.1773350f, 0.0967616f, 0.8707040f
};
constexpr mat3f sRGB_to_REC2020 = XYZ_to_REC2020 * sRGB_to_XYZ;
constexpr mat3f REC2020_to_sRGB = XYZ_to_sRGB * REC2020_to_XYZ;

View File

@@ -135,7 +135,7 @@ float3 ACES(float3 color, float brightness) noexcept {
constexpr float RRT_SAT_FACTOR = 0.96f;
constexpr float ODT_SAT_FACTOR = 0.93f;
float3 ap0 = REC2020_to_AP0 * color;
float3 ap0 = sRGB_to_AP0 * color;
// Glow module
float saturation = rgb_2_saturation(ap0);
@@ -177,7 +177,7 @@ float3 ACES(float3 color, float brightness) noexcept {
// Apply desaturation to compensate for luminance difference
linearCV = mix(float3(dot(linearCV, LUMA_AP1)), linearCV, ODT_SAT_FACTOR);
return AP1_to_REC2020 * linearCV;
return AP1_to_sRGB * linearCV;
}
} // namespace aces

View File

@@ -343,10 +343,6 @@ static void createGroundPlane(Engine* engine, Scene* scene, App& app) {
app.scene.groundMaterial = shadowMaterial;
}
static LinearColor inverseTonemapSRGB(sRGBColor x) {
return (x * -0.155) / (x - 1.019);
}
static float sGlobalScale = 1.0f;
static float sGlobalScaleAnamorphism = 0.0f;

View File

@@ -47,8 +47,8 @@ vec3 ycbcrToRgb(float luminance, vec2 cbcr) {
/*
* The input must be in the [0, 1] range.
*/
vec3 Inverse_Tonemap_Unreal(const vec3 x) {
return (x * -0.155) / (x - 1.019);
vec3 Inverse_Tonemap_Filmic(const vec3 x) {
return (0.03 - 0.59 * x - sqrt(0.0009 + 1.3702 * x - 1.0127 * x * x)) / (-5.02 + 4.86 * x);
}
/**
@@ -61,7 +61,7 @@ vec3 Inverse_Tonemap_Unreal(const vec3 x) {
vec3 inverseTonemapSRGB(vec3 color) {
// sRGB input
color = clamp(color, 0.0, 1.0);
return Inverse_Tonemap_Unreal(color);
return Inverse_Tonemap_Filmic(pow(color, vec3(2.2)));
}
/**
@@ -73,8 +73,7 @@ vec3 inverseTonemapSRGB(vec3 color) {
*/
vec3 inverseTonemap(vec3 linear) {
// Linear input
linear = clamp(linear, 0.0, 1.0);
return Inverse_Tonemap_Unreal(pow(linear, vec3(1.0 / 2.2)));
return Inverse_Tonemap_Filmic(clamp(linear, 0.0, 1.0));
}
//------------------------------------------------------------------------------