Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   [Stage3D] Texture.uploadFromBitmapData обнуляет RGB канал у прозрачных пикселей (http://www.flasher.ru/forum/showthread.php?t=209505)

VisualDima 17.11.2014 00:02

Texture.uploadFromBitmapData обнуляет RGB канал у прозрачных пикселей
 
Хочу использовать полпрозрачную текстуру с альфаканалом.
Загружаю из png. Создаю текстур формата Context3DTextureFormat.BGRA. Вызываю Texture.uploadFromBitmapData.
Ставлю обычный альфа-блендинг:
Код AS3:

context3D.setBlendFactors(Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA);

И наблюдаю такую картину: RGB каналы всех прозрачных пикселей (у которых альфа равна 0) заменяются черным цветом (нулями тоесть).
В результате, если увеличить объекты, становятся видны черные края (это происходит из-за сглаживания)
http://i.imgur.com/W6KebjW.jpg
Свой BitmapData я проверил, там все нормально: во всех пикселях (даже в полностью прозрачных) белый цвет.
Должно быть вот-так: (изображение получено из обычного флеша)
http://i.imgur.com/RmDcAfg.jpg

Покопав дальше вообще обнаружил, что uploadFromBitmapData весьма своеобразно делает текстуры из полупрозрачных битмапов. Он какбы ренедерит их на черном фоне и записывает полученый результат в RGB канал (альфу при этом копирует правильно).
Т.е. если есть белое полупрозрачное облачко, то после uploadFromBitmapData получаем текстуру с СЕРЫМ полупрозрачным облаком. Дурдом! За такое руки отрывать надо, ИМХО.

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

Zebestov 17.11.2014 04:14

Ничего подобного Stage3D при загрузке текстуры из битмапы не делает, это же форменный произвол!
Ты в шейдере часом не умножаешь RGB на A?

VisualDima 17.11.2014 14:33

Чтобы еще раз все перепроверить, сделал отдельный проект где содержится только то, что нужно для теста.
Вот исходная картинка, как она выглядит в редакторе (фактически, редактировался только альфа-канал, цвет же везде белый 0xffffff):
http://i.imgur.com/csrkDLF.jpg

Шейдер максимально простой (только XYZ и UV)
Код AS3:

                        var vertexShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
                        vertexShaderAssembler.assemble ( Context3DProgramType.VERTEX,
                                "m44 op, va0, vc0\n" +
                                "mov v0, va0\n" +                        // XYZ -> v0
                                "mov v1, va1\n"                                // UV -> v1
                        );     
                        var fragmentShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
                        fragmentShaderAssembler.assemble ( Context3DProgramType.FRAGMENT, 
                                "tex ft0, v1, fs0 <2d,repeat,nomip,linear>\n" + 
                                "mov oc, ft0\n"                 
                        );
                        m_pShaderProgram = m_pContext3D.createProgram();
                        m_pShaderProgram.upload(vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);

Создание текстуры (mipmapping'а нет):
Код AS3:

                        m_pTexture = m_pContext3D.createTexture(m_pBitmap.width, m_pBitmap.height,
                                          Context3DTextureFormat.BGRA, false);
                        m_pTexture.uploadFromBitmapData(m_pBitmap.bitmapData, 0);

Вывожу два квада. Слева без блендинга:
Код AS3:

setBlendFactors(Context3DBlendFactor.ONE, Context3DBlendFactor.ZERO)

Справа с обычным блендингом:
Код AS3:

setBlendFactors(Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA)

Внизу просто addChaild'ом добавил копию загруженной битмапы, чтобы проверить, что она загрузилась правильно (собственно, при выводе с блендингом должна быть именно такая картинка).
Вот что получилось в результате:
http://i.imgur.com/HZ1Ahwi.jpg

Либо я чего-то не понимаю, либо да, произвол. Слов нет кроме матерных.
Могу выложить весь проект (сделан в FlashDevelop'е)

Zebestov 17.11.2014 15:16

Если не затруднит, запакуй в zip и выложи непосредственно тот PNG, который ты используешь.

Gerbert 17.11.2014 15:23

Я не понял до конца, но если у Вас белый круг в черном квадрате, который Вы пытаетесь скрыть,
то черная обводка это нормально. Если в фотошопе нарисовать белый круг на черном квадрате,
а потом увеличивать, то появятся серые пиксели, это так программа видит масштаб. И ничего с этим не поделать.

Zebestov 17.11.2014 15:27

Нет. Здесь речь идет скорее всего о premultipled alpha при экспорте изображения в PNG. Поэтому и прошу показать именно файл изображения.

Забегая вперед, если таки да, то вот решение.

VisualDima 17.11.2014 16:10

Вот ссылка на исходный png в архиве. https://cloud.mail.ru/public/b2c21a360e37/Bitmapa.zip
Вот на всякий случай весь проект: https://cloud.mail.ru/public/b3343d1...adTexCheck.zip

Zebestov, точно эффект именно как при premultipled alpha. Но такое впечатление, что происходит он именно в момент uploadFromBitmapData.
Ведь на нижнем изображении на картинке эффект не наблюдается, т.е. исходный bitmapData нормальный.

Добавлено через 14 минут
Кстати, Zebestov. Огромное спасибо за ссылку! Как запасной вариант подойдет (устраняет проблему). Но меня смущает лишняя операция деления в шейдере. Не привык я разбрасываться ресурсами системы. Продолжаю копать.
Никак не пойму, почему кто-то в адоб решил что он лучше знает, как должна выглядеть моя текстура. Просто бы скопировали цвветовые данные как я прошу - быстро и просто. Нет, самодеятельность какую-то придумали.

Партизан 17.11.2014 16:56

Вроде решает проблему....
Код AS3:

m_pContext3D.setBlendFactors(Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE);


Zebestov 17.11.2014 17:08

Цитата:

Сообщение от VisualDima (Сообщение 1175053)
Но меня смущает лишняя операция деления в шейдере. Не привык я разбрасываться ресурсами системы.

Совершенно с тобой согласен. И кстати, файл таки без premultipled… странно все это.

Добавлено через 2 минуты
Партизан, не решает, потому что в данном случае совершенно идентично — SOURCE_ALPHA же у нас ноль, значит ONE_MINUS_SOURCE_ALPHA и так ONE.

Добавлено через 25 минут
Итак, вот что вычитал уважаемый Волгоградец (автор сайта, который указан выше).

Цитата:

Все пиксели в объекте BitmapData сохраняются как предварительно умноженные значения цвета.

Все методы BitmapData принимают и возвращают неумноженные значения.
Таким образом любое изображение, которое вставляется во Flash (будь то элемент из библиотеки в IDE или изображение, вставленное с помощью тега embed), превращаясь в BitmapData сразу же подлежит предварительному умножению. Таки да. Произвол. Понятно, зачем это сделано, но нам пофиг!

Как видно, никакими getPixel32/setPixel32 ты это не исправишь. Остается лишь пробежаться по содержимому BitmapData как по ByteArray и переделить все RGB на A. И лишь после этого можно отгружать текстуру в GPU.

Однако наш коллега комментирует это так:

– можно переделить, но одно деление в шейдере — это ничто, наносекунды, даже меньше, даже на отстойных телефонах;


P.S.
И да, ты был прав, Flash таки мутит свои дела, оправдывая это вероломство какими-то нелепыми отмазками типа ускорения отрисовки сцены, пфф!

Партизан 17.11.2014 17:35

Вложений: 1
ну как-бы результат такой... Вложение 31264

Zebestov 17.11.2014 17:37

…и это странно

Партизан 17.11.2014 17:47

на самом деле не очень... В первом случае от единицы отнимается значение альфа-канала и потом умножается на альфа-канал. Т.е. если изначально альфа 0.5, то 0.5*0.5=0,25 на выходе.
Во втором случае там единица и соответственно 0.5*1 = 0.5 на выходе.

Zebestov 17.11.2014 17:54

Мне казалось, что вычитаться должна альфа источника, а она у нас ноль, так что в обоих случаях будет 0.5

Партизан 17.11.2014 18:01

Цитата:

Сообщение от Zebestov (Сообщение 1175066)
Мне казалось, что вычитаться должна альфа источника, а она у нас ноль, так что в обоих случаях будет 0.5

При альфа=0 ноль и получится. Как и видно на картинке автора. А на полупрозрачности поведение как я описал выше.

VisualDima 17.11.2014 18:19

Партизан, к сожалению, это уже будет другой тип блендинга (Add). Для белых объектов он действительно работает, но если вместо этого кружка взять что-нибудь цветастое, получится уже совсем другая картина (через объект будет просвечивать фон). А мне нужен имено обычный альфа-блендинг.

Волгоградец 17.11.2014 18:22

Ничего странного. Это работает только по-случайности. Будь бэкграундом другой цвет, все было бы иначе.
Например, конечный цвет вычисляется так:
Код AS3:

c = s * sfactor + d * dfactor // обычная блендинг-формула

Возьмем цвет [1, 1, 1, 0.5]. Во флэше он будет храниться как [0.5, 0.5, 0.5, 0.5].
В случае с
Код AS3:

context3D.setBlendFactors(Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA);

SOURCE_ALPHA = 0.5
ONE_MINUS_SOURCE_ALPHA = 1 - 0.5 = 0.5
Бекграунд возьмем [0, 0, 1]
Итого получаем
Код AS3:

[0.5, 0.5, 0.5] * 0.5 + [0, 0, 1] * 0.5 = [0.25, 0.25, 0.25] + [0, 0, 0.5] = [0.25, 0.25, 0.75].

Видно, что цвет стал намного темнее исходного ([1, 1, 1, 0.5]) и темнее бэкграунда ([0, 0, 1]). Отсюда и темная обводка.
Если мы поделим в шейдере цвет на альфу, то получим
Код AS3:

[1, 1, 1] * 0.5 + [0, 0, 1] * 0.5 = [0.5, 0.5, 0.5] + [0, 0, 0.5] = [0.5, 0.5, 1].

Т.е. цвет не потеряли.

Если же не делить в шейдере и взять
Код AS3:

m_pContext3D.setBlendFactors(Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE);

то получим
Код AS3:

[0.5, 0.5, 0.5] * 0.5 + [0, 0, 1] * 1 = [0.25, 0.25, 0.25] + [0, 0, 1] = [0.25, 0.25, 1.25],

что округлится до [0.25, 0.25, 1]. На картинке Партизана видно, что синего цвета больше, чем на Bitmap.

Но если взять другой цвет ([0, 0, 1, 0.5]) и другой бэкграунд ([1, 1, 1]), то получим
Код AS3:

[0, 0, 0.5] * 0.5 + [1, 1, 1] * 1 = [0, 0, 0.25] + [1, 1, 1] = [1, 1, 1.25],

что округлится до [1, 1, 1], т.е. мы потеряем цвет.

Upd: пока писал, уже ответили )

Партизан 17.11.2014 18:24

Цитата:

Сообщение от VisualDima (Сообщение 1175070)
А мне нужен имено обычный альфа-блендинг.

Не очень понимаю сейчас...

Zebestov 17.11.2014 18:47

Цитата:

Сообщение от Партизан (Сообщение 1175068)
При альфа=0 ноль и получится. Как и видно на картинке автора. А на полупрозрачности поведение как я описал выше.

Основа 0.0, накладываем 0.5, (1.0 - 0.0) * 0.5 мне почему-то видится ровно тем же 0.5, который получается при ONE, который, как верно заметил VisualDima, в общем случае дает уже совсем другой блендинг.

Добавлено через 1 минуту
Ох мы навалились =) ну тема действительно такая, что может немножечко зарябить в глазах.

VisualDima 17.11.2014 18:50

Партизан. Я на цветную картинку заменил исходник, чтобы продемонстрировать:
http://i.imgur.com/aHTgTcq.jpg
Правая картинка получилась с использованием
Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE
Как видишь, цвет фона влияет складывается со всем изображением. (если бы там было что-то нарисовано, то оно бы прямо просвечивало)

А я хочу добиться, чтобы получилась как нижняя картинка.

Zebestov 17.11.2014 18:51

Цитата:

Сообщение от Партизан (Сообщение 1175072)
Не очень понимаю сейчас…

Если кратко, то твой "хак" работает только с прозрачным фоном.

Партизан 17.11.2014 19:47

Да... чет я запутался... Поменял на цвет, вот с такой штукой различий не нашел (Context3DBlendFactor.ONE, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA)

Партизан 17.11.2014 20:14

Вложений: 1
Только что-то по вертикали картинка отразилась, это нормально? :)Вложение 31266

VisualDima 17.11.2014 20:47

Партизан, для данного шейдера ты прав! :) (И насчет перевернутого изображения, да, так и должно быть, мой косяк, перепутал немного координаты)

Я использую более сложный шейдер, чем в демке. Текстура умножается на цвет вертекса либо на другую текстуру (с использованием альфы), и тут, увы, уже так просто не прокатит.
НО! Можно внести в шейдер дополнение - умножение финального цвета на альфу. К сожалению, это увеличит шейдер :(. Правда, качество будет наверно выше, чем в другом решении (с делением на альфу).

Добавлено через 3 минуты
Попробовал использовать ATF текстуры, а не png. Первые результаты вселяют надежду. На PC - сжатые текстуры перегоняются в GPU без всяких глупостей вроде premultiplied alpha. Но надо еще поэсперементировать со всеми форматами.

Добавлено через 51 час 14 минут
Итак, резюмирую. Было найдено три варианта решения:

1. Вариант от Волгоградца http://volgogradetzzz.blogspot.de/20...ied-alpha.html, ссылку на который предоставил Zebestov:
Добавить в шейдер деление на альфу.
Плюс:
+ Просто и элегантно.
Минусы:
- Лишняя операция в шейдере.
- По арифметическим причинам возможны небольшие отклонения в цветопередаче.
- При рендеринге без блендинга, но с альфа-отсечением, черный ободок всетаки может иногда проявляться.

2. Вариант на основе предложения Партизана:
Вместо Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA
Использовать Context3DBlendFactor.ONE, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA. И доработать шейдер в зависимости от дополнительных нужд (если, например, нужен еще цвет вершины, то добавить операцию умножения результирующего цвета на альфу вершины)
Плюсы и минусы примерно как в предыдущем варианте.

3. Использовать ATF-текстуры.
Я проверил разные варианты сжатые/несжатые, PC/Android. Все текстуры загружаются в GPU в исходном, неизмененном виде.
Плюс:
+ Всю наконец работает так, как и должно.
+ Можно не менять шейдеры
+ Автоматом доступна возможность использовать сжатые (на уровне GPU) текстуры (dxt1/5).
Минусы:
- Инструментарий для работы с ATF весьма скуден и не особо функционален.
- Если есть необходимость сочетать в рамках одного движка сжатые/несжатые, с альфой/без альфы текстуры, надо пердусматривать отдельные шейдеры для разных видов текстур.

Лично для себя выбрал 3-й вариант. Правда, из-за желания зарезервировать возможность сочетать в одном рендеренге сжатые и несжатые текстуры и текстуры с альфой/без альфы, количество шейдеров возросло с 4-х до 30-ти. Но это вроде не страшно.


Часовой пояс GMT +4, время: 21:05.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.