diff --git a/android/filament-android/src/main/cpp/ColorGrading.cpp b/android/filament-android/src/main/cpp/ColorGrading.cpp index 91c3e3ad76..d690216bb5 100644 --- a/android/filament-android/src/main/cpp/ColorGrading.cpp +++ b/android/filament-android/src/main/cpp/ColorGrading.cpp @@ -76,6 +76,13 @@ Java_com_google_android_filament_ColorGrading_nBuilderLuminanceScaling(JNIEnv*, builder->luminanceScaling(luminanceScaling); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_ColorGrading_nBuilderGamutMapping(JNIEnv*, jclass, + jlong nativeBuilder, jboolean gamutMapping) { + ColorGrading::Builder* builder = (ColorGrading::Builder*) nativeBuilder; + builder->gamutMapping(gamutMapping); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_ColorGrading_nBuilderExposure(JNIEnv*, jclass, jlong nativeBuilder, jfloat exposure) { diff --git a/android/filament-android/src/main/java/com/google/android/filament/ColorGrading.java b/android/filament-android/src/main/java/com/google/android/filament/ColorGrading.java index 01401a1eaa..ca45d37019 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/ColorGrading.java +++ b/android/filament-android/src/main/java/com/google/android/filament/ColorGrading.java @@ -65,6 +65,7 @@ import static com.google.android.filament.Asserts.assertFloat4In; *
  • Curves
  • *
  • Tone mapping
  • *
  • Luminance scaling
  • + *
  • Gamut mapping
  • * * *

    Defaults

    @@ -83,6 +84,7 @@ import static com.google.android.filament.Asserts.assertFloat4In; *
  • Curves: gamma {1,1,1}, midPoint {1,1,1}, and scale {1,1,1}
  • *
  • Tone mapping: {@link ToneMapper.ACESLegacy}
  • *
  • Luminance scaling: false
  • + *
  • Gamut mapping: false
  • * * * @see View @@ -213,6 +215,22 @@ public class ColorGrading { return this; } + /** + * Enables or disables gamut mapping to the destination color space's gamut. When gamut + * mapping is turned off, out-of-gamut colors are clipped to the destination's gamut, + * which may produce hue skews (blue skewing to purple, green to yellow, etc.). When + * gamut mapping is enabled, out-of-gamut colors are brought back in gamut by trying to + * preserve the perceived chroma and lightness of the original values. + * + * @param gamutMapping Enables or disables gamut mapping + * + * @return This Builder, for chaining calls + */ + public Builder gamutMapping(boolean gamutMapping) { + nBuilderGamutMapping(mNativeBuilder, gamutMapping); + return this; + } + /** * Adjusts the exposure of this image. The exposure is specified in stops: * each stop brightens (positive values) or darkens (negative values) the image by @@ -524,6 +542,7 @@ public class ColorGrading { private static native void nBuilderToneMapper(long nativeBuilder, long toneMapper); private static native void nBuilderToneMapping(long nativeBuilder, int toneMapping); private static native void nBuilderLuminanceScaling(long nativeBuilder, boolean luminanceScaling); + private static native void nBuilderGamutMapping(long nativeBuilder, boolean gamutMapping); private static native void nBuilderExposure(long nativeBuilder, float exposure); private static native void nBuilderWhiteBalance(long nativeBuilder, float temperature, float tint); private static native void nBuilderChannelMixer(long nativeBuilder, float[] outRed, float[] outGreen, float[] outBlue); diff --git a/filament/include/filament/ColorGrading.h b/filament/include/filament/ColorGrading.h index 9b583a5e54..1f54741537 100644 --- a/filament/include/filament/ColorGrading.h +++ b/filament/include/filament/ColorGrading.h @@ -76,6 +76,7 @@ class FColorGrading; * - Curves * - Tone mapping * - Luminance scaling + * - Gamut mapping * * Defaults * ======== @@ -93,6 +94,7 @@ class FColorGrading; * - Curves: gamma {1,1,1}, midPoint {1,1,1}, and scale {1,1,1} * - Tone mapping: ACESLegacyToneMapper * - Luminance scaling: false + * - Gamut mapping: false * * @see View */ @@ -186,12 +188,25 @@ public: * When luminance scaling is enabled, tone mapping is performed on the luminance of each * pixel instead of per-channel. * - * @param luminanceScaling Enables or disables EVILS post-tone mapping + * @param luminanceScaling Enables or disables luminance scaling post-tone mapping * * @return This Builder, for chaining calls */ Builder& luminanceScaling(bool luminanceScaling) noexcept; + /** + * Enables or disables gamut mapping to the destination color space's gamut. When gamut + * mapping is turned off, out-of-gamut colors are clipped to the destination's gamut, + * which may produce hue skews (blue skewing to purple, green to yellow, etc.). When + * gamut mapping is enabled, out-of-gamut colors are brought back in gamut by trying to + * preserve the perceived chroma and lightness of the original values. + * + * @param gamutMapping Enables or disables gamut mapping + * + * @return This Builder, for chaining calls + */ + Builder& gamutMapping(bool gamutMapping) noexcept; + /** * Adjusts the exposure of this image. The exposure is specified in stops: * each stop brightens (positive values) or darkens (negative values) the image by diff --git a/filament/src/ColorGrading.cpp b/filament/src/ColorGrading.cpp index 8fc5732bae..804cb77c06 100644 --- a/filament/src/ColorGrading.cpp +++ b/filament/src/ColorGrading.cpp @@ -56,8 +56,9 @@ struct ColorGrading::BuilderDetails { // Everything below must be part of the == comparison operator ColorGrading::QualityLevel quality = QualityLevel::MEDIUM; - // Luminance scaling + // Out-of-gamut color handling bool luminanceScaling = false; + bool gamutMapping = false; // Exposure float exposure = 0.0f; // White balance @@ -92,6 +93,7 @@ struct ColorGrading::BuilderDetails { // Note: Do NOT compare hasAdjustments and toneMapper return quality == rhs.quality && luminanceScaling == rhs.luminanceScaling && + gamutMapping == rhs.gamutMapping && exposure == rhs.exposure && whiteBalance == rhs.whiteBalance && outRed == rhs.outRed && @@ -141,6 +143,11 @@ ColorGrading::Builder& ColorGrading::Builder::luminanceScaling(bool luminanceSca return *this; } +ColorGrading::Builder& ColorGrading::Builder::gamutMapping(bool gamutMapping) noexcept { + mImpl->gamutMapping = gamutMapping; + return *this; +} + ColorGrading::Builder& ColorGrading::Builder::exposure(float exposure) noexcept { mImpl->exposure = exposure; return *this; @@ -268,22 +275,21 @@ inline float3 adjustExposure(float3 v, float exposure) { // the von Kries method, using the CIECAT16 transform. // See https://en.wikipedia.org/wiki/Chromatic_adaptation // See https://en.wikipedia.org/wiki/CIECAM02#Chromatic_adaptation -UTILS_ALWAYS_INLINE -inline float3 adaptationTransform(float2 whiteBalance) { +constexpr mat3f adaptationTransform(float2 whiteBalance) noexcept { // See Mathematica notebook in docs/math/White Balance.nb float k = whiteBalance.x; // temperature float t = whiteBalance.y; // tint - float x = ILLUMINANT_D65_xyY.x - k * (k < 0.0f ? 0.0214f : 0.066f); + float x = ILLUMINANT_D65_xyY[0] - k * (k < 0.0f ? 0.0214f : 0.066f); float y = chromaticityCoordinateIlluminantD(x) + t * 0.066f; float3 lms = XYZ_to_CIECAT16 * xyY_to_XYZ({x, y, 1.0f}); - return ILLUMINANT_D65_LMS_CAT16 / lms; + return LMS_CAT16_to_REC2020 * mat3f{ILLUMINANT_D65_LMS_CAT16 / lms} * REC2020_to_LMS_CAT16; } UTILS_ALWAYS_INLINE -inline float3 chromaticAdaptation(float3 v, float3 adaptationTransform) { - return LMS_CAT16_to_sRGB * (adaptationTransform * (sRGB_to_LMS_CAT16 * v)); +inline float3 chromaticAdaptation(float3 v, mat3f adaptationTransform) { + return adaptationTransform * v; } //------------------------------------------------------------------------------ @@ -405,7 +411,7 @@ static float3 luminanceScaling(float3 x, // Quality //------------------------------------------------------------------------------ -size_t selectLutDimension(ColorGrading::QualityLevel quality) { +static size_t selectLutDimension(ColorGrading::QualityLevel quality) noexcept { switch (quality) { case ColorGrading::QualityLevel::LOW: return 16u; case ColorGrading::QualityLevel::MEDIUM: return 32u; @@ -414,8 +420,8 @@ size_t selectLutDimension(ColorGrading::QualityLevel quality) { } } -void selectLutTextureParams(ColorGrading::QualityLevel quality, - TextureFormat& internalFormat, PixelDataFormat& format, PixelDataType& type) { +static void selectLutTextureParams(ColorGrading::QualityLevel quality, + TextureFormat& internalFormat, PixelDataFormat& format, PixelDataType& type) noexcept { // We use RGBA16F for high quality modes instead of RGB16F because RGB16F // is not supported everywhere switch (quality) { @@ -442,13 +448,45 @@ void selectLutTextureParams(ColorGrading::QualityLevel quality, } } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +// The following functions exist to preserve backward compatibility with the +// `FILMIC` set via the deprecated `ToneMapping` API. Selecting `ToneMapping::FILMIC` +// forces post-processing to be performed in sRGB to guarantee that the inverse tone +// mapping function in the shaders will match the forward tone mapping step exactly. + +static mat3f selectColorGradingTransformIn(ColorGrading::ToneMapping toneMapping) noexcept { + if (toneMapping == ColorGrading::ToneMapping::FILMIC) { + return mat3f{}; + } + return sRGB_to_REC2020; +} + +static mat3f selectColorGradingTransformOut(ColorGrading::ToneMapping toneMapping) noexcept { + if (toneMapping == ColorGrading::ToneMapping::FILMIC) { + return mat3f{}; + } + return REC2020_to_sRGB; +} + +static float3 selectColorGradingLuminance(ColorGrading::ToneMapping toneMapping) noexcept { + if (toneMapping == ColorGrading::ToneMapping::FILMIC) { + return LUMINANCE_REC709; + } + return LUMINANCE_REC2020; +} +#pragma clang diagnostic pop + //------------------------------------------------------------------------------ // Color grading implementation //------------------------------------------------------------------------------ struct Config { size_t lutDimension; - float3 adaptationTransform; + mat3f adaptationTransform; + mat3f colorGradingIn; + mat3f colorGradingOut; + float3 colorGradingLuminance; }; // Inside the FColorGrading constructor, TSAN sporadically detects a data race on the config struct; @@ -466,8 +504,11 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) { utils::SpinLock configLock; { std::lock_guard lock(configLock); - c.lutDimension = selectLutDimension(builder->quality); - c.adaptationTransform = adaptationTransform(builder->whiteBalance); + c.lutDimension = selectLutDimension(builder->quality); + c.adaptationTransform = adaptationTransform(builder->whiteBalance); + c.colorGradingIn = selectColorGradingTransformIn(builder->toneMapping); + c.colorGradingOut = selectColorGradingTransformOut(builder->toneMapping); + c.colorGradingLuminance = selectColorGradingLuminance(builder->toneMapping); } mDimension = c.lutDimension; @@ -511,18 +552,16 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) { // LogC encoding v = LogC_to_linear(v); - // TODO: Performed in sRGB, should be in Rec.2020 or AP1 + // Move to color grading color space + v = c.colorGradingIn * v; if (builder->hasAdjustments) { // Exposure v = adjustExposure(v, builder->exposure); + // White balance v = chromaticAdaptation(v, config.adaptationTransform); - } - // TODO: We should convert to a color grading color space here - - if (builder->hasAdjustments) { // Kill negative values before the next transforms v = max(v, 0.0f); @@ -530,12 +569,11 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) { v = channelMixer(v, builder->outRed, builder->outGreen, builder->outBlue); // Shadows/mid-tones/highlights - v = tonalRanges(v, LUMA_REC709, + v = tonalRanges(v, c.colorGradingLuminance, builder->shadows, builder->midtones, builder->highlights, builder->tonalRanges); - // The adjustments below behave better in log space using the ACEScct - // color space. + // The adjustments below behave better in log space v = linear_to_LogC(v); // ASC CDL @@ -548,12 +586,12 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) { v = LogC_to_linear(v); // Vibrance in linear space - v = vibrance(v, LUMA_REC709, builder->vibrance); + v = vibrance(v, c.colorGradingLuminance, builder->vibrance); // Saturation in linear space - v = saturation(v, LUMA_REC709, builder->saturation); + v = saturation(v, c.colorGradingLuminance, builder->saturation); - // Kill negative values before tone mapping + // Kill negative values before curves v = max(v, 0.0f); // RGB curves @@ -563,15 +601,23 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) { // Tone mapping if (builder->luminanceScaling) { - v = luminanceScaling(v, *builder->toneMapper, LUMA_HK_REC709); + v = luminanceScaling(v, *builder->toneMapper, c.colorGradingLuminance); } else { v = (*builder->toneMapper)(v); } + // Go back to display color space + v = c.colorGradingOut * v; + + // Apply gamut mapping + if (builder->gamutMapping) { + // TODO: This should depend on the output color space + v = gamutMapping_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 - // TODO: Gamut mapping // We need to clamp for the output transfer function v = saturate(v); diff --git a/filament/src/ColorSpace.cpp b/filament/src/ColorSpace.cpp index 93d31c662c..9b64f5b95f 100644 --- a/filament/src/ColorSpace.cpp +++ b/filament/src/ColorSpace.cpp @@ -45,10 +45,14 @@ using namespace math; * SOFTWARE. */ +// TODO: Replace compute_max_saturation() with solution from +// https://simonstechblog.blogspot.com/2021/06/implementing-gamut-mapping.html to +// target arbitrary output gamuts. The solution below works only for sRGB/Rec.709 + // Finds the maximum saturation possible for a given hue that fits in sRGB // Saturation here is defined as S = C/L // a and b must be normalized so a^2 + b^2 == 1 -float compute_max_saturation(float a, float b) noexcept { +static float compute_max_saturation(float a, float b) noexcept { // Max saturation will be when one of r, g or b goes below zero. // Select different coefficients depending on which component goes below zero first @@ -127,13 +131,13 @@ float compute_max_saturation(float a, float b) noexcept { // finds L_cusp and C_cusp for a given hue // a and b must be normalized so a^2 + b^2 == 1 -float2 find_cusp(float a, float b) noexcept { +static float2 find_cusp(float a, float b) noexcept { // First, find the maximum saturation (saturation S = C/L) float S_cusp = compute_max_saturation(a, b); // Convert to linear sRGB to find the first point where at least one of r,g or b >= 1: float3 rgb_at_max = OkLab_to_sRGB({1.0f, S_cusp * a, S_cusp * b}); - float L_cusp = std::cbrt(1.f / max(max(rgb_at_max.r, rgb_at_max.g), rgb_at_max.b)); + float L_cusp = std::cbrt(1.0f / max(rgb_at_max)); float C_cusp = L_cusp * S_cusp; return {L_cusp, C_cusp}; @@ -143,11 +147,11 @@ float2 find_cusp(float a, float b) noexcept { // L = L0 * (1 - t) + t * L1; // C = t * C1; // a and b must be normalized so a^2 + b^2 == 1 -float find_gamut_intersection(float a, float b, float L1, float C1, float L0) noexcept { +static float find_gamut_intersection(float a, float b, float L1, float C1, float L0) noexcept { // Find the cusp of the gamut triangle float2 cusp = find_cusp(a, b); - // Find the intersection for upper and lower half seprately + // Find the intersection for upper and lower half separately float t; if (((L1 - L0) * cusp.y - (cusp.x - L0) * C1) <= 0.f) { // Lower half @@ -231,8 +235,15 @@ constexpr float sgn(float x) noexcept { return (float) (0.f < x) - (float) (x < 0.f); } -inline float3 gamut_clip_adaptive_L0_0_5(float3 rgb, float alpha = 0.05f) noexcept { - if (all(lessThanEqual(rgb, float3{1.0f})) && all(greaterThanEqual(rgb, float3{0.0f}))) { +// Adaptive L0 = 0.5, with alpha set to 0.05 by default +// The threshold parameters defines a flexible range above 1.0 and below 0.0 where out-of-gamut +// values are still considered in-gamut. This helps control for inaccuracies in the previous +// color grading steps that may slightly deviate from in-gamut values when they shouldn't. +inline float3 gamut_clip_adaptive_L0_0_5(float3 rgb, + float alpha = 0.05f, float threshold = 0.03f) noexcept { + + if (all(lessThanEqual(rgb, float3{1.0f + threshold})) && + all(greaterThanEqual(rgb, float3{-threshold}))) { return rgb; } @@ -246,7 +257,7 @@ inline float3 gamut_clip_adaptive_L0_0_5(float3 rgb, float alpha = 0.05f) noexce float Ld = L - 0.5f; float e1 = 0.5f + std::abs(Ld) + alpha * C; - float L0 = 0.5f * (1.f + sgn(Ld) * (e1 - std::sqrt(e1 * e1 - 2.f * std::abs(Ld)))); + float L0 = 0.5f * (1.0f + sgn(Ld) * (e1 - std::sqrt(e1 * e1 - 2.0f * std::abs(Ld)))); float t = find_gamut_intersection(a_, b_, L, C, L0); float L_clipped = L0 * (1.f - t) + t * L; @@ -256,7 +267,7 @@ inline float3 gamut_clip_adaptive_L0_0_5(float3 rgb, float alpha = 0.05f) noexce } float3 gamutMapping_sRGB(float3 rgb) noexcept { - return gamut_clip_adaptive_L0_0_5(rgb, 0.5f); + return gamut_clip_adaptive_L0_0_5(rgb); } /* diff --git a/filament/src/ColorSpace.h b/filament/src/ColorSpace.h index 28194cd300..9622ef337b 100644 --- a/filament/src/ColorSpace.h +++ b/filament/src/ColorSpace.h @@ -53,15 +53,15 @@ constexpr mat3f sRGB_to_XYZ{ }; constexpr mat3f REC2020_to_XYZ{ - 0.6369580f, 0.2627002f, 0.0000000f, - 0.1446169f, 0.6779981f, 0.0280727f, - 0.1688810f, 0.0593017f, 1.0609851f + 0.6369530f, 0.2626983f, 0.0000000f, + 0.1446169f, 0.6780088f, 0.0280731f, + 0.1688558f, 0.0592929f, 1.0608272f }; constexpr mat3f XYZ_to_REC2020{ - 1.7166512f, -0.6666844f, 0.0176399f, - -0.3556708f, 1.6164812f, -0.0427706f, - -0.2533663f, 0.0157685f, 0.9421031f + 1.7166634f, -0.6666738f, 0.0176425f, + -0.3556733f, 1.6164557f, -0.0427770f, + -0.2533681f, 0.0157683f, 0.9422433f }; constexpr mat3f XYZ_to_CIECAT16{ @@ -156,6 +156,10 @@ constexpr mat3f sRGB_to_LMS_CAT16 = XYZ_to_CIECAT16 * sRGB_to_XYZ; constexpr mat3f LMS_CAT16_to_sRGB = XYZ_to_sRGB * CIECAT16_to_XYZ; +constexpr mat3f REC2020_to_LMS_CAT16 = XYZ_to_CIECAT16 * REC2020_to_XYZ; + +constexpr mat3f LMS_CAT16_to_REC2020 = XYZ_to_REC2020 * CIECAT16_to_XYZ; + constexpr mat3f REC2020_to_AP0 = AP1_to_AP0 * XYZ_to_AP1 * REC2020_to_XYZ; constexpr mat3f AP1_to_REC2020 = XYZ_to_REC2020 * AP1_to_XYZ; @@ -172,16 +176,16 @@ constexpr float3 ILLUMINANT_D65_xyY{0.31271f, 0.32902f, 1.0f}; constexpr float3 ILLUMINANT_D65_LMS_CAT16{0.975533f, 1.016483f, 1.084837f}; // RGB to luminance coefficients for Rec.2020, from REC2020_to_XYZ -constexpr float3 LUMA_REC2020{0.2627002f, 0.6779981f, 0.0593017f}; +constexpr float3 LUMINANCE_REC2020{0.2627002f, 0.6779981f, 0.0593017f}; // RGB to luminance coefficients for ACEScg (AP1), from AP1_to_XYZ -constexpr float3 LUMA_AP1{0.272229f, 0.674082f, 0.0536895f}; +constexpr float3 LUMINANCE_AP1{0.272229f, 0.674082f, 0.0536895f}; // RGB to luminance coefficients for Rec.709, from sRGB_to_XYZ -constexpr float3 LUMA_REC709{0.2126730f, 0.7151520f, 0.0721750f}; +constexpr float3 LUMINANCE_REC709{0.2126730f, 0.7151520f, 0.0721750f}; // RGB to luminance coefficients for Rec.709 with HK-like weighting -constexpr float3 LUMA_HK_REC709{0.13913043f, 0.73043478f, 0.13043478f}; +constexpr float3 LUMINANCE_HK_REC709{0.13913043f, 0.73043478f, 0.13043478f}; constexpr float MIDDLE_GRAY_ACEScg = 0.18f; diff --git a/filament/src/ToneMapper.cpp b/filament/src/ToneMapper.cpp index 7fdf4eb189..29d84327ef 100644 --- a/filament/src/ToneMapper.cpp +++ b/filament/src/ToneMapper.cpp @@ -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 = sRGB_to_AP0 * color; + float3 ap0 = REC2020_to_AP0 * color; // Glow module float saturation = rgb_2_saturation(ap0); @@ -156,7 +156,7 @@ float3 ACES(float3 color, float brightness) noexcept { float3 ap1 = clamp(AP0_to_AP1 * ap0, 0.0f, (float) std::numeric_limits::max()); // Global desaturation - ap1 = mix(float3(dot(ap1, LUMA_AP1)), ap1, RRT_SAT_FACTOR); + ap1 = mix(float3(dot(ap1, LUMINANCE_AP1)), ap1, RRT_SAT_FACTOR); // NOTE: This is specific to Filament and added only to match ACES to our legacy tone mapper // which was a fit of ACES in Rec.709 but with a brightness boost. @@ -175,9 +175,9 @@ float3 ACES(float3 color, float brightness) noexcept { float3 linearCV = darkSurround_to_dimSurround(rgbPost); // Apply desaturation to compensate for luminance difference - linearCV = mix(float3(dot(linearCV, LUMA_AP1)), linearCV, ODT_SAT_FACTOR); + linearCV = mix(float3(dot(linearCV, LUMINANCE_AP1)), linearCV, ODT_SAT_FACTOR); - return AP1_to_sRGB * linearCV; + return AP1_to_REC2020 * linearCV; } } // namespace aces @@ -260,7 +260,8 @@ float3 DisplayRangeToneMapper::operator()(math::float3 c) const noexcept { // The 5th color in the array (cyan) represents middle gray (18%) // Every stop above or below middle gray causes a color shift - float v = log2(dot(c, LUMA_REC709) / 0.18f); + // TODO: This should depend on the working color grading color space + float v = log2(dot(c, LUMINANCE_REC2020) / 0.18f); v = clamp(v + 5.0f, 0.0f, 15.0f); size_t index = size_t(v); diff --git a/libs/viewer/include/viewer/Settings.h b/libs/viewer/include/viewer/Settings.h index 59413898a1..8daad7244f 100644 --- a/libs/viewer/include/viewer/Settings.h +++ b/libs/viewer/include/viewer/Settings.h @@ -118,6 +118,7 @@ struct ColorGradingSettings { ToneMapping toneMapping = ToneMapping::ACES_LEGACY; GenericToneMapperSettings genericToneMapper; bool luminanceScaling = false; + bool gamutMapping = false; float exposure = 0.0f; float temperature = 0.0f; float tint = 0.0f; diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp index a255f5b6e4..954db89c9d 100644 --- a/libs/viewer/src/Settings.cpp +++ b/libs/viewer/src/Settings.cpp @@ -310,6 +310,8 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ColorGra i = parse(tokens, i + 1, jsonChunk, &out->genericToneMapper); } else if (compare(tok, jsonChunk, "luminanceScaling") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->luminanceScaling); + } else if (compare(tok, jsonChunk, "gamutMapping") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->gamutMapping); } else if (compare(tok, jsonChunk, "exposure") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->exposure); } else if (compare(tok, jsonChunk, "temperature") == 0) { @@ -1034,6 +1036,7 @@ ColorGrading* createColorGrading(const ColorGradingSettings& settings, Engine* e .curves(settings.gamma, settings.midPoint, settings.scale) .toneMapper(toneMapper) .luminanceScaling(settings.luminanceScaling) + .gamutMapping(settings.gamutMapping) .build(*engine); delete toneMapper; return colorGrading; @@ -1156,6 +1159,7 @@ static std::ostream& operator<<(std::ostream& out, const ColorGradingSettings& i << "\"toneMapping\": " << (in.toneMapping) << ",\n" << "\"genericToneMapper\": " << (in.genericToneMapper) << ",\n" << "\"luminanceScaling\": " << to_string(in.luminanceScaling) << ",\n" + << "\"gamutMapping\": " << to_string(in.gamutMapping) << ",\n" << "\"exposure\": " << (in.exposure) << ",\n" << "\"temperature\": " << (in.temperature) << ",\n" << "\"tint\": " << (in.tint) << ",\n" @@ -1427,6 +1431,7 @@ bool ColorGradingSettings::operator==(const ColorGradingSettings &rhs) const { toneMapping == rhs.toneMapping && genericToneMapper == rhs.genericToneMapper && luminanceScaling == rhs.luminanceScaling && + gamutMapping == rhs.gamutMapping && exposure == rhs.exposure && temperature == rhs.temperature && tint == rhs.tint && diff --git a/libs/viewer/src/SimpleViewer.cpp b/libs/viewer/src/SimpleViewer.cpp index 18112bdb00..6a6afb03c9 100644 --- a/libs/viewer/src/SimpleViewer.cpp +++ b/libs/viewer/src/SimpleViewer.cpp @@ -211,6 +211,7 @@ static void colorGradingUI(Settings& settings, float* rangePlot, float* curvePlo ImGui::PopStyleColor(); ImGui::Checkbox("Luminance scaling", &colorGrading.luminanceScaling); + ImGui::Checkbox("Gamut mapping", &colorGrading.gamutMapping); ImGui::SliderFloat("Exposure", &colorGrading.exposure, -10.0f, 10.0f); diff --git a/libs/viewer/tests/test_settings.cpp b/libs/viewer/tests/test_settings.cpp index 2f4f6f4a43..75f3e84c90 100644 --- a/libs/viewer/tests/test_settings.cpp +++ b/libs/viewer/tests/test_settings.cpp @@ -45,6 +45,7 @@ static const char* JSON_TEST_DEFAULTS = R"TXT( "hdrMax": 16.0 }, "luminanceScaling": false, + "gamutMapping": false, "exposure": 0, "temperature": 0, "tint": 0,