Мое приложение часто меняет один из этих каналов, не меняя два других, и это ускорило бы мой рендеринг, если бы я мог объединять существующие изображения на графическом процессоре вместо рендеринга совершенно нового изображения каждый раз, когда что-то меняется. [OP, @ZevEisenberg]
Что касается быстродействия и графического процессора, я бы просто добавил функцию преобразования во фрагментный шейдер (например,). Это будет читать 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. [Bounty, @phisch]
Что касается фотошопа ... Я не на деньгах. Итак, в gimp есть Colours -> Components -> Compose/Decompose
, который делает это за вас. Я был бы немного удивлен, если бы этого не было в фотошопе, но это тоже не так. Возможно, есть скрипты / плагины для фотошопа, которые могли бы это сделать, если нет? Но вы конкретно сказали смешивание. Вашему вопросу можно уделить больше внимания на 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)
найти дробную часть
- with subtractive blending, subtract 0.5 (this is for a
floor()
as I'm assuming GL rounds colours when converting to 8 bits. if not, skip this)
- уменьшить на 1/255. это можно сделать с помощью обычного альфа-смешивания, но вместо этого я масштабировал цветную текстуру.
- пройти через текстуру без плавающей запятой, чтобы округлить до ближайшего 1/255
масштабировать обратно на 255 (обратно в текстуру с плавающей запятой)
теперь у нас есть целочисленный компонент. вычтите это из того, с чего мы начали
масштабировать на 6
- с субтрактивным смешиванием дубль 3
взять абсолютное значение
Я собираюсь просто использовать для этого GL_DIFFERENCE_NV
, но без него на следующем этапе можно было бы использовать два отдельных зажима. поскольку негативы все равно будут зажаты, что-то вроде clamp(p-K.xxx,0,1) + clamp(-p-K.xxx,0,1)
.
вычесть 1
ну вот и оттенок готов
можно было бы зажать, пропустив текстуру без плавающей запятой, но просто собираюсь использовать GL_MIN
теперь я могу использовать альфа-смешение для mix()
, но насыщенность загружается как черно-белое изображение без альфа-канала. так как он смешивает белый цвет, делать это вручную на самом деле проще ...
масштабировать по насыщенности
- добавить 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
person
jozxyqk
schedule
06.03.2015