0

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

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;
}

编辑:作为参考,这是与此问题相关的帖子,目的是解释“错误”在实践中的样子:https ://graphicdesign.stackexchange.com/questions/64890/in-gimp-how-做我得到的污迹模糊工具正确工作

4

4 回答 4

0

当您不得不担心伽玛时,没有合适的答案。

在混合、混合、计算照明等时,您通常希望在线性色彩空间中工作。

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

完成后,您要确保针对输出设备的色彩空间校正线性值,无论是简单的伽马变换还是其他色彩空间变换。同样,这里没有简单的答案,因为您必须知道该转换是在堆栈中的较低级别隐式为您完成的,还是您的责任。

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

于 2016-02-29T22:45:02.390 回答
0

您的第二个代码示例是完全正确的,只是 alpha 通道通常没有经过伽马校正,因此您不应该使用pow它。为了效率起见,最好对每个通道进行一次伽马校正,而不是加倍。

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

有时您可能会发现在未经校正的空间中工作可以获得更好的结果。例如,如果您正在调整图像大小,则在缩小尺寸时应该进行伽马校正,但在放大尺寸时则不进行。我忘记了我在哪里读到的,但我自己验证了它——如果你使用伽马校正像素值与线性像素值相比,放大后的伪影就不那么令人反感了。

于 2016-03-01T04:06:11.093 回答
0

您的问题完全在于感知颜色实现领域。要处理感知亮度畸变,您可以使用在线找到的众多算法之一,其中一种算法是Luma

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

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

colorRGB --(convert)--> colorPerceptual --(input)--> f ( colorPerceptual ) --(output)--> colorPerceptual' --(convert)--> 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;
}
于 2016-03-01T03:51:44.007 回答
0

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

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

您目前设置计算的方式允许负面结果,这可以解释黑点。

于 2016-02-29T19:25:23.933 回答