在图形中,我什么时候需要考虑Gamma?

时间:2016-02-29 18:25:04

标签: c++ graphics colors gamma

所以我有一些代码可以在两种输入颜色之间生成线性渐变:

struct color {
    float r, g, b, a;
}

color produce_gradient(const color & c1, const color & c2, float ratio) {
    color output_color;
    output_color.r = c1.r + (c2.r - c1.r) * ratio;
    output_color.g = c1.g + (c2.g - c1.g) * ratio;
    output_color.b = c1.b + (c2.b - c1.b) * ratio;
    output_color.a = c1.a + (c2.a - c1.a) * ratio;
    return output_color;
}

我也在我的着色器中编写(语义相同)代码。

问题是使用这种代码会产生"暗带"在颜色相遇的中间,由于计算机屏幕与用于表示这些像素的原始数据之间的亮度变化的怪癖。

所以我的问题是:

  • 我是否需要在主机功能,设备功能,或两者中都进行伽马校正?
  • 纠正正确处理伽玛功能的最佳方法是什么?我在下面提供的代码是否以适当的方式转换颜色?

代码:

color produce_gradient(const color & c1, const color & c2, float ratio) {
    color output_color;
    output_color.r = pow(pow(c1.r,2.2) + (pow(c2.r,2.2) - pow(c1.r,2.2)) * ratio, 1/2.2);
    output_color.g = pow(pow(c1.g,2.2) + (pow(c2.g,2.2) - pow(c1.g,2.2)) * ratio, 1/2.2);
    output_color.b = pow(pow(c1.b,2.2) + (pow(c2.b,2.2) - pow(c1.b,2.2)) * ratio, 1/2.2);
    output_color.a = pow(pow(c1.a,2.2) + (pow(c2.a,2.2) - pow(c1.a,2.2)) * ratio, 1/2.2);
    return output_color;
}

编辑:作为参考,这里有一个与此问题相关的帖子,目的是解释" bug"在实践中看起来像:https://graphicdesign.stackexchange.com/questions/64890/in-gimp-how-do-i-get-the-smudge-blur-tools-to-work-properly

4 个答案:

答案 0 :(得分:0)

我认为您的代码存在缺陷。 首先,我要确保0 <= ratio <=1

第二,我将使用公式c1.x * (1-ratio) + c2.x *ratio

您此刻设置计算的方式可以产生负面结果,这可以解释黑点。

答案 1 :(得分:0)

当你不得不担心伽玛时,没有答案。

您通常希望在混合,混合,计算光照等时使用线性色彩空间

如果您的输入不在线性空间中(例如,经过伽马校正或处于某些颜色空间,如sRGB),那么您通常希望将它们一次转换为线性。您还没有告诉我们您的输入是否为线性RGB。

完成后,您需要确保为输出设备的色彩空间校正线性值,无论是简单的伽玛还是其他色彩空间变换。同样,这里没有轻拍答案,因为您必须知道是否在堆栈的较低级别隐式地为您进行了转换,或者您是否负责。

那就是说,很多代码都会因为作弊而逃脱。他们将在sRGB中输入他们的输入并应用alpha混合或淡化,就好像它们是线性RGB一样,然后按原样输出结果(可能是钳位)。有时这是一个合理的权衡。

答案 2 :(得分:0)

你的问题完全在于感知色彩实现领域。 为了处理感知亮度偏差,您可以使用在线发现的众多算法之一 一个这样的算法是http://www.codeproject.com/Tips/715891/Compiling-Csharp-Code-at-Runtime

float luma(color c){
return 0.30 * c.r + 0.59 * c.g + 0.11 * c.b;
}

此时我想指出标准方法是在感知颜色空间中应用所有算法,然后转换为rgb颜色空间进行显示。

colorRGB - (转换) - &gt; colorPerceptual - (输入) - &gt; f( colorPerceptual ) - (输出) - &gt; colorPerceptual' - (转换) - &gt;的 colorRGB

但是如果你只想调整亮度(感知色差不会被修复),你可以用以下方式有效地做到这一点

//define color of unit lightness. based on Luma algorithm 
color unit_l(1/0.3/3, 1/0.59/3, 1/0.11/3);

color produce_gradient(const color & c1, const color & c2, float ratio) {
    color output_color;
    output_color.r = c1.r + (c2.r - c1.r) * ratio;
    output_color.g = c1.g + (c2.g - c1.g) * ratio;
    output_color.b = c1.b + (c2.b - c1.b) * ratio;
    output_color.a = c1.a + (c2.a - c1.a) * ratio;

    float target_lightness = luma(c1) + (luma(c2) - luma(c1)) * ratio; //linearly interpolate perceptual lightness
    float delta_lightness = target_lightness - luma(output_color); //calculate required lightness change magnitude

    //adjust lightness
    output_color.g += unit_l.r * delta_lightness;
    output_color.b += unit_l.g * delta_lightness;
    output_color.a += unit_l.b * delta_lightness;

    //at this point luma(output_color) approximately equals target_lightness which takes care of the perceptual lightness aberrations

    return output_color;
}

答案 3 :(得分:0)

您的第二个代码示例完全正确,但alpha通道通常未经伽马校正,因此您不应在其上使用pow。为了效率,最好对每个通道进行一次伽马校正,而不是加倍。

一般规则是,无论何时添加或减去值,都必须在两个方向上进行伽玛。如果您只是乘法或除法,则没有区别:pow(pow(x, 2.2) * pow(y, 2.2), 1/2.2)在数学上等同于x * y

有时您可能会发现通过在未经修正的空间中工作可以获得更好的效果。例如,如果您要调整图像大小,则应该在缩小尺寸时进行伽玛校正,但如果您正在进行尺寸调整则不应该进行伽玛校正。我忘记了我读到这里的内容,但我自己验证了 - 如果你使用伽玛校正的像素值而不是线性像素,那么来自大小化的伪像就不那么令人反感了。