Russian Qt Forum
Ноябрь 23, 2024, 15:27 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Объединение двух QColor  (Прочитано 8710 раз)
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« : Июль 25, 2011, 15:07 »

Есть задача: надо наложить один QColor с прозрачностью поверх другого QColor, как будто происходило рисование точки одним цветом и  поверх рисование точки другим цветом. Не нашел в доке функций/методов для этого.  (

Сейчас вижу только один способ - самостоятельно просчитать цвет результирующей точки с учетом прозрачности. Но хотелось бы не изобретать лисапедов.
Записан

Гугль в помощь
Ground
Гость
« Ответ #1 : Июль 25, 2011, 15:13 »

Немного не понял вопроса, нужно смешать цвет до рисования?
Можно посмотреть QPainter::setCompositionMode
« Последнее редактирование: Июль 25, 2011, 15:17 от Ground » Записан
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #2 : Июль 25, 2011, 15:20 »

Да. Именно смешать два цвета, получить результирующий, при рисовании которым, результат должен совпадать с результатом последовательного рисования двумя цветами.
Записан

Гугль в помощь
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Июль 26, 2011, 10:29 »

Да. Именно смешать два цвета, получить результирующий, при рисовании которым, результат должен совпадать с результатом последовательного рисования двумя цветами.
Это неоднозначно, напр

- просто интерполировать 2 цвета с учетом их alpha (порядок слагаемых не имеет значения)
- второй цвет виден "сквозь" первый (здесь уже результат разный в зависимости от того кто первый)
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #4 : Июль 26, 2011, 12:32 »

По-моему это уже где-то обсуждалось)

То что результат будет зависеть от порядка наложения цветов, это понятно.
Предлагаю такую реализацию:

Код
C++ (Qt)
QColor mix2clr(const QColor &clr1, const QColor &clr2) {
   qreal r = clr1.redF() + (clr2.redF() - clr1.redF()) * clr2.alphaF();
   qreal g = clr1.greenF() + (clr2.greenF() - clr1.greenF()) * clr2.alphaF();
   qreal b = clr1.blueF() + (clr2.blueF() - clr1.blueF()) * clr2.alphaF();
   return QColor(r, g, b, qMax(clr1.alphaF(), clr2.alphaF()));
}
 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
navrocky
Гипер активный житель
*****
Offline Offline

Сообщений: 817


Погроммист


Просмотр профиля
« Ответ #5 : Июль 26, 2011, 12:50 »

У меня получилось так:

Код
C++ (Qt)
inline qreal __blend(qreal s, qreal d, qreal a)
{
   return s + (d - s) * a;
}
 
QColor color_blend(const QColor& c1, const QColor& c2)
{
   QColor res;
   res.setRedF(__blend(c1.redF(), c2.redF(), c2.alphaF()));
   res.setGreenF(__blend(c1.greenF(), c2.greenF(), c2.alphaF()));
   res.setBlueF(__blend(c1.blueF(), c2.blueF(), c2.alphaF()));
   res.setAlphaF((1 - c1.alphaF()) * c2.alphaF() + c1.alphaF());
   return res;
}

Основное отличие от предыдущего поста получилось в просчете альфа-канала.
Записан

Гугль в помощь
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Июль 26, 2011, 13:25 »

То что результат будет зависеть от порядка наложения цветов, это понятно.
Лучше если человек скажет что ему нужно

Код
C++ (Qt)
   ...
   return QColor(r, g, b, qMax(clr1.alphaF(), clr2.alphaF()))
 
Если один цвет виден сквозь другой, прозрачность падает по зкспоненте
Код
C++ (Qt)
QColor CombineTrans( const QColor & fore, const QColor & back )
{
float foreA = fore.alphaF();
float backA = back.alphaF();
if (foreA + backA <= 0.0f) return QColor(0, 0, 0, 0); // full transparent
 
float newA = backA * (1.0f - foreA);  // added alpha
float sumA = foreA + newA;             // combined alpha
 
// Red component
float foreR = fore.redF();
float backR = back.redF();
float sumR = (foreR * foreA + backR * newA) / sumA;
 
// Green & Blue components
 ...
 
QColor result;
result.setRgbF(sumR, sumG, sumB, sumA);
return result;
}
 
Не пойму откуда такая велосипедо-боязнь?  Улыбающийся
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #7 : Июль 26, 2011, 15:54 »

Цитировать
Не пойму откуда такая велосипедо-боязнь?
Да какая там велосипедобоязнь)) У меня таблетки от велосипедофобии)

Вот непонятно, зачем так накручивать:
Код
C++ (Qt)
// Red component
float foreR = fore.redF();
float backR = back.redF();
float sumR = (foreR * foreA + backR * newA) / sumA;
 

И что Вы всё float, да float? qreal - это ж double.

А по-поводу альфа-канала, согласен с тем, что здесь больше зависит от постановки задачи. В зависимости от желаемого поведения, короче)
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Июль 27, 2011, 11:00 »

Да какая там велосипедобоязнь)) У меня таблетки от велосипедофобии)
У Вас с велосипедами все отлично, вот только проверять нужно тщательнее

Код
C++ (Qt)
   qreal r = clr1.redF() + (clr2.redF() - clr1.redF()) * clr2.alphaF();
 
Пусть clr1.alpha = 1.0f. Тогда выходной цвет должен быть = clr1 (позади полностью непрозрачного цвета ничего нового мы не увидим). А у Вас?

Вот непонятно, зачем так накручивать:
Ну вообще-то это называется "взвешивать"  Улыбающийся

Edit: кстати о птичках
Код
C++ (Qt)
inline qreal __blend(qreal s, qreal d, qreal a)
{
   return s + (d - s) * a;
}
 
Этот весьма популярный inline (а иногда и макрос) часто называют lerp
« Последнее редактирование: Июль 27, 2011, 11:06 от Igors » Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #9 : Июль 27, 2011, 16:23 »

Цитировать
Пусть clr1.alpha = 1.0f. Тогда выходной цвет должен быть = clr1 (позади полностью непрозрачного цвета ничего нового мы не увидим). А у Вас?
Нет, clr1 - это цвет который кладут первым. Результирующий цвет зависит как от прозрачности этого первого clr1.alphaF() так и от прозрачности второго цвета clr2.
Если у обоих цветов альфа = 1, то результирующий цвет будет соответствовать clr2, поскольку он кладётся последним и полностью закрашивает первый clr1.
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Июль 27, 2011, 17:06 »

Нет, clr1 - это цвет который кладут первым. Результирующий цвет зависит как от прозрачности этого первого clr1.alphaF() так и от прозрачности второго цвета clr2.
Если у обоих цветов альфа = 1, то результирующий цвет будет соответствовать clr2, поскольку он кладётся последним и полностью закрашивает первый clr1.
Если бы Вы назвали переменные как-то разумно (напр front, back) - ничего бы "дешифрировать" и пояснять не пришлось Улыбающийся Непонятно причем тут "у обоих цветов" - если у переднего цвета полная alpha = 1.0f то какой цвет и alpha у "заднего" - до лампочки, сквозь совершенно непрозрачный передний все равно ничего не увидеть.

Код
C++ (Qt)
   qreal r = clr1.redF() + (clr2.redF() - clr1.redF()) * clr2.alphaF();
 
Это не пляшет потому что clr1.alpha никак не учтена. Ладно, подставим цифирки

// clr1 задний. 1% красного
clr1.red = 1.0
clr1.alpha = 0.01

// clr2 передний, красный отсутствует
clr2.red = 0.0;
clr2.alpha = 0.5;

r = 1.0 + (0.0 - 1.0) * 0.5 = 0.5
В итоге видимая красная компонента = 0.5 * 0.5 = 0.25  (25%). Как так если в обоих исходных красного было 1%? Опять ребенок накакал больше собственного веса   Улыбающийся
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #11 : Июль 27, 2011, 20:07 »

Цитировать
В итоге видимая красная компонента = 0.5 * 0.5 = 0.25  (25%). Как так если в обоих исходных красного было 1%? Опять ребенок накакал больше собственного веса   Улыбающийся
Да, верно, здесь явный косяк)

А как Вы себе представляете процесс смешения цветов? В смысле, какой аналогией с реальностью руководствуетесь в этом методе весов?

Вот я сейчас прикинул такой вариант.
Итоговый цвет это результат следующего процесса.
Есть источник некоторых объектов (типа фотонов) которые характеризуются тремя значениями (r, g, b) Источник приготавлевает их в состоянии (r=1, g=1, b=1).

Есть приёмник, который их принимает и может распознать значение каждой компоненты.
В приёмник они попадают в результате отражения от "плёнки", причём плёнка обладает свойствами:
1) процентом отражённых "фотонов" (альфа-канал)
2) новыми компонентами r, g, b. (при условии что падающие ф. были в состоянии (1, 1, 1)).
3) прошедшие сквозь плёнку ф. не подвергаются изменению компонент.

Теперь наш случай с наложением двух цветов.
Пусть первый цвет - back, а накладываемый на него - front.
Тогда, в рамках рассматриваемой модели итоговый цвет есть результат следующего процесса:

Приёмник испускает N "фотонов" в состоянии (1, 1, 1) которые вначале падают на front - плёнку. Число отразившихся от неё ф.
R1 = N*front.alphaF()
которые попадают в приёмник и имеют компоненты соответствующего front - цвета.
Число прошедших ф.
D1 = N*(1 - front.alphaF())
которые имеют по-прежнему компоненты (1, 1, 1) и попадают на back - пластинку.
Число отразившихся от неё составит:
R2 = D1 * back.alphaF()
после чего они попадают в приёмник.

И так, в приёмнике у нас имеется R1 "фотонов" с компонентами front и R2 "фотонов" с компонентами back. Общие число испущенных ф. N.
1) Считаем итоговый альфа-канал:
alpha = (R1 + R2)/N =  front.alphaF() * (1 - back.alphaF()) + back.alphaF()

2) Считаем итоговый цвет:
R = a1 * front.redF() + a2 * back.redF();
G = ...
B = ...
где
a1 + a2 = 1
a1/a2 = R1/R2
т.е.
a1 = front.alphaF() / (front.alphaF() + back.alphaF() * (1 - front.alphaF()))
a2 = back.alphaF() * (1 - front.alphaF()) / (front.alphaF() + back.alphaF() * (1 - front.alphaF()))

Вобщем это всего лишь один из вариантов, просто как аналогия некого процесса, который можно себе представить.
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Июль 27, 2011, 21:19 »

a1 = front.alphaF() / (front.alphaF() + back.alphaF() * (1 - front.alphaF()))
a2 = back.alphaF() * (1 - front.alphaF()) / (front.alphaF() + back.alphaF() * (1 - front.alphaF()))
Теперь все верно, но записывать надо аккуратно и не лениться создавать локальные переменные.

Если говорить о фотонах то лучше в терминах "фотонной карты". Да, действительно, изначально испускается N фотонов. Проходя через материал, цвет фотонов не изменяется, просто их разное число проходит. Если материал полностью прозрачен (alpha = 0) то все фотоны пройдут и наоборот ни один при (alpha = 1). То есть с альфой все прекрасно сходится, и мы быстро приходим к тому же численному выражению экспоненты. А вот с RGB такого нет. Если материал напр. зеленый - мы не можем сказать что, мол, он отражает зеленую компоненту а остальные пропускает - тогда бы зеленая бутылка бросала фиолетовую тень, но в жизни этого нет. Но мы всегда можем взвеситься по альфе - и в принятой модели это 100% корректно.

А вообще хотя это самая популярная модель прозрачности "subtractive" (вычитающая) - не надо считать что это "корректно и единственно правильно" - это всего лишь "удобно". Материал может напр поглощать какие-то цветовые компоненты (и совсем не в терминах RGB), "отражение" - то просто так сказано, их там вида 3 только популярных и.т.д. и.т.п
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #13 : Июль 27, 2011, 21:41 »

Цитировать
Если говорить о фотонах то лучше в терминах "фотонной карты". Да, действительно, изначально испускается N фотонов. Проходя через материал, цвет фотонов не изменяется, просто их разное число проходит. Если материал полностью прозрачен (alpha = 0) то все фотоны пройдут и наоборот ни один при (alpha = 1). То есть с альфой все прекрасно сходится, и мы быстро приходим к тому же численному выражению экспоненты. А вот с RGB такого нет. Если материал напр. зеленый - мы не можем сказать что, мол, он отражает зеленую компоненту а остальные пропускает - тогда бы зеленая бутылка бросала фиолетовую тень, но в жизни этого нет. Но мы всегда можем взвеситься по альфе - и в принятой модели это 100% корректно.
На все вопросы, касательно света, ответит КЭД  Улыбающийся
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #14 : Июль 28, 2011, 09:45 »

На все вопросы, касательно света, ответит КЭД  Улыбающийся
Со своим провинциальным политехом полагаю что это "квантовая электродинамика" (поправьте если не так). Действительно, уравнение Максвелла первое что тыкается человеку в морду почти в любой статье. Вот только толку от него как от козла молока Улыбающийся Есть конечно фанаты "смотрите какой прекрасный результат 640x480 и всего-навсего после 30 часов рендера!". Но в реальной жизни упрощенные CG модели рулят. 
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.364 секунд. Запросов: 23.