core/colorpipeline: refactor tone mapping to be about dimming instead of addnig headroom
No functional change, just so that the logic is a bit easier to follow
This commit is contained in:
parent
587afb3076
commit
eae1f51304
3 changed files with 21 additions and 16 deletions
|
@ -37,7 +37,7 @@ ColorPipeline ColorPipeline::create(const ColorDescription &from, const ColorDes
|
||||||
// that's not necessarily true, and figuring out the actual range could be complicated..
|
// that's not necessarily true, and figuring out the actual range could be complicated..
|
||||||
ret.addMatrix(from.toOther(to, intent), ret.currentOutputRange() * (to.referenceLuminance() / from.referenceLuminance()));
|
ret.addMatrix(from.toOther(to, intent), ret.currentOutputRange() * (to.referenceLuminance() / from.referenceLuminance()));
|
||||||
if (!s_disableTonemapping && ret.currentOutputRange().max > maxOutputLuminance * 1.01 && intent == RenderingIntent::Perceptual) {
|
if (!s_disableTonemapping && ret.currentOutputRange().max > maxOutputLuminance * 1.01 && intent == RenderingIntent::Perceptual) {
|
||||||
ret.addTonemapper(to.containerColorimetry(), to.referenceLuminance(), ret.currentOutputRange().max, maxOutputLuminance, 1.5);
|
ret.addTonemapper(to.containerColorimetry(), to.referenceLuminance(), ret.currentOutputRange().max, maxOutputLuminance);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.addInverseTransferFunction(to.transferFunction());
|
ret.addInverseTransferFunction(to.transferFunction());
|
||||||
|
@ -240,7 +240,7 @@ static const QMatrix4x4 s_toICtCp = QMatrix4x4(
|
||||||
0.0, 0.0, 0.0, 1.0).transposed();
|
0.0, 0.0, 0.0, 1.0).transposed();
|
||||||
static const QMatrix4x4 s_fromICtCp = s_toICtCp.inverted();
|
static const QMatrix4x4 s_fromICtCp = s_toICtCp.inverted();
|
||||||
|
|
||||||
void ColorPipeline::addTonemapper(const Colorimetry &containerColorimetry, double referenceLuminance, double maxInputLuminance, double maxOutputLuminance, double maxAddedHeadroom)
|
void ColorPipeline::addTonemapper(const Colorimetry &containerColorimetry, double referenceLuminance, double maxInputLuminance, double maxOutputLuminance)
|
||||||
{
|
{
|
||||||
// convert from rgb to ICtCp
|
// convert from rgb to ICtCp
|
||||||
addMatrix(containerColorimetry.toLMS(), currentOutputRange());
|
addMatrix(containerColorimetry.toLMS(), currentOutputRange());
|
||||||
|
@ -249,8 +249,8 @@ void ColorPipeline::addTonemapper(const Colorimetry &containerColorimetry, doubl
|
||||||
// apply the tone mapping to the intensity component
|
// apply the tone mapping to the intensity component
|
||||||
ops.push_back(ColorOp{
|
ops.push_back(ColorOp{
|
||||||
.input = currentOutputRange(),
|
.input = currentOutputRange(),
|
||||||
.operation = ColorTonemapper(referenceLuminance, maxInputLuminance, maxOutputLuminance, maxAddedHeadroom),
|
.operation = ColorTonemapper(referenceLuminance, maxInputLuminance, maxOutputLuminance),
|
||||||
.output = ValueRange {
|
.output = ValueRange{
|
||||||
.min = currentOutputRange().min,
|
.min = currentOutputRange().min,
|
||||||
.max = maxOutputLuminance,
|
.max = maxOutputLuminance,
|
||||||
},
|
},
|
||||||
|
@ -335,23 +335,26 @@ ColorMultiplier::ColorMultiplier(double factor)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorTonemapper::ColorTonemapper(double referenceLuminance, double maxInputLuminance, double maxOutputLuminance, double maxAddedHeadroom)
|
ColorTonemapper::ColorTonemapper(double referenceLuminance, double maxInputLuminance, double maxOutputLuminance)
|
||||||
: m_inputReferenceLuminance(referenceLuminance)
|
: m_inputReferenceLuminance(referenceLuminance)
|
||||||
, m_maxInputLuminance(maxInputLuminance)
|
, m_maxInputLuminance(maxInputLuminance)
|
||||||
, m_maxOutputLuminance(maxOutputLuminance)
|
, m_maxOutputLuminance(maxOutputLuminance)
|
||||||
{
|
{
|
||||||
m_inputRange = maxInputLuminance / referenceLuminance;
|
m_inputRange = maxInputLuminance / referenceLuminance;
|
||||||
const double outputRange = maxOutputLuminance / referenceLuminance;
|
const double outputRange = maxOutputLuminance / referenceLuminance;
|
||||||
// = how much dynamic range this algorithm adds, by reducing the reference luminance
|
// how much range we need to at least decently present the content
|
||||||
m_addedRange = std::min(m_inputRange, std::clamp(maxAddedHeadroom / outputRange, 1.0, maxAddedHeadroom));
|
// 50% HDR headroom should be enough for the tone mapper to do a good enough job, without dimming the image too much
|
||||||
m_outputReferenceLuminance = referenceLuminance / m_addedRange;
|
const double minDecentRange = std::min(m_inputRange, 1.5);
|
||||||
|
// if the output doesn't provide enough HDR headroom for the tone mapper to do a good job, dim the image to create some
|
||||||
|
m_referenceDimming = 1.0 / std::clamp(outputRange / minDecentRange, 1.0, minDecentRange);
|
||||||
|
m_outputReferenceLuminance = referenceLuminance * m_referenceDimming;
|
||||||
}
|
}
|
||||||
|
|
||||||
double ColorTonemapper::map(double pqEncodedLuminance) const
|
double ColorTonemapper::map(double pqEncodedLuminance) const
|
||||||
{
|
{
|
||||||
const double luminance = TransferFunction(TransferFunction::PerceptualQuantizer).encodedToNits(pqEncodedLuminance);
|
const double luminance = TransferFunction(TransferFunction::PerceptualQuantizer).encodedToNits(pqEncodedLuminance);
|
||||||
// keep things linear up to the reference luminance
|
// keep things linear up to the reference luminance
|
||||||
const double low = std::min(luminance / m_addedRange, m_outputReferenceLuminance);
|
const double low = std::min(luminance * m_referenceDimming, m_outputReferenceLuminance);
|
||||||
// and apply a nonlinear curve above, to reduce the luminance without completely removing differences
|
// and apply a nonlinear curve above, to reduce the luminance without completely removing differences
|
||||||
const double relativeHighlight = std::clamp((luminance / m_inputReferenceLuminance - 1.0) / (m_inputRange - 1.0), 0.0, 1.0);
|
const double relativeHighlight = std::clamp((luminance / m_inputReferenceLuminance - 1.0) / (m_inputRange - 1.0), 0.0, 1.0);
|
||||||
const double high = std::log(relativeHighlight * (std::numbers::e - 1) + 1) * (m_maxOutputLuminance - m_outputReferenceLuminance);
|
const double high = std::log(relativeHighlight * (std::numbers::e - 1) + 1) * (m_maxOutputLuminance - m_outputReferenceLuminance);
|
||||||
|
|
|
@ -67,7 +67,7 @@ public:
|
||||||
class KWIN_EXPORT ColorTonemapper
|
class KWIN_EXPORT ColorTonemapper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit ColorTonemapper(double referenceLuminance, double maxInputLuminance, double maxOutputLuminance, double maxAddedHeadroom);
|
explicit ColorTonemapper(double referenceLuminance, double maxInputLuminance, double maxOutputLuminance);
|
||||||
|
|
||||||
double map(double pqEncodedLuminance) const;
|
double map(double pqEncodedLuminance) const;
|
||||||
bool operator==(const ColorTonemapper &) const = default;
|
bool operator==(const ColorTonemapper &) const = default;
|
||||||
|
@ -77,7 +77,7 @@ public:
|
||||||
double m_maxOutputLuminance;
|
double m_maxOutputLuminance;
|
||||||
private:
|
private:
|
||||||
double m_inputRange;
|
double m_inputRange;
|
||||||
double m_addedRange;
|
double m_referenceDimming;
|
||||||
double m_outputReferenceLuminance;
|
double m_outputReferenceLuminance;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ public:
|
||||||
void addTransferFunction(TransferFunction tf);
|
void addTransferFunction(TransferFunction tf);
|
||||||
void addInverseTransferFunction(TransferFunction tf);
|
void addInverseTransferFunction(TransferFunction tf);
|
||||||
void addMatrix(const QMatrix4x4 &mat, const ValueRange &output);
|
void addMatrix(const QMatrix4x4 &mat, const ValueRange &output);
|
||||||
void addTonemapper(const Colorimetry &containerColorimetry, double referenceLuminance, double maxInputLuminance, double maxOutputLuminance, double maxAddedHeadroom);
|
void addTonemapper(const Colorimetry &containerColorimetry, double referenceLuminance, double maxInputLuminance, double maxOutputLuminance);
|
||||||
void add(const ColorOp &op);
|
void add(const ColorOp &op);
|
||||||
|
|
||||||
ValueRange inputRange;
|
ValueRange inputRange;
|
||||||
|
|
|
@ -114,14 +114,16 @@ vec3 doTonemapping(vec3 color) {
|
||||||
vec3 ICtCp = toICtCp * lms_PQ;
|
vec3 ICtCp = toICtCp * lms_PQ;
|
||||||
float luminance = singlePqToLinear(ICtCp.r) * 10000.0;
|
float luminance = singlePqToLinear(ICtCp.r) * 10000.0;
|
||||||
|
|
||||||
// if the reference is too close to the maximum luminance, reduce it to get up to 50% headroom
|
|
||||||
float inputRange = maxTonemappingLuminance / destinationReferenceLuminance;
|
float inputRange = maxTonemappingLuminance / destinationReferenceLuminance;
|
||||||
float outputRange = maxDestinationLuminance / destinationReferenceLuminance;
|
float outputRange = maxDestinationLuminance / destinationReferenceLuminance;
|
||||||
float addedRange = min(inputRange, clamp(1.5 / outputRange, 1.0, 1.5));
|
// how much dynamic range we need to decently present the content
|
||||||
float outputReferenceLuminance = destinationReferenceLuminance / addedRange;
|
float minDecentRange = min(inputRange, 1.5);
|
||||||
|
// if the output doesn't provide enough HDR headroom for the tone mapper to do a good job, dim the image to create some
|
||||||
|
float referenceDimming = 1.0 / clamp(outputRange / minDecentRange, 1.0, minDecentRange);
|
||||||
|
float outputReferenceLuminance = destinationReferenceLuminance * referenceDimming;
|
||||||
|
|
||||||
// keep it linear up to the reference luminance
|
// keep it linear up to the reference luminance
|
||||||
float low = min(luminance / addedRange, outputReferenceLuminance);
|
float low = min(luminance * referenceDimming, outputReferenceLuminance);
|
||||||
// and apply a nonlinear curve above, to reduce the luminance without completely removing differences
|
// and apply a nonlinear curve above, to reduce the luminance without completely removing differences
|
||||||
float relativeHighlight = clamp((luminance / destinationReferenceLuminance - 1.0) / (inputRange - 1.0), 0.0, 1.0);
|
float relativeHighlight = clamp((luminance / destinationReferenceLuminance - 1.0) / (inputRange - 1.0), 0.0, 1.0);
|
||||||
const float e = 2.718281828459045;
|
const float e = 2.718281828459045;
|
||||||
|
|
Loading…
Reference in a new issue