我的应用程序经常更改其中一个通道而不更改其他两个通道,如果我可以在 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);
}
我只知道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/3,1/3)
找到小数部分
- 使用减法混合,减去 0.5(这是
floor()
因为我假设 GL在转换为 8 位时会舍入颜色。如果不是,请跳过此步骤)
- 缩小 1/255。这可以通过常规的 alpha 混合来完成,但我已经使用颜色纹理进行了缩放。
- 通过非浮点纹理四舍五入到最接近的 1/255
放大 255(回到浮点纹理)
现在我们有了整数分量。从我们开始的内容中减去这个
放大 6
- 减法混合,取 3
取值的绝对值
我将简单地使用GL_DIFFERENCE_NV
它,但如果没有它,下一步可能会使用两个单独的夹子。因为底片无论如何都会被钳制,类似于clamp(p-K.xxx,0,1) + clamp(-p-K.xxx,0,1)
.
减 1
好了,色调就完成了
可以通过非浮点纹理进行夹紧,但只是要使用GL_MIN
现在我可以使用 alpha 混合mix()
,但饱和度被加载为没有 alpha 通道的 B/W 图像。因为它是混合白色的,所以用手做实际上更容易......
按饱和度缩放
- 加 1
减去饱和度
并且已经应用了饱和度
按值缩放
还有图像
咖啡时间(休闲时光
全部使用完成
glBlendEquation
和GL_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