diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index f5c841d9c1..8861c61be9 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -13,7 +13,8 @@ A new header is inserted each time a *tag* is created. - SSAO now has an optional high(er) quality upsampler. - Tone mappping now uses the real ACES tone mapper, applied in the proper color space. - Tone mapping is now applied via a LUT. -- Color grading capabilities per View: white balance (temperature/tint), channel mixer. +- Color grading capabilities per View: white balance (temperature/tint), channel mixer, + tonal ranges (shadows/mid-tones/highlights). - Fixed bug in the Metal backend when SSR and MSAA were turned on - Fixed Metal issue with `BufferDescriptor` and `PixelBufferDescriptor`s not being called on application thread. diff --git a/docs/math/Shadow Midtones Highglits.nb b/docs/math/Shadow Midtones Highglits.nb new file mode 100644 index 0000000000..f98d960994 --- /dev/null +++ b/docs/math/Shadow Midtones Highglits.nb @@ -0,0 +1,197 @@ +(* Content-type: application/vnd.wolfram.mathematica *) + +(*** Wolfram Notebook File ***) +(* http://www.wolfram.com/nb *) + +(* CreatedBy='Mathematica 12.1' *) + +(*CacheID: 234*) +(* Internal cache information: +NotebookFileLineBreakTest +NotebookFileLineBreakTest +NotebookDataPosition[ 158, 7] +NotebookDataLength[ 7662, 189] +NotebookOptionsPosition[ 6996, 170] +NotebookOutlinePosition[ 7393, 186] +CellTagsIndexPosition[ 7350, 183] +WindowFrame->Normal*) + +(* Beginning of Notebook Content *) +Notebook[{ +Cell[TextData[{ + StyleBox["Shadows/mid-tones/highlights ranges\n", "Subsection"], + "This notebook explores how to define the shadows/mid-tones/highlights \ +ranges for color grading. This implementation is based loosely on the log mod \ +of the color wheels palette of DaVinci Resolve (see \ +https://documents.blackmagicdesign.com/UserManuals/DaVinci_Resolve_14_\ +Reference_Manual.pdf for details)." +}], "Text", + CellChangeTimes->{{3.7996982241825457`*^9, 3.7996982477885103`*^9}, { + 3.800036555016198*^9, + 3.8000366861328773`*^9}},ExpressionUUID->"f0eee150-e72e-4950-83b3-\ +9d4eb478523d"], + +Cell[BoxData[ + RowBox[{ + RowBox[{"smoothStep", "[", + RowBox[{"e0_", ",", " ", "e1_", ",", " ", "x_"}], "]"}], ":=", + RowBox[{"Module", "[", + RowBox[{ + RowBox[{"{", "t", "}"}], ",", "\[IndentingNewLine]", + RowBox[{ + RowBox[{"t", "=", + RowBox[{"Clip", "[", + RowBox[{ + RowBox[{ + RowBox[{"(", + RowBox[{"x", "-", "e0"}], ")"}], "/", + RowBox[{"(", + RowBox[{"e1", "-", "e0"}], ")"}]}], ",", + RowBox[{"{", + RowBox[{"0", ",", "1"}], "}"}]}], "]"}]}], ";", + "\[IndentingNewLine]", + RowBox[{ + RowBox[{"t", "^", "2"}], "*", + RowBox[{"(", + RowBox[{"3", " ", "-", " ", + RowBox[{"2", " ", "*", " ", "t"}]}], ")"}]}]}]}], + "\[IndentingNewLine]", "]"}]}]], "Input", + CellChangeTimes->{{3.800035828984764*^9, 3.800035897263982*^9}, { + 3.800035928251821*^9, 3.800035935254816*^9}}, + CellLabel->"In[5]:=",ExpressionUUID->"b2cca214-3159-4432-b089-098eda680ce0"], + +Cell[CellGroupData[{ + +Cell[BoxData[ + RowBox[{"Manipulate", "[", "\[IndentingNewLine]", + RowBox[{ + RowBox[{"Plot", "[", + RowBox[{ + RowBox[{"{", + RowBox[{ + RowBox[{"1", "-", + RowBox[{"smoothStep", "[", + RowBox[{"shadow", ",", "dark", ",", "x"}], "]"}]}], ",", + RowBox[{"1", "-", + RowBox[{"(", + RowBox[{"1", "-", + RowBox[{"smoothStep", "[", + RowBox[{"shadow", ",", "dark", ",", "x"}], "]"}]}], ")"}], "-", + RowBox[{"smoothStep", "[", + RowBox[{"light", ",", "highlight", ",", "x"}], "]"}]}], ",", + RowBox[{"smoothStep", "[", + RowBox[{"light", ",", "highlight", ",", "x"}], "]"}]}], "}"}], ",", + RowBox[{"{", + RowBox[{"x", ",", "0", ",", "1"}], "}"}], ",", + RowBox[{"ImageSize", "\[Rule]", "Large"}], ",", + RowBox[{"Filling", "->", "Axis"}]}], "]"}], ",", "\[IndentingNewLine]", + RowBox[{"{", + RowBox[{ + RowBox[{"{", + RowBox[{"shadow", ",", "0.0"}], "}"}], ",", "0.0", ",", "0.9999"}], + "}"}], ",", "\[IndentingNewLine]", + RowBox[{"{", + RowBox[{ + RowBox[{"{", + RowBox[{"dark", ",", "0.333"}], "}"}], ",", "0.0001", ",", "0.9999"}], + "}"}], ",", "\[IndentingNewLine]", + RowBox[{"{", + RowBox[{ + RowBox[{"{", + RowBox[{"light", ",", "0.550"}], "}"}], ",", "0.0001", ",", "0.9999"}], + "}"}], ",", "\[IndentingNewLine]", + RowBox[{"{", + RowBox[{ + RowBox[{"{", + RowBox[{"highlight", ",", "1.0"}], "}"}], ",", "0.0001", ",", "1.0"}], + "}"}]}], "\[IndentingNewLine]", "]"}]], "Input", + CellChangeTimes->{{3.800035899978855*^9, 3.800036212318406*^9}, { + 3.800036351085218*^9, 3.800036352868197*^9}, {3.8000369201535683`*^9, + 3.800036935029261*^9}, {3.8000372433968763`*^9, 3.800037243786892*^9}, { + 3.800037310348159*^9, 3.800037376071424*^9}}, + CellLabel->"In[28]:=",ExpressionUUID->"0a516cc2-23b9-453a-a83b-dc42b3dce7ac"], + +Cell[BoxData[ + TagBox[ + StyleBox[ + DynamicModuleBox[{$CellContext`dark$$ = 0.333, $CellContext`highlight$$ = + 1., $CellContext`light$$ = 0.55, $CellContext`shadow$$ = 0., + Typeset`show$$ = True, Typeset`bookmarkList$$ = {}, + Typeset`bookmarkMode$$ = "Menu", Typeset`animator$$, Typeset`animvar$$ = + 1, Typeset`name$$ = "\"untitled\"", Typeset`specs$$ = {{{ + Hold[$CellContext`shadow$$], 0.}, 0., 0.9999}, {{ + Hold[$CellContext`dark$$], 0.333}, 0.0001, 0.9999}, {{ + Hold[$CellContext`light$$], 0.55}, 0.0001, 0.9999}, {{ + Hold[$CellContext`highlight$$], 1.}, 0.0001, 1.}}, Typeset`size$$ = { + 576., {175., 179.}}, Typeset`update$$ = 0, Typeset`initDone$$, + Typeset`skipInitDone$$ = True}, + DynamicBox[Manipulate`ManipulateBoxes[ + 1, StandardForm, + "Variables" :> {$CellContext`dark$$ = 0.333, $CellContext`highlight$$ = + 1., $CellContext`light$$ = 0.55, $CellContext`shadow$$ = 0.}, + "ControllerVariables" :> {}, + "OtherVariables" :> { + Typeset`show$$, Typeset`bookmarkList$$, Typeset`bookmarkMode$$, + Typeset`animator$$, Typeset`animvar$$, Typeset`name$$, + Typeset`specs$$, Typeset`size$$, Typeset`update$$, Typeset`initDone$$, + Typeset`skipInitDone$$}, "Body" :> + Plot[{1 - $CellContext`smoothStep[$CellContext`shadow$$, \ +$CellContext`dark$$, $CellContext`x], 1 - ( + 1 - $CellContext`smoothStep[$CellContext`shadow$$, \ +$CellContext`dark$$, $CellContext`x]) - \ +$CellContext`smoothStep[$CellContext`light$$, $CellContext`highlight$$, \ +$CellContext`x], + $CellContext`smoothStep[$CellContext`light$$, \ +$CellContext`highlight$$, $CellContext`x]}, {$CellContext`x, 0, 1}, ImageSize -> + Large, Filling -> Axis], + "Specifications" :> {{{$CellContext`shadow$$, 0.}, 0., + 0.9999}, {{$CellContext`dark$$, 0.333}, 0.0001, + 0.9999}, {{$CellContext`light$$, 0.55}, 0.0001, + 0.9999}, {{$CellContext`highlight$$, 1.}, 0.0001, 1.}}, + "Options" :> {}, "DefaultOptions" :> {}], + ImageSizeCache->{621., {262., 268.}}, + SingleEvaluation->True], + Deinitialization:>None, + DynamicModuleValues:>{}, + SynchronousInitialization->True, + UndoTrackedVariables:>{Typeset`show$$, Typeset`bookmarkMode$$}, + UnsavedVariables:>{Typeset`initDone$$}, + UntrackedVariables:>{Typeset`size$$}], "Manipulate", + Deployed->True, + StripOnInput->False], + Manipulate`InterpretManipulate[1]]], "Output", + CellChangeTimes->{{3.800035909887515*^9, 3.800035924906485*^9}, { + 3.800035963413073*^9, 3.800036037313202*^9}, {3.800036095635211*^9, + 3.800036213562434*^9}, {3.8000363288112383`*^9, 3.80003635325991*^9}, + 3.8000364348959017`*^9, 3.8000365413816338`*^9, 3.800036962971013*^9, { + 3.800037354906559*^9, 3.800037376658716*^9}, 3.800037480044058*^9}, + CellLabel->"Out[28]=",ExpressionUUID->"3b35b3b2-6445-4c38-9b7d-bc731c2eccce"] +}, Open ]] +}, +WindowSize->{983, 1064}, +WindowMargins->{{584, Automatic}, {232, Automatic}}, +FrontEndVersion->"12.1 for Mac OS X x86 (64-bit) (March 18, 2020)", +StyleDefinitions->"Default.nb", +ExpressionUUID->"ed9be208-fe57-4431-8fc3-063e24460812" +] +(* End of Notebook Content *) + +(* Internal cache information *) +(*CellTagsOutline +CellTagsIndex->{} +*) +(*CellTagsIndex +CellTagsIndex->{} +*) +(*NotebookFileOutline +Notebook[{ +Cell[558, 20, 590, 11, 110, "Text",ExpressionUUID->"f0eee150-e72e-4950-83b3-9d4eb478523d"], +Cell[1151, 33, 974, 27, 94, "Input",ExpressionUUID->"b2cca214-3159-4432-b089-098eda680ce0"], +Cell[CellGroupData[{ +Cell[2150, 64, 1912, 47, 178, "Input",ExpressionUUID->"0a516cc2-23b9-453a-a83b-dc42b3dce7ac"], +Cell[4065, 113, 2915, 54, 549, "Output",ExpressionUUID->"3b35b3b2-6445-4c38-9b7d-bc731c2eccce"] +}, Open ]] +} +] +*) + diff --git a/filament/include/filament/ColorGrading.h b/filament/include/filament/ColorGrading.h index 9038f9551a..2272971fc5 100644 --- a/filament/include/filament/ColorGrading.h +++ b/filament/include/filament/ColorGrading.h @@ -60,13 +60,24 @@ class FColorGrading; * 3D LUT may need to be generated. The generation of a 3D LUT, if necessary, may happen on * the CPU. * + * Ordering + * ======== + * + * The various transforms held by ColorGrading are applied in the following order: + * - White balance + * - Channel mixer + * - Shadows/mid-tones/highlights + * - Tone mapping + * * Defaults * ======== * * Here are the default color grading options: - * - Tone mapping: ACES * - White balance: temperature 0, and tint 0 * - Channel mixer: red {1,0,0}, green {0,1,0}, blue {0,0,1} + * - Shadows/mid-tones/highlights: shadows {1,1,1,0}, mid-tones {1,1,1,0}, highlights {1,1,1,0}, + * ranges {0,0.333,0.550,1} + * - Tone mapping: ACES * * @see View */ @@ -118,8 +129,8 @@ public: * - Temperature, to modify the color temperature. This value will modify the colors * on a blue/yellow axis. Lower values apply a cool color temperature, and higher * values apply a warm color temperature. The lowest value, -1.0f, is equivalent to - * a temperature of 2,000K. The highest value, 1.0f, is equivalent to a temperature - * of 50,000K. + * a temperature of 50,000K. The highest value, 1.0f, is equivalent to a temperature + * of 2,000K. * - Tint, to modify the colors on a green/magenta axis. The lowest value, -1.0f, will * apply a strong green cast, and the highest value, 1.0f, will apply a strong magenta * cast. @@ -164,6 +175,33 @@ public: Builder& channelMixer( math::float3 outRed, math::float3 outGreen, math::float3 outBlue) noexcept; + /** + * Adjusts the colors separately in 3 distinct tonal ranges or zones: shadows, mid-tones, + * and highlights. + * + * The tonal zones are by the ranges parameter: the x and y components define the beginning + * and end of the transition from shadows to mid-tones, and the z and w components define + * the beginning and end of the transition from mid-tones to highlights. + * + * A smooth transition is applied between the zones which means for instance that the + * correction color of the shadows range will partially apply to the mid-tones, and the + * other way around. This ensure smooth visual transitions in the final image. + * + * Each correction color is defined as a linear RGB color and a weight. The weight is a + * value (which may be positive or negative) that is added to the linear RGB color before + * mixing. This can be used to darken or brighten the selected tonal range. + * + * @param shadows Linear RGB color (.rgb) and weight (.w) to apply to the shadows + * @param midtones Linear RGB color (.rgb) and weight (.w) to apply to the mid-tones + * @param highlights Linear RGB color (.rgb) and weight (.w) to apply to the highlights + * @param ranges Range of the shadows (x and y), and range of the highlights (z and w) + * + * @return This Builder, for chaining calls + */ + Builder& shadowsMidtonesHighlights( + math::float4 shadows, math::float4 midtones, math::float4 highlights, + math::float4 ranges = math::float4{0.0f, 0.333f, 0.550f, 1.0f}) noexcept; + /** * Creates the ColorGrading object and returns a pointer to it. * diff --git a/filament/src/ColorGrading.cpp b/filament/src/ColorGrading.cpp index cb5f780e30..77574ced1f 100644 --- a/filament/src/ColorGrading.cpp +++ b/filament/src/ColorGrading.cpp @@ -29,6 +29,7 @@ #include #include +#include #include @@ -55,6 +56,10 @@ struct ColorGrading::BuilderDetails { float3 outRed = {1.0f, 0.0f, 0.0f}; float3 outGreen = {0.0f, 1.0f, 0.0f}; float3 outBlue = {0.0f, 0.0f, 1.0f}; + float3 shadows = {1.0f, 1.0f, 1.0f}; + float3 midtones = {1.0f, 1.0f, 1.0f}; + float3 highlights = {1.0f, 1.0f, 1.0f}; + float4 tonalRanges = {0.0f, 0.333f, 0.550f, 1.0f}; // defaults in DaVinci Resolve }; using BuilderType = ColorGrading; @@ -86,6 +91,21 @@ ColorGrading::Builder& ColorGrading::Builder::channelMixer( return *this; } +ColorGrading::Builder& ColorGrading::Builder::shadowsMidtonesHighlights( + float4 shadows, float4 midtones, float4 highlights, float4 ranges) noexcept { + mImpl->shadows = max(shadows.rgb + shadows.w, 0.0f); + mImpl->midtones = max(midtones.rgb + midtones.w, 0.0f); + mImpl->highlights = max(highlights.rgb + highlights.w, 0.0f); + + ranges.x = saturate(ranges.x); // shadows + ranges.w = saturate(ranges.w); // highlights + ranges.y = clamp(ranges.y, ranges.x + 1e-5f, ranges.z - 1e-5f); // darks + ranges.z = clamp(ranges.z, ranges.x + 1e-5f, ranges.z - 1e-5f); // lights + mImpl->tonalRanges = ranges; + + return *this; +} + ColorGrading* ColorGrading::Builder::build(Engine& engine) { return upcast(engine).createColorGrading(*this); } @@ -131,7 +151,15 @@ mat3f selectColorGradingTransform(ColorGrading::ToneMapping toneMapping) { default: return mat3f{}; } - return mat3f{}; +} + +float3 selectLumaTransform(ColorGrading::ToneMapping toneMapping) { + switch (toneMapping) { + case ColorGrading::ToneMapping::ACES: + return LUMA_AP1; + default: + return LUMA_REC709; + } } UTILS_ALWAYS_INLINE @@ -139,6 +167,24 @@ inline constexpr float3 channelMixer(float3 v, float3 r, float3 g, float3 b) { return {dot(v, r), dot(v, g), dot(v, b)}; } +UTILS_ALWAYS_INLINE +inline constexpr float3 tonalRanges( + float3 v, float3 luma, float3 shadows, float3 midtones, float3 highlights, float4 ranges) { + // See the Mathematica notebook at docs/math/Shadows Midtones Highlight.nb for + // details on how the curves were designed. The default curve values are based + // on the defaults from the "Log" color wheels in DaVinci Resolve. + float y = dot(v, luma); + + // Shadows curve + float s = 1.0f - smoothstep(ranges.x, ranges.y, y); + // Highlights curve + float h = smoothstep(ranges.z, ranges.w, y); + // Mid-tones curves + float m = 1.0f - s - h; + + return v * s * shadows + v * m * midtones + v * h * highlights; +} + //------------------------------------------------------------------------------ // Tone mapping //------------------------------------------------------------------------------ @@ -167,6 +213,7 @@ ToneMapper selectToneMapping(ColorGrading::ToneMapping toneMapping) { struct Config { mat3f colorGradingTransform; + float3 lumaTransform; ToneMapper toneMapper; }; @@ -181,6 +228,7 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) { Config config{ .colorGradingTransform = selectColorGradingTransform(builder->toneMapping), + .lumaTransform = selectLumaTransform(builder->toneMapping), .toneMapper = selectToneMapping(builder->toneMapping), }; @@ -196,7 +244,7 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) { half4* UTILS_RESTRICT p = (half4*) data + b * LUT_DIMENSION * LUT_DIMENSION; for (size_t g = 0; g < LUT_DIMENSION; g++) { for (size_t r = 0; r < LUT_DIMENSION; r++) { - float3 v = float3{ r, g, b } * (1.0f / (LUT_DIMENSION - 1u)); + float3 v = float3{r, g, b} * (1.0f / (LUT_DIMENSION - 1u)); // LogC encoding v = lutToLinear(v); @@ -207,9 +255,19 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) { // Convert to color grading color space v = config.colorGradingTransform * v; + // TODO: Should any of the color grading transforms be applied in ACEScc or + // ACEScct isntead of ACEScg? The primaries are the same (AP1) but + // ACEScc/cct use a log encoding which may be better suited to some kinds + // of transforms + // Channel mixer v = channelMixer(v, builder->outRed, builder->outGreen, builder->outBlue); + // Shadows/mid-tones/highlights + v = tonalRanges(v, config.lumaTransform, + builder->shadows, builder->midtones, builder->highlights, + builder->tonalRanges); + // Kill negative values before tone mapping v = max(v, 0.0f); diff --git a/filament/src/ColorSpace.h b/filament/src/ColorSpace.h index 44fcc3ae88..728f7fb25f 100644 --- a/filament/src/ColorSpace.h +++ b/filament/src/ColorSpace.h @@ -90,6 +90,10 @@ constexpr mat3f sRGB_to_AP1{ 0.04737f, 0.01345f, 0.86961f }; +constexpr mat3f sRGB_to_LMS = XYZ_to_CIECAT02 * sRGB_to_XYZ; + +constexpr mat3f LMS_to_sRGB = XYZ_to_sRGB * CIECAT02_to_XYZ; + // Standard CIE 1931 2° illuminant D65, in xyY space constexpr float3 ILLUMINANT_D65_xyY{0.31271f, 0.32902f, 1.0f}; @@ -97,9 +101,11 @@ constexpr float3 ILLUMINANT_D65_xyY{0.31271f, 0.32902f, 1.0f}; // Result of: XYZ_to_CIECAT02 * xyY_to_XYZ(ILLUMINANT_D65_xyY); constexpr float3 ILLUMINANT_D65_LMS{0.949237f, 1.03542f, 1.08728f}; -constexpr mat3f sRGB_to_LMS = XYZ_to_CIECAT02 * sRGB_to_XYZ; +// RGB to luma coefficients for ACEScg (AP1), from AP1_to_XYZ +constexpr float3 LUMA_AP1{0.272229f, 0.674082f, 0.0536895f}; -constexpr mat3f LMS_to_sRGB = XYZ_to_sRGB * CIECAT02_to_XYZ; +// RGB to luma coefficients for Rec.709, from sRGB_to_XYZ +constexpr float3 LUMA_REC709{0.2126730f, 0.7151520f, 0.0721750f}; inline constexpr XYZ xyY_to_XYZ(xyY v) { const float a = v.z / max(v.y, 1e-5f); diff --git a/filament/src/ToneMapping.h b/filament/src/ToneMapping.h index fa75d25a42..2dc36a8e9f 100644 --- a/filament/src/ToneMapping.h +++ b/filament/src/ToneMapping.h @@ -164,12 +164,10 @@ inline float3 ACES(float3 color) { ap0.r += hueWeight * saturation * (RRT_RED_PIVOT - ap0.r) * (1.0f - RRT_RED_SCALE); // ACES to RGB rendering space - float3 ap1 = clamp(AP0_to_AP1 * ap0, 0.0f, (float)std::numeric_limits::max()); + float3 ap1 = clamp(AP0_to_AP1 * ap0, 0.0f, (float) std::numeric_limits::max()); // Global desaturation - const float3 AP1_RGB2Y{ 0.272229f, 0.674082f, 0.0536895f }; - - ap1 = mix(float3(dot(ap1, AP1_RGB2Y)), ap1, RRT_SAT_FACTOR); + ap1 = mix(float3(dot(ap1, LUMA_AP1)), ap1, RRT_SAT_FACTOR); #if defined(TONEMAP_ACES_MATCH_BRIGHTNESS) ap1 *= 1.0f / 0.6f; @@ -188,7 +186,7 @@ inline float3 ACES(float3 color) { float3 linearCV = darkSurround_to_dimSurround(rgbPost); // Apply desaturation to compensate for luminance difference - linearCV = mix(float3(dot(linearCV, AP1_RGB2Y)), linearCV, ODT_SAT_FACTOR); + linearCV = mix(float3(dot(linearCV, LUMA_AP1)), linearCV, ODT_SAT_FACTOR); // Convert to display primary encoding (Rec.709 primaries, D65 white point) return AP1_to_sRGB * linearCV; diff --git a/samples/material_sandbox.cpp b/samples/material_sandbox.cpp index 2dcddb3ac5..69d044b55c 100644 --- a/samples/material_sandbox.cpp +++ b/samples/material_sandbox.cpp @@ -71,6 +71,7 @@ static ColorGrading* g_colorGrading = nullptr; static Config g_config; static bool g_shadowPlane = false; static bool g_singleMode = false; +static float g_rangePlot[1024 * 3]; static void printUsage(char* name) { std::string exec_name(Path(name).getName()); @@ -417,6 +418,18 @@ static filament::MaterialInstance* updateInstances( return materialInstance; } +static void computeRangePlot(SandboxParameters ¶meters) { + const ColorGradingOptions& options = parameters.colorGradingOptions; + for (size_t i = 0; i < 1024; i++) { + float x = i / 1024.0f; + float s = 1.0f - smoothstep(options.ranges.x, options.ranges.y, x); + float h = smoothstep(options.ranges.z, options.ranges.w, x); + g_rangePlot[i] = s; + g_rangePlot[1024 + i] = 1.0f - s - h; + g_rangePlot[2048 + i] = h; + } +} + static void gui(filament::Engine* engine, filament::View*) { auto& params = g_params; ImGui::SetNextWindowSize(ImVec2(0.0f, 0.0f)); @@ -576,11 +589,30 @@ static void gui(filament::Engine* engine, filament::View*) { ImGui::Combo("Tone-mapping", reinterpret_cast(¶ms.colorGradingOptions.toneMapping), "Linear\0ACES\0Filmic\0Reinhard\0Display Range\0\0"); - ImGui::SliderInt("Temperature", ¶ms.colorGradingOptions.temperature, -100, 100); - ImGui::SliderInt("Tint", ¶ms.colorGradingOptions.tint, -100, 100); - ImGui::SliderFloat3("Out Red", ¶ms.colorGradingOptions.outRed.x, -2.0f, 2.0f); - ImGui::SliderFloat3("Out Green", ¶ms.colorGradingOptions.outGreen.x, -2.0f, 2.0f); - ImGui::SliderFloat3("Out Blue", ¶ms.colorGradingOptions.outBlue.x, -2.0f, 2.0f); + ImGui::Indent(); + if (ImGui::CollapsingHeader("While balance")) { + ImGui::SliderInt("Temperature", ¶ms.colorGradingOptions.temperature, -100, 100); + ImGui::SliderInt("Tint", ¶ms.colorGradingOptions.tint, -100, 100); + } + if (ImGui::CollapsingHeader("Channel mixer")) { + ImGui::SliderFloat3("Out Red", ¶ms.colorGradingOptions.outRed.x, -2.0f, 2.0f); + ImGui::SliderFloat3("Out Green", ¶ms.colorGradingOptions.outGreen.x, -2.0f, 2.0f); + ImGui::SliderFloat3("Out Blue", ¶ms.colorGradingOptions.outBlue.x, -2.0f, 2.0f); + } + if (ImGui::CollapsingHeader("Tonal ranges")) { + ImGui::ColorEdit3("Shadows", ¶ms.colorGradingOptions.shadows.x); + ImGui::SliderFloat("Shadows weight", ¶ms.colorGradingOptions.shadows.w, -2.0f, 2.0f); + ImGui::ColorEdit3("Mid-tones", ¶ms.colorGradingOptions.midtones.x); + ImGui::SliderFloat("Mid-tones weight", ¶ms.colorGradingOptions.midtones.w, -2.0f, 2.0f); + ImGui::ColorEdit3("Highlights", ¶ms.colorGradingOptions.highlights.x); + ImGui::SliderFloat("Highlights weight", ¶ms.colorGradingOptions.highlights.w, -2.0f, 2.0f); + ImGui::SliderFloat4("Ranges", ¶ms.colorGradingOptions.ranges.x, 0.0f, 1.0f); + computeRangePlot(params); + ImGui::PlotLines("Shadows curve", g_rangePlot, 1024, 0); + ImGui::PlotLines("Mid-tones curve", g_rangePlot + 1024, 1024); + ImGui::PlotLines("Highlights curve", g_rangePlot + 2048, 1024); + } + ImGui::Unindent(); } if (ImGui::CollapsingHeader("Debug")) { @@ -699,6 +731,12 @@ static void preRender(filament::Engine* engine, filament::View* view, filament:: ColorGrading* colorGrading = ColorGrading::Builder() .whiteBalance(options.temperature / 100.0f, options.tint / 100.0f) .channelMixer(options.outRed, options.outGreen, options.outBlue) + .shadowsMidtonesHighlights( + Color::toLinear(options.shadows), + Color::toLinear(options.midtones), + Color::toLinear(options.highlights), + options.ranges + ) .toneMapping(options.toneMapping) .build(*engine); view->setColorGrading(colorGrading); diff --git a/samples/material_sandbox.h b/samples/material_sandbox.h index 27806fdbb4..263ad125c0 100644 --- a/samples/material_sandbox.h +++ b/samples/material_sandbox.h @@ -67,6 +67,10 @@ struct ColorGradingOptions { math::float3 outRed{1.0f, 0.0f, 0.0f}; math::float3 outGreen{0.0f, 1.0f, 0.0f}; math::float3 outBlue{0.0f, 0.0f, 1.0f}; + math::float4 shadows{1.0f, 1.0f, 1.0f, 0.0f}; + math::float4 midtones{1.0f, 1.0f, 1.0f, 0.0f}; + math::float4 highlights{1.0f, 1.0f, 1.0f, 0.0f}; + math::float4 ranges{0.0f, 0.333f, 0.550f, 1.0f}; }; struct SandboxParameters {