10 февраля 2010 в 20:46
Уроки Direct3D - 2.5. Использование прозрачности
Чтобы приступить к изучению четвертой компоненты цвета A (альфа-компоненты), приготовим, как водится, опытный проект. Предыдущий проект преобразован для использования одной пары текстурных координат и цвета в вертексе.
Обратите внимание на процедуру Setting, она вызывается из Sub Main непосредственно перед главным циклом. В этой процедуре мы будем тестировать различные варианты использования альфа-компоненты, пока она пустая. Альфа-компонента используется для имитации эффекта полупрозрачности предметов, так же, как и компоненты R, G и B, альфа – это байт в представлении цвета RGBA, либо значение от 0 до 1 в представлении DirectX. Наиболее распространенная формула для имитации эффекта полупрозрачности такая:
R = Arg1 ´ Alpha + Arg2 ´ (1 – Alpha)
Здесь R – это результирующий цвет, Arg1 и Arg2 – смешиваемые цвета. Альфа-компонента может применяться как «внутри» стадии текстурирования для работы с ее аргументами, так и к общему результату работы текстурирования для смешивания его с ранее нарисованным изображением. Рассмотрим сначала более простой первый вариант. Текстура к этому проекту сохранена в формате TGA и имеет в составе альфа-компоненту, также альфа-компоненту содержит и цвет вертексов, обратите внимание – все нижние вертексы в цилиндре имеют цвет &HFF0000FF, то есть:
Alpha = &HFF
R = &H0
G = &H0
B = &HFF
А верхние вертексы в цилиндре имеют цвет &HFF, то есть Alpha в них равна 0. Так же, как и другие компоненты цвета вертекса, альфа-компонента линейно интерполируется между вертексами. Впишите в процедуру Setting такие строки:
d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_BLENDDIFFUSEALPHA d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE
D3DTOP_BLENDDIFFUSEALPHA для параметра D3DTSS_COLOROP обозначает, что используется альфа-компонента из DIFFUSE. Запускаем программу и видим, что цилиндр снизу покрыт текстурой, которая при подъеме постепенно переходит в DIFFUSE. Чтобы поменять местами TEXTURE и DIFFUSE, достаточно поменять местами COLORARG1 и COLORARG2:
d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_DIFFUSE d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG2, D3DTA_TEXTURE
Теперь попробуем использовать альфа-компоненту текстуры:
d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA
Не правда ли, названия параметров говорят сами за себя? Так же, как и для цвета, для альфа-компоненты тоже предусмотрены оператор и аргументы, они применяются, если нужно каким-либо образом совместить альфа-компоненты нескольких аргументов. Рассмотрим такой вариант:
d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE d3dDevice.SetTextureStageState 1, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA d3dDevice.SetTextureStageState 1, D3DTSS_COLORARG1, D3DTA_CURRENT d3dDevice.SetTextureStageState 1, D3DTSS_COLORARG2, D3DTA_DIFFUSE
Мы применили две стадии текстурирования. В нулевой стадии накладывается цвет из текстуры, а альфа-компоненты текстуры и Diffuse перемножаются. В следующей стадии смешиваются цвет из Diffuse с текущим цветом (то есть ранее наложенным, из текстуры), оператор смешения использует текущее (то есть ранее вычисленное в нулевой стадии) значение альфа-компоненты.
Мы рассмотрели несколько примеров использования прозрачности, но цилиндр пока прозрачным не был. Чтобы сделать его прозрачным, нужно чтобы в альфа-смешении участвовали не только аргументы текстурирования, но и ранее отрисованное изображение, то есть то, что находится в BackBuffer. При этом в качестве аргументов альфа-смешения будут выступать значение цвета, взятое из BackBuffer, и значение, полученное в результате текстурирования. Эти аргументы нужно указать:
d3dDevice.SetRenderState D3DRS_SRCBLEND, D3DBLEND_SRCALPHA d3dDevice.SetRenderState D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA d3dDevice.SetRenderState D3DRS_BLENDOP, D3DBLENDOP_ADD
Это более гибкий подход, позволяющий большего достичь, но и более ресурсоемкий, так как производится чтение из BackBuffer. В этих строках указывается, что в качестве множителя для первого аргумента – D3DRS_SRCBLEND, выступает альфа-компонента первого аргумента – D3DBLEND_SRCALPHA, для второго аргумента в качестве множителя мы выбрали D3DBLEND_INVSRCALPHA, то есть 1 – D3DBLEND_SRCALPHA. Далее мы выбрали оператор, применяемый для смешения полученных произведений – D3DRS_BLENDOP, этот оператор – D3DBLENDOP_ADD, то есть сложение.
Использование такого типа альфа-смешения необходимо разрешить:
d3dDevice.SetRenderState D3DRS_ALPHABLENDENABLE, 1
Закрасим цилиндр текстурой, с использования альфа-компоненты из DIFFUSE:
d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE
Цилиндр действительно стал полупрозрачным, но явно видны недостатки – временами изображение правильное, но через ближнюю сторону цилиндра, вторая видна не всегда. Это результат работы ZBuffer, если ближняя сторона цилиндра рисуется раньше, то дальняя в этом месте уже не рисуется, это отлично работало и избавляло нас от упорядочивания треугольников, пока мы не использовали прозрачность. Отключим ZBuffer:
d3dDevice.SetRenderState D3DRS_ZENABLE, D3DZB_FALSE
Эта строка находится в процедуре Main.
Что ж, изображение улучшилось, но небольшие погрешности в изображении остались, результирующий цвет получается разным, в зависимости от того, какая сторона цилиндра рисуется раньше. Кроме того отключение ZBuffer приведет к тому, что прозрачные предметы будут рисоваться даже тогда, когда они закрыты непрозрачными. Для того, чтобы правильно отображать предметы, использующие такой тип прозрачности, во время их рисования отключают не ZBuffer, а запись в него:
d3dDevice.SetRenderState D3DRS_ZWRITEENABLE, 0
И прозрачные треугольники нужно упорядочивать так, чтобы они отображались от дальних к ближним. Упорядочивание – процесс весьма ресурсоемкий, ведь число треугольников может достигать сотен тысяч! Но иногда удается этого избежать. Например, в нашем цилиндре внутренняя сторона (а это обратная сторона треугольников) всегда находится сзади. Таким образом, достаточно отобразить цилиндр дважды – сначала внутреннюю, а затем и внешнюю стороны, и необходимость в упорядочивании отпадает. Для этого изменим процедуру Render:
Private Sub Render() d3dDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET Or D3DCLEAR_ZBUFFER, &H346666, 1, 0 d3dDevice.BeginScene d3dDevice.SetStreamSource 0, vBuf, vSize d3dDevice.SetVertexShader vFlag d3dDevice.SetTexture 0, Tex0 d3dDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CW d3dDevice.DrawPrimitive D3DPT_TRIANGLESTRIP, 0, 128 d3dDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CCW d3dDevice.DrawPrimitive D3DPT_TRIANGLESTRIP, 0, 128 d3dDevice.EndScene d3dDevice.Present ByVal 0, ByVal 0, 0, ByVal 0 End Sub
Посмотрите на цилиндр теперь – он отображается корректно!
Но такие проблемы с альфа-смешением возникают не всегда, если множитель при D3DRS_DESTBLEND равен единице – упорядочивание не требуется (запись в ZBuffer отключаем все равно!). Такие настройки могут применяться, например, для изображения огня. Впишите в процедуру Setting такие настройки:
d3dDevice.SetRenderState D3DRS_ZENABLE, D3DZB_TRUE d3dDevice.SetRenderState D3DRS_ZWRITEENABLE, 0 d3dDevice.SetRenderState D3DRS_ALPHABLENDENABLE, 1 d3dDevice.SetRenderState D3DRS_SRCBLEND, D3DBLEND_SRCALPHA d3dDevice.SetRenderState D3DRS_DESTBLEND, D3DBLEND_ONE d3dDevice.SetRenderState D3DRS_BLENDOP, D3DBLENDOP_ADD d3dDevice.SetTextureStageState 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 d3dDevice.SetTextureStageState 0, D3DTSS_COLORARG1, D3DTA_TEXTURE d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE d3dDevice.SetTextureStageState 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE
Такой цилиндр отображается корректно без упорядочивания. При таком альфа-смешении задний фон всегда осветляется. Есть второе значение для D3DRS_BLENDOP, при котором не требуется упорядочивание треугольников, а изображение затемняется:
d3dDevice.SetRenderState D3DRS_BLENDOP, D3DBLENDOP_REVSUBTRACT
При таком значении D3DRS_BLENDOP, произведение цвета, полученного при текстурировании, на альфа-аргумент вычитается из цвета заднего плана.
комментарии отсутствуют
авторизуйтесь