diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index b3fac3b18a..c430c39172 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -5,6 +5,10 @@ A new header is inserted each time a *tag* is created.
## v1.11.1 (currently main branch)
+- engine: Luminance scaling can now be used with any tone mapping operator. It was previously tied
+ to the "EVILS" tone mapping operator.
+- engine: Removed the "EVILS" tone mapping operator [⚠️ **API Change**].
+
## v1.11.0
- engine: Added support for transparent shadows. Add `transparentShadow : true` in the material file.
diff --git a/android/filament-android/src/main/cpp/ColorGrading.cpp b/android/filament-android/src/main/cpp/ColorGrading.cpp
index 814012cafc..725e88dd4f 100644
--- a/android/filament-android/src/main/cpp/ColorGrading.cpp
+++ b/android/filament-android/src/main/cpp/ColorGrading.cpp
@@ -60,6 +60,13 @@ Java_com_google_android_filament_ColorGrading_nBuilderToneMapping(JNIEnv*, jclas
builder->toneMapping(toneMapping);
}
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_android_filament_ColorGrading_nBuilderLuminanceScaling(JNIEnv*, jclass,
+ jlong nativeBuilder, jboolean luminanceScaling) {
+ ColorGrading::Builder* builder = (ColorGrading::Builder*) nativeBuilder;
+ builder->luminanceScaling(luminanceScaling);
+}
+
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 da54f06f99..2118257ba6 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
@@ -64,6 +64,7 @@ import static com.google.android.filament.Asserts.assertFloat4In;
*
Saturation
* Curves
* Tone mapping
+ * Luminance scaling
*
*
* Defaults
@@ -81,6 +82,7 @@ import static com.google.android.filament.Asserts.assertFloat4In;
* Saturation: 1.0
* Curves: gamma {1,1,1}, midPoint {1,1,1}, and scale {1,1,1}
* Tone mapping: {@link ToneMapping#ACES_LEGACY}
+ * Luminance scaling: false
*
*
* @see View
@@ -111,8 +113,8 @@ public class ColorGrading {
ACES,
/** Filmic tone mapping, modelled after ACES but applied in sRGB space. */
FILMIC,
- /** Exposure value invariant luminance scaling tone mapping, offers the best behaviors in high intensity areas. */
- EVILS,
+ /** Reserved for future use. */
+ RESERVED,
/** Reinhard luma-based tone mapping. */
REINHARD,
/** Tone mapping used to validate/debug scene exposure. */
@@ -173,6 +175,25 @@ public class ColorGrading {
return this;
}
+ /**
+ * Enables or disables the luminance scaling component (LICH) from the exposure value
+ * invariant luminance system (EVILS). When this setting is enabled, pixels with high
+ * chromatic values will roll-off to white to offer a more natural rendering. This step
+ * also helps avoid undesirable hue skews caused by out of gamut colors clipped
+ * to the destination color gamut.
+ *
+ * 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
+ *
+ * @return This Builder, for chaining calls
+ */
+ public Builder luminanceScaling(boolean luminanceScaling) {
+ nBuilderLuminanceScaling(mNativeBuilder, luminanceScaling);
+ 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
@@ -482,6 +503,7 @@ public class ColorGrading {
private static native void nBuilderQuality(long nativeBuilder, int quality);
private static native void nBuilderToneMapping(long nativeBuilder, int toneMapper);
+ private static native void nBuilderLuminanceScaling(long nativeBuilder, boolean luminanceScaling);
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 4b4d88e42b..cf336c16ad 100644
--- a/filament/include/filament/ColorGrading.h
+++ b/filament/include/filament/ColorGrading.h
@@ -74,6 +74,7 @@ class FColorGrading;
* - Saturation
* - Curves
* - Tone mapping
+ * - Luminance scaling
*
* Defaults
* ========
@@ -90,6 +91,7 @@ class FColorGrading;
* - Saturation: 1.0
* - Curves: gamma {1,1,1}, midPoint {1,1,1}, and scale {1,1,1}
* - Tone mapping: ACES_LEGACY
+ * - Luminance scaling: false
*
* @see View
*/
@@ -111,7 +113,7 @@ public:
ACES_LEGACY = 1, //!< ACES tone mapping, with a brightness modifier to match Filament's legacy tone mapper
ACES = 2, //!< ACES tone mapping
FILMIC = 3, //!< Filmic tone mapping, modelled after ACES but applied in sRGB space
- EVILS = 4, //!< Exposure value invariant luminance scaling tone mapping, offers the best behaviors in high intensity areas
+ RESERVED = 4, //!< Currently unused
REINHARD = 5, //!< Reinhard luma-based tone mapping
DISPLAY_RANGE = 6, //!< Tone mapping used to validate/debug scene exposure
};
@@ -154,6 +156,22 @@ public:
*/
Builder& toneMapping(ToneMapping toneMapping) noexcept;
+ /**
+ * Enables or disables the luminance scaling component (LICH) from the exposure value
+ * invariant luminance system (EVILS). When this setting is enabled, pixels with high
+ * chromatic values will roll-off to white to offer a more natural rendering. This step
+ * also helps avoid undesirable hue skews caused by out of gamut colors clipped
+ * to the destination color gamut.
+ *
+ * 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
+ *
+ * @return This Builder, for chaining calls
+ */
+ Builder& luminanceScaling(bool luminanceScaling) 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 d2dd509005..b6dcb9b3a6 100644
--- a/filament/src/ColorGrading.cpp
+++ b/filament/src/ColorGrading.cpp
@@ -52,6 +52,8 @@ struct ColorGrading::BuilderDetails {
ColorGrading::QualityLevel quality = QualityLevel::MEDIUM;
ToneMapping toneMapping = ToneMapping::ACES_LEGACY;
+ // Luminance scaling
+ bool luminanceScaling = false;
// Exposure
float exposure = 0.0f;
// White balance
@@ -88,6 +90,7 @@ struct ColorGrading::BuilderDetails {
// Note: Do NOT compare hasAdjustments
return quality == rhs.quality &&
toneMapping == rhs.toneMapping &&
+ luminanceScaling == rhs.luminanceScaling &&
exposure == rhs.exposure &&
whiteBalance == rhs.whiteBalance &&
outRed == rhs.outRed &&
@@ -127,6 +130,11 @@ ColorGrading::Builder& ColorGrading::Builder::toneMapping(ToneMapping toneMappin
return *this;
}
+ColorGrading::Builder& ColorGrading::Builder::luminanceScaling(bool luminanceScaling) noexcept {
+ mImpl->luminanceScaling = luminanceScaling;
+ return *this;
+}
+
ColorGrading::Builder& ColorGrading::Builder::exposure(float exposure) noexcept {
mImpl->exposure = exposure;
return *this;
@@ -273,7 +281,8 @@ mat3f selectColorGradingTransformIn(ColorGrading::ToneMapping toneMapping) {
case ColorGrading::ToneMapping::ACES:
return sRGB_to_AP1;
case ColorGrading::ToneMapping::FILMIC:
- case ColorGrading::ToneMapping::EVILS:
+ case ColorGrading::ToneMapping::RESERVED:
+ case ColorGrading::ToneMapping::DISPLAY_RANGE:
return mat3f{}; // stay in sRGB
default:
return sRGB_to_REC2020;
@@ -286,7 +295,8 @@ mat3f selectColorGradingTransformOut(ColorGrading::ToneMapping toneMapping) {
case ColorGrading::ToneMapping::ACES:
return AP1_to_sRGB;
case ColorGrading::ToneMapping::FILMIC:
- case ColorGrading::ToneMapping::EVILS:
+ case ColorGrading::ToneMapping::RESERVED:
+ case ColorGrading::ToneMapping::DISPLAY_RANGE:
return mat3f{}; // stay in sRGB
default:
return REC2020_to_sRGB;
@@ -303,6 +313,16 @@ float3 selectLuminanceTransform(ColorGrading::ToneMapping toneMapping) {
}
}
+float3 selectLuminanceScalingTransform(ColorGrading::ToneMapping toneMapping) {
+ switch (toneMapping) {
+ case ColorGrading::ToneMapping::ACES_LEGACY:
+ case ColorGrading::ToneMapping::ACES:
+ return LUMA_AP1;
+ default:
+ return LUMA_HK_REC709;
+ }
+}
+
UTILS_ALWAYS_INLINE
inline constexpr float3 channelMixer(float3 v, float3 r, float3 g, float3 b) {
return {dot(v, r), dot(v, g), dot(v, b)};
@@ -379,6 +399,39 @@ inline float3 curves(float3 v, float3 shadowGamma, float3 midPoint, float3 highl
};
}
+//------------------------------------------------------------------------------
+// Luminance scaling
+//------------------------------------------------------------------------------
+
+static float3 luminanceScaling(
+ float3 x, ColorTransform toneMapper, float3 luminanceWeights) noexcept {
+
+ // Troy Sobotka, 2021, "EVILS - Exposure Value Invariant Luminance Scaling"
+ // https://colab.research.google.com/drive/1iPJzNNKR7PynFmsqSnQm3bCZmQ3CvAJ-#scrollTo=psU43hb-BLzB
+
+ float luminanceIn = dot(x, luminanceWeights);
+
+ // TODO: We could optimize for the case of single-channel luminance
+ float luminanceOut = toneMapper(luminanceIn).x;
+
+ float peak = max(x);
+ float3 chromaRatio = max(x / peak, 0.0f);
+
+ float chromaRatioLuminance = dot(chromaRatio, luminanceWeights);
+
+ float3 maxReserves = 1.0f - chromaRatio;
+ float maxReservesLuminance = dot(maxReserves, luminanceWeights);
+
+ float luminanceDifference = std::max(luminanceOut - chromaRatioLuminance, 0.0f);
+ float scaledLuminanceDifference =
+ luminanceDifference / std::max(maxReservesLuminance, std::numeric_limits::min());
+
+ float chromaScale = (luminanceOut - luminanceDifference) /
+ std::max(chromaRatioLuminance, std::numeric_limits::min());
+
+ return chromaScale * chromaRatio + scaledLuminanceDifference * maxReserves;
+}
+
//------------------------------------------------------------------------------
// Quality
//------------------------------------------------------------------------------
@@ -435,8 +488,8 @@ ColorTransform selectToneMapping(ColorGrading::ToneMapping toneMapping) {
return tonemap::ACES;
case ColorGrading::ToneMapping::FILMIC:
return tonemap::Filmic;
- case ColorGrading::ToneMapping::EVILS:
- return tonemap::EVILS;
+ case ColorGrading::ToneMapping::RESERVED:
+ return tonemap::Linear;
case ColorGrading::ToneMapping::REINHARD:
return tonemap::Reinhard;
case ColorGrading::ToneMapping::DISPLAY_RANGE:
@@ -452,6 +505,7 @@ struct Config {
mat3f colorGradingTransformIn;
mat3f colorGradingTransformOut;
float3 luminanceTransform;
+ float3 luminanceScalingTransform;
ColorTransform linearToLogTransform;
ColorTransform logToLinearTransform;
ColorTransform toneMapper;
@@ -474,14 +528,15 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
utils::SpinLock configLock;
{
std::lock_guard lock(configLock);
- c.colorGradingTransformIn = selectColorGradingTransformIn(builder->toneMapping);
- c.colorGradingTransformOut = selectColorGradingTransformOut(builder->toneMapping);
- c.luminanceTransform = selectLuminanceTransform(builder->toneMapping);
- c.linearToLogTransform = selectLinearToLogTransform(builder->toneMapping);
- c.logToLinearTransform = selectLogToLinearTransform(builder->toneMapping);
- c.toneMapper = selectToneMapping(builder->toneMapping);
- c.lutDimension = selectLutDimension(builder->quality);
- c.adaptationTransform = adaptationTransform(builder->whiteBalance);
+ c.colorGradingTransformIn = selectColorGradingTransformIn(builder->toneMapping);
+ c.colorGradingTransformOut = selectColorGradingTransformOut(builder->toneMapping);
+ c.luminanceTransform = selectLuminanceTransform(builder->toneMapping);
+ c.luminanceScalingTransform = selectLuminanceScalingTransform(builder->toneMapping);
+ c.linearToLogTransform = selectLinearToLogTransform(builder->toneMapping);
+ c.logToLinearTransform = selectLogToLinearTransform(builder->toneMapping);
+ c.toneMapper = selectToneMapping(builder->toneMapping);
+ c.lutDimension = selectLutDimension(builder->quality);
+ c.adaptationTransform = adaptationTransform(builder->whiteBalance);
}
size_t lutElementCount = c.lutDimension * c.lutDimension * c.lutDimension;
@@ -574,7 +629,11 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
}
// Tone mapping
- v = config.toneMapper(v);
+ if (builder->luminanceScaling) {
+ v = luminanceScaling(v, config.toneMapper, config.luminanceScalingTransform);
+ } else {
+ v = config.toneMapper(v);
+ }
// Convert to output color space
// TODO: allow to customize the output color space,
diff --git a/filament/src/ToneMapping.cpp b/filament/src/ToneMapping.cpp
index 34610188a6..74b51aa220 100644
--- a/filament/src/ToneMapping.cpp
+++ b/filament/src/ToneMapping.cpp
@@ -188,8 +188,19 @@ float3 ACES_Legacy(float3 x) noexcept {
return aces::ACES(x, 1.0f / 0.6f);
}
-float genericTonemap(float x, float contrast, float shoulder,
- float midGreyIn, float midGreyOut, float hdrMax) noexcept {
+// TODO: These constants were chosen to match our ACES tone mappers as closely as possible
+// in terms of compression. We should expose these parameters to users via an API.
+// We must however carefully validate exposed parameters as it is easy to get the
+// generic tonemapper to produce invalid curves.
+// TODO: Expose this as a public tone mapper
+float genericTonemap(
+ float x,
+ float contrast = 1.6f,
+ float shoulder = 1.0f,
+ float midGreyIn = 0.18f,
+ float midGreyOut = 0.227f,
+ float hdrMax = 64.0f
+) noexcept {
// Lottes, 2016,"Advanced Techniques and Optimization of VDR Color Pipelines"
// https://gpuopen.com/wp-content/uploads/2016/03/GdcVdrLottes.pdf
float mc = std::pow(midGreyIn, contrast);
@@ -210,46 +221,6 @@ float genericTonemap(float x, float contrast, float shoulder,
return saturate(xc / (std::pow(xc, shoulder) * b + c));
}
-constexpr float luminance(float3 v) noexcept {
- return dot(v, LUMA_HK_REC709);
-}
-
-float3 EVILS(float3 x) noexcept {
- // Troy Sobotka, 2021, "EVILS - Exposure Value Invariant Luminance Scaling"
- // https://colab.research.google.com/drive/1iPJzNNKR7PynFmsqSnQm3bCZmQ3CvAJ-#scrollTo=psU43hb-BLzB
-
- // TODO: These constants were chosen to match our ACES tone mappers as closely as possible
- // in terms of compression. We should expose these parameters to users via an API.
- // We must however carefully validate exposed parameters as it is easy to get the
- // generic tonemapper to produce invalid curves.
- constexpr float contrast = 1.6f;
- constexpr float shoulder = 1.0f;
- constexpr float midGreyIn = 0.18f;
- constexpr float midGreyOut = 0.227f;
- constexpr float hdrMax = 64.0f;
-
- // We assume an input compatible with Rec.709 luminance weights
- float luminanceIn = luminance(x);
- float luminanceOut = genericTonemap(luminanceIn, contrast, shoulder, midGreyIn, midGreyOut, hdrMax);
-
- float peak = max(x);
- float3 chromaRatio = max(x / peak, 0.0f);
-
- float chromaRatioLuminance = luminance(chromaRatio);
-
- float3 maxReserves = 1.0f - chromaRatio;
- float maxReservesLuminance = luminance(maxReserves);
-
- float luminanceDifference = std::max(luminanceOut - chromaRatioLuminance, 0.0f);
- float scaledLuminanceDifference =
- luminanceDifference / std::max(maxReservesLuminance, std::numeric_limits::min());
-
- float chromaScale = (luminanceOut - luminanceDifference) /
- std::max(chromaRatioLuminance, std::numeric_limits::min());
-
- return chromaScale * chromaRatio + scaledLuminanceDifference * maxReserves;
-}
-
float3 DisplayRange(float3 x) noexcept {
// 16 debug colors + 1 duplicated at the end for easy indexing
diff --git a/filament/src/ToneMapping.h b/filament/src/ToneMapping.h
index f4073ee381..6502f81610 100644
--- a/filament/src/ToneMapping.h
+++ b/filament/src/ToneMapping.h
@@ -67,8 +67,6 @@ float3 ACES(float3 x) noexcept;
float3 ACES_Legacy(float3 x) noexcept;
-float3 EVILS(float3 x) noexcept;
-
/**
* Converts the input HDR RGB color into one of 16 debug colors that represent
* the pixel's exposure. When the output is cyan, the input color represents
diff --git a/libs/viewer/include/viewer/Settings.h b/libs/viewer/include/viewer/Settings.h
index 578f17ddcd..65f1b9d9b3 100644
--- a/libs/viewer/include/viewer/Settings.h
+++ b/libs/viewer/include/viewer/Settings.h
@@ -98,6 +98,7 @@ struct ColorGradingSettings {
bool enabled = true;
filament::ColorGrading::QualityLevel quality = filament::ColorGrading::QualityLevel::MEDIUM;
ToneMapping toneMapping = ToneMapping::ACES_LEGACY;
+ bool luminanceScaling = 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 91f845f92c..93de1a75e6 100644
--- a/libs/viewer/src/Settings.cpp
+++ b/libs/viewer/src/Settings.cpp
@@ -186,7 +186,7 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ToneMapp
else if (0 == compare(tokens[i], jsonChunk, "ACES_LEGACY")) { *out = ToneMapping::ACES_LEGACY; }
else if (0 == compare(tokens[i], jsonChunk, "ACES")) { *out = ToneMapping::ACES; }
else if (0 == compare(tokens[i], jsonChunk, "FILMIC")) { *out = ToneMapping::FILMIC; }
- else if (0 == compare(tokens[i], jsonChunk, "EVILS")) { *out = ToneMapping::EVILS; }
+ else if (0 == compare(tokens[i], jsonChunk, "RESERVED")) { *out = ToneMapping::RESERVED; }
else if (0 == compare(tokens[i], jsonChunk, "REINHARD")) { *out = ToneMapping::REINHARD; }
else if (0 == compare(tokens[i], jsonChunk, "DISPLAY_RANGE")) { *out = ToneMapping::DISPLAY_RANGE; }
else {
@@ -279,6 +279,8 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ColorGra
i = parse(tokens, i + 1, jsonChunk, &out->quality);
} else if (compare(tok, jsonChunk, "toneMapping") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->toneMapping);
+ } else if (compare(tok, jsonChunk, "luminanceScaling") == 0) {
+ i = parse(tokens, i + 1, jsonChunk, &out->luminanceScaling);
} else if (compare(tok, jsonChunk, "exposure") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->exposure);
} else if (compare(tok, jsonChunk, "temperature") == 0) {
@@ -980,6 +982,7 @@ ColorGrading* createColorGrading(const ColorGradingSettings& settings, Engine* e
.saturation(settings.saturation)
.curves(settings.gamma, settings.midPoint, settings.scale)
.toneMapping(settings.toneMapping)
+ .luminanceScaling(settings.luminanceScaling)
.build(*engine);
}
@@ -1049,7 +1052,7 @@ static std::ostream& operator<<(std::ostream& out, ToneMapping in) {
case ToneMapping::ACES_LEGACY: return out << "\"ACES_LEGACY\"";
case ToneMapping::ACES: return out << "\"ACES\"";
case ToneMapping::FILMIC: return out << "\"FILMIC\"";
- case ToneMapping::EVILS: return out << "\"EVILS\"";
+ case ToneMapping::RESERVED: return out << "\"RESERVED\"";
case ToneMapping::REINHARD: return out << "\"REINHARD\"";
case ToneMapping::DISPLAY_RANGE: return out << "\"DISPLAY_RANGE\"";
}
@@ -1089,6 +1092,7 @@ static std::ostream& operator<<(std::ostream& out, const ColorGradingSettings& i
<< "\"enabled\": " << to_string(in.enabled) << ",\n"
<< "\"quality\": " << (in.quality) << ",\n"
<< "\"toneMapping\": " << (in.toneMapping) << ",\n"
+ << "\"luminanceScaling\": " << (in.luminanceScaling) << ",\n"
<< "\"exposure\": " << (in.exposure) << ",\n"
<< "\"temperature\": " << (in.temperature) << ",\n"
<< "\"tint\": " << (in.tint) << ",\n"
@@ -1347,6 +1351,7 @@ bool ColorGradingSettings::operator==(const ColorGradingSettings &rhs) const {
return enabled == rhs.enabled &&
quality == rhs.quality &&
toneMapping == rhs.toneMapping &&
+ luminanceScaling == rhs.luminanceScaling &&
exposure == rhs.exposure &&
temperature == rhs.temperature &&
tint == rhs.tint &&
diff --git a/libs/viewer/src/SimpleViewer.cpp b/libs/viewer/src/SimpleViewer.cpp
index a87c315738..ffc8b97333 100644
--- a/libs/viewer/src/SimpleViewer.cpp
+++ b/libs/viewer/src/SimpleViewer.cpp
@@ -151,8 +151,9 @@ static void colorGradingUI(Settings& settings, float* rangePlot, float* curvePlo
int toneMapping = (int) colorGrading.toneMapping;
ImGui::Combo("Tone-mapping", &toneMapping,
- "Linear\0ACES (legacy)\0ACES\0Filmic\0EVILS\0Reinhard\0Display Range\0\0");
+ "Linear\0ACES (legacy)\0ACES\0Filmic\0Reserved\0Reinhard\0Display Range\0\0");
colorGrading.toneMapping = (decltype(colorGrading.toneMapping)) toneMapping;
+ ImGui::Checkbox("Luminance scaling", &colorGrading.luminanceScaling);
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 3e106b3ead..3273fb8f8d 100644
--- a/libs/viewer/tests/test_settings.cpp
+++ b/libs/viewer/tests/test_settings.cpp
@@ -37,6 +37,7 @@ static const char* JSON_TEST_DEFAULTS = R"TXT(
"enabled": true,
"quality": "MEDIUM",
"toneMapping": "ACES_LEGACY",
+ "luminanceScaling": false,
"exposure": 0,
"temperature": 0,
"tint": 0,
diff --git a/samples/material_sandbox.cpp b/samples/material_sandbox.cpp
index c7a70b4064..f3b2c53a62 100644
--- a/samples/material_sandbox.cpp
+++ b/samples/material_sandbox.cpp
@@ -716,7 +716,8 @@ static void gui(filament::Engine* engine, filament::View*) {
ImGui::Indent();
ImGui::Checkbox("Enabled##colorGrading", ¶ms.colorGrading);
ImGui::Combo("Tone-mapping", &colorGrading.toneMapping,
- "Linear\0ACES (legacy)\0ACES\0Filmic\0EVILS\0Reinhard\0Display Range\0\0");
+ "Linear\0ACES (legacy)\0ACES\0Filmic\0Reserved\0Reinhard\0Display Range\0\0");
+ ImGui::Checkbox("Luminance scaling", &colorGrading.luminanceScaling);
if (ImGui::CollapsingHeader("While balance")) {
ImGui::SliderInt("Temperature", &colorGrading.temperature, -100, 100);
ImGui::SliderInt("Tint", &colorGrading.tint, -100, 100);
@@ -984,6 +985,7 @@ static void preRender(filament::Engine* engine, filament::View* view, filament::
.saturation(options.saturation)
.curves(options.gamma, options.midPoint, options.scale)
.toneMapping(static_cast(options.toneMapping))
+ .luminanceScaling(options.luminanceScaling)
.build(*engine);
if (g_colorGrading) {
diff --git a/samples/material_sandbox.h b/samples/material_sandbox.h
index 2e6c258db9..022966fe0d 100644
--- a/samples/material_sandbox.h
+++ b/samples/material_sandbox.h
@@ -62,6 +62,7 @@ using namespace filament;
struct ColorGradingOptions {
int toneMapping = static_cast(ColorGrading::ToneMapping::ACES_LEGACY);
+ bool luminanceScaling = false;
int temperature = 0;
int tint = 0;
math::float3 outRed{1.0f, 0.0f, 0.0f};
@@ -88,6 +89,7 @@ struct ColorGradingOptions {
bool operator==(const ColorGradingOptions &rhs) const {
return toneMapping == rhs.toneMapping &&
+ luminanceScaling == rhs.luminanceScaling &&
temperature == rhs.temperature &&
outRed == rhs.outRed &&
outGreen == rhs.outGreen &&
diff --git a/web/filament-js/jsenums.cpp b/web/filament-js/jsenums.cpp
index de0524ce38..ed7037aad7 100644
--- a/web/filament-js/jsenums.cpp
+++ b/web/filament-js/jsenums.cpp
@@ -164,7 +164,7 @@ enum_("ColorGrading$ToneMapping")
.value("ACES_LEGACY", ColorGrading::ToneMapping::ACES_LEGACY)
.value("ACES", ColorGrading::ToneMapping::ACES)
.value("FILMIC", ColorGrading::ToneMapping::FILMIC)
- .value("EVILS", ColorGrading::ToneMapping::EVILS)
+ .value("RESERVED", ColorGrading::ToneMapping::RESERVED)
.value("REINHARD", ColorGrading::ToneMapping::REINHARD)
.value("DISPLAY_RANGE", ColorGrading::ToneMapping::DISPLAY_RANGE);