Войти
ПрограммированиеФорум2D графика и изометрия

Блендинг по альфа-маске в Direct3D

Страницы: 1 2 3 4 Следующая »
#0
21:47, 26 дек. 2013

Помогите пожалуйста разобраться с блендингом в Direct3D 9.
Игра 2D, все спрайты выводятся как квады, D3DX не используется.

Имеется фон, на который нужно накладывать полупрозрачный тайл, который при этом ещё и обрезается по альфа-каналу маски.

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

BlendingTask | Блендинг по альфа-маске в Direct3D

Я реализовал это с помощью пиксельного шейдера, в который передаю большую текстуру с бликом, сдвиг на текстуре блика и текстуру маски. Сопоставляю тексели маски и бликов и перемножаю альфу. Оно работает, но не батчится, т.к. для каждого тайла стеклянного пола (на картинке один тайл 256х256) приходится заряжать шейдер новыми параметрами. Да и вообще, до этого обходился без шейдеров, не хочется вводить новые требования к железу ради одного эффекта на стёклах.

Можно ли последовательно отрисовывая спрайт маски и спрайт с бликами добиться результата, который на картинке?


#1
21:53, 26 дек. 2013

Нашел решение для наложения непрозрачного спрайта по альфа-маске: http://stackoverflow.com/questions/5097145/opengl-mask-with-multiple-textures
У меня это получается так.

1. Вывожу фон так:

_d3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
_d3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

2. Накладываю маску так (она забрасывает свой альфа-канал и всё, сама не выводится):

_d3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
_d3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
_d3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
_d3dDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ZERO);
_d3dDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_SRCALPHA);

3. Вот, теперь альфа канал у Dest заряжен какой мне нужен и нужно только перемножить альфу у Dest и Source, но я не знаю как. Поэтому тупо использую заряженную маской альфу из фона:

_d3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, FALSE);
_d3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTALPHA);
_d3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVDESTALPHA);

В результате вывожу по маске, но теряю прозрачность спрайта бликов:
Fail | Блендинг по альфа-маске в Direct3D

#2
22:32, 26 дек. 2013

Задача вывести одну текстуру с альфой из другой текстуры. Остальное что ты понаписал к этому имеет какое-то отношение?

В нулевой стейдж грузишь маску (альфу).
В первый стейдж - стекло, с его альфой. Режим смешивания альфы MODULATE.

Всё, на выходе полупрозрачное стекло обрезанное по маске.

#3
23:16, 26 дек. 2013

Оу, спасибо! А я могу это без мультитекстурирования провернуть?

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

Вроде как msdn уверяет, что так можно: http://msdn.microsoft.com/en-us/library/windows/desktop/bb147218%… vs.85%29.aspx Попробую просто подряд накладывать стейдж 0 с D3DTSS_ALPHAOP - D3DTOP_MODULATE.

#4
0:09, 27 дек. 2013

Не, просто так не получается. Утонул в этих SRCDESCINVALPHAOP'ах...

#5
0:22, 27 дек. 2013

>мне понадобятся разные текстурные координаты для них
Ну, если существует матрица переводящая первые во вторые (а я не вижу причин ей не существовать), то можно установить её как трансформацию на первом стейдже для нулевого набора координат.

В два прохода вариант наверное через стенсил буфер.
Но это какой-то совсем крайний вариант. Мультитекстурингом тупо быстрее.

Можно конечно извратиться, сделать pre-multiplied цвет текстуре стекла и затем рендерить его вторым проходом установив SRC=DESTALPHA, DEST=INVSRCALPHA
Но чет я не уверен что тебе это настолько нужно.

#6
6:29, 27 дек. 2013

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

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

#7
10:40, 27 дек. 2013

Боюсь я недостаточно подробно описал свою задачу.  У меня тайловый ландшафт, дырки в полу, это такая мозаика, собираемая из набора в 15 тайлов.
Форма этой дырки может быть вот такой (желтые квадраты - тайлы):
WindowTiles | Блендинг по альфа-маске в Direct3D

То, что я описывал выше - круглая дырка, состоящая на самом деле из четырёх тайлов, с окошками в уголках. Мне нужно рендерить окно состоящее из различных фрагментов, каждый из которых имеет свою форму и свою маску точно размером с тайл.

Но это не всё - блики, это тоже тайл, но какого-то своего размера (на блике  не нужно супер качество, я его буду нещадно растягивать), который на каждый конкретный тайл стекла проецируется определённым своим фрагментом - блики всех тайлов стекла должны не иметь швов между собой и при этом вся эта конструкция должна перемещаться относительно стекла в зависимости от положения камера. Игрок сдвинул чуть-чуть экран - стёкла остались на месте, а блики на них переместились.

Итого, для отрисовки массива стёкол и бликов я бы хотел уместиться в два-три батча. Все тайлы стёкол лежат в одной текстуре, маски в другой такой же, блики - третья текстура. Если бы я мог рисовать это всё многопроходно, то я бы сделал так:

1. Первым батчем отрисовал все тайлы стёкол.
2. Вторым батчем модифицировал альфа канал невидимой отрисовывкой туда всех тайлов маски.
3. Третьим батчем наложил на все тайлы блики.

С третьим пунктом проблема, т.к. я не понимаю как накладывать спрайты так, чтобы их альфа канал перемножился с альфаканалом того, на что я их накладываю.
В пиксельном шейдере я смогу узнать альфа пикселя на который рисуется пиксель моей текстуры? Можно попробовать шейдер без параметров только на третьем этапе использовать.

#8
10:51, 27 дек. 2013

x
> > не понадобятся разные текстурные координаты для них
> Ну, если существует матрица переводящая первые во вторые (а я не вижу причин ей не существовать)
Эта матрица будет разной для разных тайлов, для каждого придётся делать отдельный батч.

> В два прохода вариант наверное через стенсил буфер.
> Но это какой-то совсем крайний вариант. Мультитекстурингом тупо быстрее.
Стенсил буфер даст мне полупрозрачность маски? Я с ним никогда не работал, но, насколько я понимаю, он на весь экран один, а если мне поверх стекла с бликами понадобится выводить другой объект со своими бликами, то это будет невозможно. А в игре такая ситуация очень вероятна - игровые объекты, которые находятся на стеклянной поверхности могут иметь свои собственные бликующие части.

x
> Можно конечно извратиться, сделать pre-multiplied цвет текстуре стекла и затем
> рендерить его вторым проходом установив SRC=DESTALPHA, DEST=INVSRCALPHA
> Но чет я не уверен что тебе это настолько нужно.

MrShoor
> Имхо самый оптимальный вариант: запечь блики в атлас с накладкой на фон.
Боюсь такие варианты с не подойдут, т.к. стекло (на моей картинке я это накладкой на фон назвал) должно быть самостоятельным полупрозрачным тайлом. Просто без блика. Дело в том, что если машина игрока слабая и нужно бороться за FPS или его видеократа не поддерживает шейдеры или мультитекстурирование, то я просто не вывожу блики, а просто рисую стеклянные тайлы ландшафта.

Вот видеопруф игрухи, дырки в полу, это оно самое - там будет бликующее стекло. Весь ландшафт собирается из тайликов.

#9
12:15, 27 дек. 2013

>С третьим пунктом проблема, т.к. я не понимаю как накладывать спрайты так, чтобы их альфа канал перемножился с альфаканалом того, на что я их накладываю.

Именно это я тебе уже и написал. Сделать pre-multiplied цвет текстуре бликов и затем рендерить его установив SRC=DESTALPHA, DEST=INVSRCALPHA
Т.е. формула блендинга получится Target.RGB * (1.0 - Texture.A)  + Texture.RGB * Target.A,
где в Target.A уже отрендерили маску с кругом,
а Texture.RGB предварительно умножили на Texture.A

Блики к "накладке на фон" здесь не имеют никакого отношения.

#10
12:26, 27 дек. 2013

Прости, я не понимаю. Можешь чуть подробнее про приём с  pre-multiplied цветом?

Судя по этой ссылке http://www.spherevfx.com/understanding-premultiplied-images/  pre-multiplied, это изображение, в котором полупрозрачные области специально обрабатывают (накладывают на чёрный цвет).
Но в спрайте бликов я не могу заранее знать где ляжет маска, т.к. он будет отрисовываться разными своими частями разными масками. Я не догоняю сути?

#11
12:39, 27 дек. 2013

Внимательно посмотри на формулу. Блики ты предварительно умножаешь на СОБСТВЕННУЮ альфу.
Круг же берется из альфы таргета уже в рантайме.

>pre-multiplied, это изображение, в котором полупрозрачные области специально обрабатывают (накладывают на чёрный цвет).
Формально это можно назвать и "накладыванием на черный цвет", но это бредовая формулировка.
Pre-multiplied это, внезапно, когда каналы цвета заранее умножаются на альфу: RGBA -> (R*A, G*A, B*A, A)

#12
12:44, 27 дек. 2013

x
> Pre-multiplied это, внезапно, когда каналы цвета заранее умножаются на альфу:
> RGBA -> (R*A, G*A, B*A, A)
Пока не понял, как это работает, но, вроде, догоняю что нужно сделать.
Вечером попробую реализовать эту схему. Спасибо за совет!

#13
12:48, 27 дек. 2013

Kozinaka
> Боюсь такие варианты с не подойдут, т.к. стекло (на моей картинке я это
> накладкой на фон назвал) должно быть самостоятельным полупрозрачным тайлом.
> Просто без блика. Дело в том, что если машина игрока слабая и нужно бороться за
> FPS или его видеократа не поддерживает шейдеры или мультитекстурирование, то я
> просто не вывожу блики, а просто рисую стеклянные тайлы ландшафта.
> Вот видеопруф игрухи, дырки в полу, это оно самое - там будет бликующее стекло.
> Весь ландшафт собирается из тайликов.
>
Ну глядя на видео - похоже тебе нужно стекло строго внуртри дырок? Без нахлеста же?

Классный пруф. Мне игруха нравится. Когда релиз?
#14
12:57, 27 дек. 2013

>как это работает
Классический альфаблендинг это

DEST.RGB * (1 - SRC.A) + SRC.RGB * SRC.A

Правое слагаемое можно посчитать заранее раз и навсегда, тот самый pre-multiply (если используешь DXT то для этого даже есть редко используемые DXT2 и 4)
Получается:

DEST.RGB * (1 - SRC.A) + SRC.R'G'B'

Мы освободили правую часть от операции, но можем её снова использовать, на этот раз уже для дополнительного маскирования по альфе из таргета:

DEST.RGB * (1 - SRC.A) + SRC.R'G'B' * DEST.A

Собственно все, выбираем соответсвующие формуле стейты блендинга:

(1 - SRC.A) = _INVSRCALPHA
DEST.A = _DESTALPHA

Это и есть твой третий пункт.

Страницы: 1 2 3 4 Следующая »
ПрограммированиеФорум2D графика и изометрия

Тема в архиве.