Mi aplicación a menudo cambia uno de estos canales sin cambiar los otros dos, y aceleraría mi renderizado si pudiera componer imágenes existentes en la GPU en lugar de representar una imagen completamente nueva cada vez que algo cambia. [OP, @ZevEisenberg]
Con respecto a la solidez y la GPU, que acababa de tirar a conversion function en un fragment shader (e.g.). Esto leería HSV almacenado en una textura o tres texturas diferentes, haga la conversión por píxel y la salida RGB. Bonito y fácil. No veo ningún beneficio al no cambiar otras capas ya que H, S o V afectarán a todos los canales RGB. Tal vez almacenar resultados RGB intermedios como hue=hsv2rgb(H,1,1)
y actualizar con final=(hue*S+1-S)*V
, almacenar en caché hue-to-rgb, pero no creo que valga la pena.
De todos modos, cada modo de mezcla tiene una fórmula simple, y se podía unirlos para el VHS que involucra un conjunto extraordinariamente complejo de texturas intermedias, pero será mucho más lento, principalmente debido al ancho de banda de almacenamiento y la memoria temporal innecesaria. Por no hablar, tratando de volver a escribir la fórmula en funciones de mezcla suena bastante difícil, lo que con ramificación, divisiones, fract
, sujeción, etc ... absolutos
Estoy muy interesado en una solución para la división de una imagen en sus componentes HSV y recrean la imagen original usando modos de fusión en Photoshop. [Bounty, @phisch]
Con respecto al photoshop ... No estoy hecho de dinero. Entonces en gimp, hay Colours -> Components -> Compose/Decompose
que hace esto por usted. Me sorprendería que esto no exista en Photoshop, pero tampoco es así. Tal vez hay scripts/plugins de photoshop que podrían hacerlo si no? Pero dijiste específicamente blending. Su pregunta podría obtener una mejor atención al https://graphicdesign.stackexchange.com/.A continuación, he dado una idea de la complejidad involucrada y dudo que Photoshop pueda hacerlo. Puede haber formas en torno a los valores de píxel por fuera de 0 a 1, pero luego podría encontrarse con problemas de precisión, simplemente no debería hacerse.
De todos modos, un desafío es un desafío a pesar de lo poco práctico. Lo siguiente es solo por diversión.
Voy a empezar con la siguiente función (de here) y tres texturas ... 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);
}
Sólo sé OpenGL y no estoy seguro de cómo haría esto sin texturas de coma flotante o algunas de las funciones de fusión extendidas, así que las estoy usando. Pero solo puedo mezclar (sin sombreadores). Para las constantes haré texturas con (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) y (0,0,0) porque no pude obtener GL_ZERO para escalar con GL_DIFFERENCE_NV
.
- comienzo con textura tonalidad
- mezcla uso de aditivos para agregar (1,2/3,1/3)
encontrar la parte fraccional
- con mezcla sustractiva, restar 0,5 (esto es para un
floor()
ya que estoy asumiendo GL colores colores al convertir a 8 bits. Si no, omita esto)
- escalado por 1/255. esto podría hacerse con mezclas alfa regulares, pero en cambio, he escalado con una textura de color.
- pase a través de un punto de la textura no flotante para redondear a la escala 1/255
más cercana copia de seguridad de 255 (de nuevo en un punto de la textura flotante)
Ahora tenemos la componente entero. restar esto desde lo que empezamos con
escala 6
- con la mezcla sustractiva, tomar 3
tomar el valor absoluto de la
estoy simplemente use GL_DIFFERENCE_NV
para esto, pero sin él podría haber una forma de usar dos abrazaderas separadas para el próximo paso. ya que los negativos se pinzarán de todos modos, algo similar a clamp(p-K.xxx,0,1) + clamp(-p-K.xxx,0,1)
.
restar 1
bueno, eso es tonalidad hecho
podría sujetar al pasar por un punto de la textura no flotante, pero sólo va a utilizar GL_MIN
ahora podía utilizar alfa mezcla para mix()
, pero la saturación se carga como una imagen en blanco y negro sin un canal alfa. ya que es la mezcla de blanco, hacerlo a mano es realmente más fácil ...
escala por la saturación
- añadir al menos 1
restar la saturación
y la saturación se ha aplicado
escala por valor
y está la imagen
café
Todo hecho utilizando
glBlendEquation
con GL_FUNC_REVERSE_SUBTRACT
, GL_MIN
y GL_DIFFERENCE_NV
glBlendFunc
Aquí está mi código ...
//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
Creo que es una pregunta muy interesante. Estoy tratando de descubrir la respuesta también. ¿Encontraste algo? – Alejandro
En realidad estaba jugando con esto recientemente. No encontré una solución exacta, pero Screen se acerca. Una imagen que creé en Photoshop en realidad terminó viéndose mejor (colores más brillantes y más saturados) que la versión generada por código, pero por supuesto si buscas precisión, eso no servirá para nada. ¿Tal vez podrías usar alguna combinación de modos de mezcla de Matiz y Luminosidad con capas duplicadas? –