使用混合模式重新创建HSV颜色

Zev*_*erg 6 graphics compositing alphablending colors color-blending

我正在开发一个应用程序来创建图像,其色调,饱和度和值根据不同的参数而变化.出于性能原因,分别渲染色调,饱和度和值组件,然后使用Photoshop样式混合模式(乘法,叠加,屏幕,色调等)将它们合成在一起是有意义的.

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

由组件制成的RGB图像

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

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

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

joz*_*yqk 7

我的应用程序经常更改其中一个通道而不更改其他两个通道,如果我可以在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脚本/插件可以做到,如果没有?但你确实说过混合.您可以通过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);
}
Run Code Online (Sandbox Code Playgroud)

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通道的黑白图像.因为它是混合白色,手工制作实际上更容易......

    按饱和度衡量

  10. 加1
  11. 减去饱和度

    在此输入图像描述 和饱和度已被应用

  12. 按价值扩大

    在此输入图像描述 而且有图像

  13. 咖啡时间(休闲时光

全部完成使用

  • glBlendEquationGL_FUNC_REVERSE_SUBTRACT,GL_MINGL_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
Run Code Online (Sandbox Code Playgroud)