【问题标题】:In Graphics, when do I need to account for Gamma?在图形中,我什么时候需要考虑 Gamma?
【发布时间】:2016-06-12 22:02:40
【问题描述】:

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

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-do-i-get-the-smudge-blur-tools-to-work-properly

【问题讨论】:

    标签: c++ graphics colors gamma


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

      您的问题完全在于感知颜色实现领域。 要处理感知亮度畸变,您可以使用在线找到的众多算法之一 一种这样的算法是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;
      }
      

      【讨论】:

        【解决方案3】:

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

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

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

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

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

        【讨论】:

          【解决方案4】:

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

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

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

          【讨论】:

          • 您提供的公式在数学上与我的相同。除非您的公式忘记了 1-ratio 周围的必要括号。
          • 然而,如果 c1.xc2.x 是正数,那么一个有效的 ratio 并且我意思是 0 <= ratio <=1 不会产生 output_color.x 这很可能是您遇到舍入错误,您需要使用 unsigned float
          • 我不关心限制ratio 的输入域。 ratio 值介于 0.25 和 0.75 之间时会出现问题。
          • 暗带问题很奇怪。我看不到您的整个代码,但在计算返回预期值的假设下,我建议将您的颜色从 rgb 空间转换为 hsl 空间以进行计算。这有点矫枉过正,但正如我所说,不知道其余的代码......
          • 我将此链接添加到我的帖子中,因为它似乎是我能找到的与此问题最明显相关的帖子:graphicdesign.stackexchange.com/questions/64890/…
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-21
          相关资源
          最近更新 更多