Moja aplikacja często zmienia jeden z tych kanałów bez zmiany pozostałe dwa, i to przyspieszyć mój rendering gdybym mógł kompozytowe istniejących obrazów na GPU zamiast renderowania zupełnie nowy obraz za każdym razem coś się zmienia. [OP @ZevEisenberg]
W odniesieniu do trwałości i graficznego, że właśnie rzut a conversion function w cieniującego fragment (e.g.). To odczytałoby HSV przechowywane w tekstury lub trzech różnych teksturach, wykonaj konwersję na piksel i wyjście RGB. Ładne i łatwe. Nie widzę żadnej korzyści, aby nie zmieniać innych warstw, ponieważ H, S lub V będą wpływać na wszystkie kanały RGB. Być może przechowywanie pośrednich wyników RGB, takich jak hue=hsv2rgb(H,1,1)
i aktualizowanie z final=(hue*S+1-S)*V
, buforowanie hue-to-rgb, ale nie sądzę, że warto.
Zresztą, każdy tryb mieszania ma prostą formułę, i można ciąg je razem dla HSV udziałem nadmiernie skomplikowany zestaw tekstur pośrednich, ale to będzie znacznie wolniej głównie z powodu niepotrzebnego tymczasowego magazynowania i przepustowość pamięci. Nie wspominając, próbując przepisać wzór na funkcje mieszania brzmi dość trudne, co z rozgałęzienia, podziałów, fract
, zaciskania, absoluty etc ...
Jestem bardzo zainteresowany w roztworze do dzielenia obrazu na jego komponentów HSV i odtworzyć oryginalny obraz przy użyciu trybów mieszania w programie Photoshop. [Bounty, @phisch]
Co do Photoshopa ... Nie jestem stworzony z pieniędzy. Tak więc w gimpie jest Colours -> Components -> Compose/Decompose
który robi to za Ciebie. Byłbym nieco zaskoczony, gdyby to nie było w Photoshopie, ale potem też nie. Być może są skrypty/wtyczki Photoshopa, które mogą to zrobić, jeśli nie? Ale konkretnie powiedziałeś mieszanie. Twoje pytanie może uzyskać lepszą uwagę pod adresem https://graphicdesign.stackexchange.com/.Poniżej przedstawiłem pojęcie złożoności i wątpię, czy program Photoshop może to zrobić. Może istnieć wiele sposobów na wartości pikseli poza 0 do 1, ale wtedy możesz napotkać problemy z precyzją, po prostu nie powinno się tego robić.
W każdym razie wyzwanie stanowi wyzwanie, mimo że jest niepraktyczne. Poniższe są tylko dla zabawy.
Zacznę poniższej funkcji (od here) i trzech faktur 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);
}
wiem tylko OpenGL i nie jestem jestem pewien, jak bym to zrobił bez tekstur zmiennoprzecinkowych lub niektórych rozszerzonych funkcji mieszania, więc używam ich. Ale mogę używać tylko mieszania (bez shaderów). Dla stałych zrobię tekstury za pomocą (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) i (0,0,0), ponieważ nie mogłem uzyskać skali GL_ZERO za pomocą GL_DIFFERENCE_NV
.
- Rozpocznij barwy tekstury
- stosowanie dodatku mieszania dodać (1,2/3,1/3)
znaleźć część ułamkowa
- z subtraktywnego mieszania, odejmowanie 0,5 (to jest dla
floor()
, ponieważ zakładam, że kolory są konwertowane na 8 bitów. Jeśli nie, pomiń to)
- skalowanie w dół o 1/255. można to zrobić za pomocą zwykłego mieszania alfa, ale zamiast tego przeskalowałem z kolorową teksturą.
- przechodzą przez nie zmiennoprzecinkowej tekstury zaokrąglić do skali najbliższej 1/255
z powrotem przez 255 (z powrotem do zmiennoprzecinkowej tekstury)
teraz mamy składowa całkowita. odjąć od tego, co zaczęliśmy
skalę przez 6
- z subtraktywnego mieszania, weź 3
wziąć bezwzględne wartości
jestem zamierzam po prostu użyć do tego celu GL_DIFFERENCE_NV
, ale bez niego może istnieć sposób użycia dwóch oddzielnych zacisków do następnego kroku. ponieważ i tak negatywy będą zaciśnięte, coś w stylu clamp(p-K.xxx,0,1) + clamp(-p-K.xxx,0,1)
.
odjąć 1
dobrze, to barwa zrobić
mogła zacisnąć przechodząc przez nie-zmiennoprzecinkowej tekstury, ale tylko zamiar użyć GL_MIN
teraz mogę używać alfa mieszanie dla mix()
, ale nasycenie jest ładowane jako obraz czarno-biały bez kanału alfa. ponieważ jest mieszanie biały, robi to ręcznie jest rzeczywiście łatwiej ... skala
przez nasycenie
- dodać 1
odjąć nasycenie
i nasycenie zostało zastosowane
- Skala
według wartości
i jest tam zdjęcie
przerwa kawowa
Wszystko odbywa się za pomocą
glBlendEquation
z GL_FUNC_REVERSE_SUBTRACT
, GL_MIN
i GL_DIFFERENCE_NV
glBlendFunc
Oto mój kod ...
//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
Myślę, że to bardzo interesujące pytanie. Próbuję również znaleźć odpowiedź. Czy coś znalazłeś? – Alejandro
Tak naprawdę grałem z tym niedawno. Nie znalazłem dokładnego rozwiązania, ale ekran jest blisko. Obraz, który stworzyłem w Photoshopie, rzeczywiście wyglądał lepiej (jaśniejsze, bardziej nasycone kolory) niż wersja generowana przez kod, ale oczywiście, jeśli idziesz o dokładność, to go nie wycinam. Być może mógłbyś użyć kombinacji trybów mieszania Hue i Lightness z duplikatami warstw? –