6

我正在开发一个应用程序,该应用程序创建的图像的色调、饱和度和值会根据不同的参数而变化。出于性能原因,单独渲染色调、饱和度和值分量,然后使用 Photoshop 风格的混合模式(正片叠底、叠加、屏幕、色调等)将它们合成在一起是有意义的。

我已经知道如何对 RGB 图像执行此操作:将每个通道拆分为自己的红色、绿色或蓝色图像,其值范围从透明到该通道的颜色。将它们叠加在黑色之上并将它们的混合模式设置为屏幕,嘿,你有你的彩色图像:

由组件制成的 RGB 图像

我将如何使用由 HSV 值定义的图像来执行此操作?我的应用程序经常更改其中一个通道而不更改其他两个通道,如果我可以在 GPU 上合成现有图像而不是每次发生变化时都渲染一个全新的图像,它将加快我的渲染速度。

这是一个例子: 使用单独的 H、S 和 V 通道生成的图像

在此示例中,色调在圆周上从 0º 到 360º 变化,饱和度从中心到边缘从 0% 到 100% 变化,亮度 (V) 在圆周上从 0% 到 100% 变化。这是我的应用程序创建的典型图像类型。有没有可以用来分别创建这些通道并以数学上完美的方式合成它们的常见混合模式的组合?

4

1 回答 1

10

我的应用程序经常更改其中一个通道而不更改其他两个通道,如果我可以在 GPU 上合成现有图像而不是每次发生变化时都渲染一个全新的图像,它将加快我的渲染速度。[OP,@ZevEisenberg]

关于速度和 GPU,我只是将转换函数放入片段着色器(例如)中。这将读取存储在纹理或三个不同纹理中的 HSV,按像素进行转换并输出 RGB。好,易于。我看不出不更改其他图层有什么好处,因为 H、S 或 V 都会影响所有 RGB 通道。也许存储中间 RGB 结果,例如hue=hsv2rgb(H,1,1),并更新final=(hue*S+1-S)*V,缓存 hue-to-rgb 但我认为这不值得。

无论如何,每种混合模式都有一个简单的公式,您可以将它们串在一起以用于 HSV,涉及一组过于复杂的中间纹理,但它会慢得多,主要是因为不必要的临时存储和内存带宽。更不用说,尝试将公式重写为混合函数听起来非常具有挑战性,分支、除法fract、、钳位、绝对等...

我对将图像拆分为其 HSV 组件并使用 Photoshop 中的混合模式重新创建原始图像的解决方案非常感兴趣。[赏金,@phisch]

关于photoshop...我不是靠钱做的。所以在 gimp 中,有Colours -> Components -> Compose/Decompose哪个可以为你做到这一点。如果这在 Photoshop 中不存在,我会有点惊讶,但也有点奇怪。如果没有的话,也许有 Photoshop 脚本/插件可以做到这一点?但是你确实特别说blending您的问题可能会在https://graphicdesign.stackexchange.com/上得到更多关注。下面,我对所涉及的复杂性给出了一个想法,我怀疑 Photoshop 是否真的可以做到。在 0 到 1 之外的像素值可能有一些方法,但是您可能会遇到精度问题,这是不应该的。


不管怎样,挑战就是挑战,尽管不切实际。以下内容仅供娱乐。

我将从以下函数(从这里)和三个 HSV 纹理开始......

vec3 hsv2rgb(vec3 c)
{
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

H H H

我只知道OpenGL,我不确定在没有浮点纹理或一些扩展混合功能的情况下如何做到这一点,所以我正在使用它们。但我只被允许使用混合(没有任何着色器)。对于常量,我将使用 (1,1,1), (1,2/3,1/3), (3,3,3), (6,6,6) (1/255,1) 制作纹理/255,1/255), (255,255,255), (1/2,1/2,1/2) 和 (0,0,0) 因为我无法让 GL_ZERO 与GL_DIFFERENCE_NV.

  1. 从色调纹理开始
  2. 使用添加剂混合添加 (1,2/3,1/3)
  3. 找到小数部分

    1. 使用减法混合,减去 0.5(这是floor()因为我假设 GL在转换为 8 位时会入颜色。如果不是,请跳过此步骤)
    2. 缩小 1/255。这可以通过常规的 alpha 混合来完成,但我已经使用颜色纹理进行了缩放。
    3. 通过非浮点纹理四舍五入到最接近的 1/255
    4. 放大 255(回到浮点纹理)

      整数

    5. 现在我们有了整数分量。从我们开始的内容中减去这个

      分数

  4. 放大 6

  5. 减法混合,取 3
  6. 取值的绝对值

    我将简单地使用GL_DIFFERENCE_NV它,但如果没有它,下一步可能会使用两个单独的夹子。因为底片无论如何都会被钳制,类似于clamp(p-K.xxx,0,1) + clamp(-p-K.xxx,0,1).

  7. 减 1

    在此处输入图像描述好了,色调就完成了

  8. 可以通过非浮点纹理进行夹紧,但只是要使用GL_MIN

  9. 现在我可以使用 alpha 混合mix(),但饱和度被加载为没有 alpha 通道的 B/W 图像。因为它是混合白色的,所以用手做实际上更容易......

    按饱和度缩放

  10. 加 1
  11. 减去饱和度

    在此处输入图像描述并且已经应用​​了饱和度

  12. 按值缩放

    在此处输入图像描述还有图像

  13. 咖啡时间(休闲时光

全部使用完成

  • glBlendEquationGL_FUNC_REVERSE_SUBTRACT_ GL_MIN_GL_DIFFERENCE_NV
  • glBlendFunc

这是我的代码...

//const tex init
constTex[0] = makeTex() with 1, 1, 1...
constTex[1] = makeTex() with 1, 2/3, 1/3...
constTex[2] = makeTex() with 3, 3, 3...
constTex[3] = makeTex() with 6, 6, 6...
constTex[4] = makeTex() with 1/255, 1/255, 1/255...
constTex[5] = makeTex() with 255, 255, 255...
constTex[6] = makeTex() with 1/2, 1/2, 1/2...
constTex[7] = makeTex() with 0, 0, 0...

...

fbo[0] = makeFBO() with GL_RGB
fbo[1] = makeFBO() with GL_RGB32F
fbo[2] = makeFBO() with GL_RGB32F

...


hsv[0] = loadTex() hue
hsv[1] = loadTex() value
hsv[2] = loadTex() saturation

...

fbo[1].bind();
glDisable(GL_BLEND);
draw(hsv[0]); //start with hue
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE); //add
draw(constTex[1]); //(1, 2/3, 1/3)
glBlendFunc(GL_ONE, GL_ONE);
fbo[1].unbind();

//compute integer part
fbo[2].bind();
glDisable(GL_BLEND);
draw(*fbo[1].colour[0]); //copy the last bit
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
glBlendFunc(GL_ONE, GL_ONE); //subtract
draw(constTex[6]); //0.5
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ZERO, GL_SRC_COLOR); //scale down
draw(constTex[4]); //1/255
fbo[2].unbind();

fbo[0].bind(); //floor to integer
glDisable(GL_BLEND);
draw(*fbo[2].colour[0]);
fbo[0].unbind();

fbo[2].bind(); //scale back up
glDisable(GL_BLEND);
draw(*fbo[0].colour[0]);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ZERO, GL_SRC_COLOR); //scale up
draw(constTex[5]); //255
fbo[2].unbind();

//take integer part for fractional
fbo[1].bind();
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
glBlendFunc(GL_ONE, GL_ONE); //subtract
draw(*fbo[2].colour[0]); //integer part
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ZERO, GL_SRC_COLOR); //scale
draw(constTex[3]); //6
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
glBlendFunc(GL_ONE, GL_ONE); //subtract
draw(constTex[2]); //3
glBlendEquation(GL_DIFFERENCE_NV);
glBlendFunc(GL_ZERO, GL_ONE); //take the absolute
draw(constTex[7]); //0
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
glBlendFunc(GL_ONE, GL_ONE); //subtract
draw(constTex[0]); //1
glBlendEquation(GL_MIN);
glBlendFunc(GL_ONE, GL_ONE); //clamp (<0 doesn't matter, >1 use min)
draw(constTex[0]); //1
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ZERO, GL_SRC_COLOR); //scale
draw(hsv[1]); //saturation
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ONE, GL_ONE); //add
draw(constTex[0]); //1
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
glBlendFunc(GL_ONE, GL_ONE); //subtract
draw(hsv[1]); //saturation
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_ZERO, GL_SRC_COLOR); //scale
draw(hsv[2]); //saturation
fbo[1].unbind();

fbo[1].blit(); //check result
于 2015-03-06T10:54:31.573 回答