我的应用程序经常更改其中一个通道而不更改其他两个通道,如果我可以在 GPU 上合成现有图像,而不是每次发生变化时都渲染一个全新的图像,那么我的渲染速度将会加快。 [OP,@ZevEisenberg]
关于速度和 GPU,我只想抛出转换函数 http://www.cs.rit.edu/%7Encs/color/t_convert.html进入片段着色器(e.g. https://stackoverflow.com/questions/15095909/from-rgb-to-hsv-in-opengl-glsl)。这将读取存储在纹理或三个不同纹理中的 HSV,对每个像素进行转换并输出 RGB。好,易于。我看不出不更改其他图层有什么好处,因为 H、S 或 V 都会影响所有 RGB 通道。也许存储中间 RGB 结果,例如hue=hsv2rgb(H,1,1)
,并更新为final=(hue*S+1-S)*V
,缓存色调到RGB,但我认为这不值得。
无论如何,每种混合模式都有一个简单的公式,您可以将它们串在一起以用于涉及一组过于复杂的中间纹理的 HSV,但它会much速度较慢主要是因为不必要的临时存储和内存带宽。更不用说,尝试将公式重写为混合函数听起来相当具有挑战性,对于分支、除法、fract
、夹紧、绝对值等...
我对将图像分割为其 HSV 组件并使用 Photoshop 中的混合模式重新创建原始图像的解决方案非常感兴趣。 [赏金,@phisch]
至于Photoshop……我不是很有钱。所以在 gimp 中,有Colours -> Components -> Compose/Decompose
它会为你做这件事。如果 Photoshop 中不存在这种情况,我会感到有点惊讶,但也有点不存在。如果没有的话,也许有 Photoshop 脚本/插件可以做到这一点?但你确实说blending。您的问题可能会得到更好的关注https://graphicdesign.stackexchange.com/ https://graphicdesign.stackexchange.com/。下面,我已经给出了所涉及的复杂性的想法,但我怀疑 Photoshop 是否真的可以做到这一点。可能有一些方法可以解决 0 到 1 之外的像素值,但是这样您可能会遇到精度问题,这是不应该这样做的。
不管怎样,挑战就是挑战,尽管不切实际。以下内容仅供娱乐。
我将从以下函数开始(来自here https://stackoverflow.com/questions/15095909/from-rgb-to-hsv-in-opengl-glsl)和三个 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()
因为我假设 GLrounds转换为 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
well, that's hue done
-
可以通过传递非浮点纹理来夹紧,但只是要使用GL_MIN
-
现在我可以使用 alpha 混合mix()
,但饱和度会作为没有 Alpha 通道的黑白图像加载。因为它是混合白色的,所以手工操作实际上更容易......
按饱和度缩放
-
add 1
-
减去饱和度
and saturation has been applied
-
按价值缩放
and there's the image
-
咖啡时间(休闲时光
全部完成使用
-
glBlendEquation
with GL_FUNC_REVERSE_SUBTRACT
, GL_MIN
and 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