альфа канал
От: Шебеко Евгений  
Дата: 02.06.10 10:29
Оценка:
Есть буфер с альфа каналом, ну и цвета с альфа составляющей.
Не могу их правильно смешать Уже весь мозг себе сломал.

Основная идея в том, что если буфер залит непрозрачным цветом, то цвета должны смешиваться с фоном,
а если фон прозрачный, то не должны.

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

Пока что велосипед выглядит так:

static AGG_INLINE void blend_pix(value_type* p, 
                                    unsigned cr, unsigned cg, unsigned cb,
                                    unsigned alpha, 
                                    unsigned cover=0)
{
    if(alpha==0)return;
    calc_type a=p[Order::A];
    
    if(a==0)
    {
        p[Order::R]=(value_type)cr;
        p[Order::G]=(value_type)cg;
        p[Order::B]=(value_type)cb;
        p[Order::A]=(value_type)alpha;
        return;
    }

    calc_type r = p[Order::R];
    calc_type g = p[Order::G];
    calc_type b = p[Order::B];
    p[Order::R] = (value_type)((cr*alpha*base_mask+r*a*(base_mask-alpha))/base_mask/base_mask);
    p[Order::G] = (value_type)((cg*alpha*base_mask+g*a*(base_mask-alpha))/base_mask/base_mask);
    p[Order::B] = (value_type)((cb*alpha*base_mask+b*a*(base_mask-alpha))/base_mask/base_mask);
    p[Order::A] = (value_type)((alpha + a) - ((alpha * a + base_mask) >> base_shift));
}


Использую agg. Но это в принципе не важно. Давайте будем считать
что цвета у нас от 0 до 1. И альфа канал тоже от 0 до 1.
r,g,b,a — цвет фона.
cr,cg,cb,ca — накладываемый сверху цвет.

Код у нас получается такой:
if(ca==0)return;
if(a==0)
{
    r=cr;
    g=cg;
    b=cb;
    a=ca;
    return;
}

r=cr*ca+r*a*(1.0-ca);
g=cg*ca+g*a*(1.0-ca);
b=cb*ca+b*a*(1.0-ca);
a=ca+a-ca*a;


Первый if: Если новый цвет целиком прозрачный, то он не оказывает никакого влияния на буфер, даже если тот тоже целиком прозрачный.
Второй if — это костыль.
Посмотрим на выражение: r=cr*ca+r*a*(1.0-ca);
Допустим буфер у нас целиком прозрачный, тогда правая часть суммы r*a*(1.0-ca)=0.
Допустим мы накладываем белый цвет, с прозрачностью 0.5. Тогда левая часть суммы, превращает наш цвет из белого в серый.
Хотя по идее он должен был остаться белым с альфа каналом 0.5.

Эту формулу взял с википедии. Мне она показалась наиболее разумной в моём случае.
Формула смешивания альфа канал хз почему такая. Взял с той же википедии и в том же agg часто используется именно в таком виде.
Что она значит, так и не понял Для меня это "суммируем значение прозрачности, главное чтобы она не превысила максимальное значение"

В чём проблема с моим кодом?

1. Хотелось бы убрать костыли и не изобретать велосипед.
2. Хотелось бы добиться непрерывности функций.
Ещё раз приведу формулу: r=cr*ca+r*a*(1.0-ca);
Допустим буфер у нас "почти прозрачный" a — стремится к нулю, тогда правая часть суммы r*a*(1.0-ca) тоже стремиться к нулю.
И мы получаем туже проблему, что белый полупрозрачный цвет превращается в грязно-серый.
3. Хотелось бы понять как именно складывать альфа канал.
Re: альфа канал
От: denisko http://sdeniskos.blogspot.com/
Дата: 02.06.10 11:44
Оценка:
Здравствуйте, Шебеко Евгений, Вы писали:

ШЕ>Есть буфер с альфа каналом, ну и цвета с альфа составляющей.

ШЕ>Не могу их правильно смешать Уже весь мозг себе сломал.

ШЕ>Основная идея в том, что если буфер залит непрозрачным цветом, то цвета должны смешиваться с фоном,

ШЕ>а если фон прозрачный, то не должны.

ШЕ>Первый используется для отображения на экране, второй для генерации прозрачных картинок.


ШЕ>Пока что велосипед выглядит так:


ШЕ>
ШЕ>static AGG_INLINE void blend_pix(value_type* p, 
ШЕ>                                    unsigned cr, unsigned cg, unsigned cb,
ШЕ>                                    unsigned alpha, 
ШЕ>                                    unsigned cover=0)
ШЕ>{
ШЕ>    if(alpha==0)return;
ШЕ>    calc_type a=p[Order::A];
    
ШЕ>    if(a==0)
ШЕ>    {
ШЕ>        p[Order::R]=(value_type)cr;
ШЕ>        p[Order::G]=(value_type)cg;
ШЕ>        p[Order::B]=(value_type)cb;
ШЕ>        p[Order::A]=(value_type)alpha;
ШЕ>        return;
ШЕ>    }

ШЕ>    calc_type r = p[Order::R];
ШЕ>    calc_type g = p[Order::G];
ШЕ>    calc_type b = p[Order::B];
ШЕ>    p[Order::R] = (value_type)((cr*alpha*base_mask+r*a*(base_mask-alpha))/base_mask/base_mask);
ШЕ>    p[Order::G] = (value_type)((cg*alpha*base_mask+g*a*(base_mask-alpha))/base_mask/base_mask);
ШЕ>    p[Order::B] = (value_type)((cb*alpha*base_mask+b*a*(base_mask-alpha))/base_mask/base_mask);
ШЕ>    p[Order::A] = (value_type)((alpha + a) - ((alpha * a + base_mask) >> base_shift));
ШЕ>}
ШЕ>


ШЕ>Использую agg. Но это в принципе не важно. Давайте будем считать

ШЕ>что цвета у нас от 0 до 1. И альфа канал тоже от 0 до 1.
ШЕ>r,g,b,a — цвет фона.
ШЕ>cr,cg,cb,ca — накладываемый сверху цвет.

ШЕ>Код у нас получается такой:

ШЕ>
ШЕ>if(ca==0)return;
ШЕ>if(a==0)
ШЕ>{
ШЕ>    r=cr;
ШЕ>    g=cg;
ШЕ>    b=cb;
ШЕ>    a=ca;
ШЕ>    return;
ШЕ>}

ШЕ>r=cr*ca+r*a*(1.0-ca);
ШЕ>g=cg*ca+g*a*(1.0-ca);
ШЕ>b=cb*ca+b*a*(1.0-ca);
ШЕ>a=ca+a-ca*a;
ШЕ>


ШЕ>Первый if: Если новый цвет целиком прозрачный, то он не оказывает никакого влияния на буфер, даже если тот тоже целиком прозрачный.

ШЕ>Второй if — это костыль.
ШЕ>Посмотрим на выражение: r=cr*ca+r*a*(1.0-ca);
ШЕ>Допустим буфер у нас целиком прозрачный, тогда правая часть суммы r*a*(1.0-ca)=0.
ШЕ>Допустим мы накладываем белый цвет, с прозрачностью 0.5. Тогда левая часть суммы, превращает наш цвет из белого в серый.
ШЕ>Хотя по идее он должен был остаться белым с альфа каналом 0.5.

ШЕ>Эту формулу взял с википедии. Мне она показалась наиболее разумной в моём случае.

ШЕ>Формула смешивания альфа канал хз почему такая. Взял с той же википедии и в том же agg часто используется именно в таком виде.
ШЕ>Что она значит, так и не понял Для меня это "суммируем значение прозрачности, главное чтобы она не превысила максимальное значение"

ШЕ>В чём проблема с моим кодом?


ШЕ>1. Хотелось бы убрать костыли и не изобретать велосипед.

ШЕ>2. Хотелось бы добиться непрерывности функций.
ШЕ>Ещё раз приведу формулу: r=cr*ca+r*a*(1.0-ca);
ШЕ>Допустим буфер у нас "почти прозрачный" a — стремится к нулю, тогда правая часть суммы r*a*(1.0-ca) тоже стремиться к нулю.
ШЕ>И мы получаем туже проблему, что белый полупрозрачный цвет превращается в грязно-серый.
ШЕ>3. Хотелось бы понять как именно складывать альфа канал.


1. Зачем ты два раза на base_mask делишь каждую составляющую?
2. имхо надо рассматривать относительные прозрачности, т.е если даные {r1,g1,b1,a1},{r2,g2,b2,a2}, то при а2>a1
r = (r1*a1 + r2(a2-a1))/a2 , если a1 < a2
r = (r2*a2 + r1*(a1-a2))/a2. Тогда таких артефактов с грязно серым можно избежать.
<Подпись удалена модератором>
Re: альфа канал
От: Кодт Россия  
Дата: 02.06.10 11:58
Оценка: 96 (2)
Здравствуйте, Шебеко Евгений, Вы писали:

<>

Представим себе, что у нас трёхслойная картинка: полностью непрозрачный задний план z.{r,g,b} и два полупрозрачных слоя, которые мы накладываем поверх — сперва первый y.{r,g,b,a}, затем второй x.{r,g,b,a}

Рассмотрим один цветовой канал; назовём его, для краткости z, y, x, а альфа-каналы, соответственно, ya, xa

Посмотрим, что получается.
Накладываем первый слой
z' = z·(1-ya) + y·ya

Накладываем второй слой
z'' = (z·(1-ya) + y·ya)·(1-xa) + x·xa
z'' = z·(1-ya)·(1-xa) + y·ya·(1-xa) + x·xa

Попробуем найти эквивалент (t,ta) этих двух слоёв
z'' = z·(1-ta) + t·ta

1-ta = (1-ya)·(1-xa)
t·ta = y·ya·(1-xa) + x·xa

Откуда, элементарно, получаем
ta = 1-(1-ya)·(1-xa) = xa+ya-xa·ya
t = (y·ya·(1-xa) + x·xa)/ta

Если xa=ya=0, то t=0/0 — сингулярность, из-за полной прозрачности нам побарабану, какой там цвет.
Если ya=0, то ta=xa, t=x
Если xa=0, то ta=ya, t=y
Вроде бы, всё логично получается.

Если x=y=v, то t=(v·ya·(1-xa)+v·xa)/ta = v·(xa+ya-xa·ya)/ta = v·ta/ta = v
Вуаля! Цвет сохранился!
Перекуём баги на фичи!
Re[2]: альфа канал
От: Шебеко Евгений  
Дата: 02.06.10 13:35
Оценка:
Блин, супер!
Всё просто и доходчиво.
Проверил — работает!
С антиалиасингом и без.
Самое прикольное, что фон у этих картинок зелёный-прозрачный.
Это видно, если открыть файлы в Paint.
А тут нет даже намёка на зелёный. Вообщем cool


 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.