Direct3D рендеринг 2D-изображений с «умножением» режима наложения и альфа - PullRequest
4 голосов
/ 23 октября 2009

Я пытаюсь повторить фильтр Photoshop умножить с Direct3D. Я читал и гуглял о различных состояниях рендеринга, и у меня эффект почти работал. Проблема в том, что он игнорирует альфа-значение текстур.

Вот изображение, которое объясняет ситуацию:

http://www.kloonigames.com/petri/stackoverflow_doesnt_allow_.jpg

Я нашел одно решение: сохранить изображения без прозрачности и белого фона. Но я не удовлетворен этим решением. Проблема в том, что мне действительно нужно использовать альфа-значение. Я хочу постепенно исчезать изображения. И я не могу сделать это, если режим смешивания игнорирует альфа-значение.

Итак, вопрос в том, как визуализировать изображения с альфа-каналом?

Вот код режима смешивания:

dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);

Правка добавила SetTextureStageState

dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);

Ответы [ 4 ]

4 голосов
/ 17 октября 2014

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

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

Blend = ( src.rgb * src.a ) + ( dest.rgb * (1-src.a) )
Add = ( src.rgb * src.a ) + ( dest.rgb )
Multiply = (src.rgb * dest.rgb * src.a) + (dest.rgb * (1-src.a) )

Вы заметите, что умножение невозможно за один проход, потому что есть две операции с исходным цветом. Но если вы предварительно умножите альфа в своем шейдере, вы можете извлечь альфа-компонент из операции смешивания, и станет возможным смешать все три операции в одном шейдере.

В вашем пиксельном шейдере вы можете предварительно умножить альфа вручную. Или используйте такой инструмент, как DirectXTex texconv, чтобы изменить текстуры.

return float4(color.rgb*color.a, color.a);

Операции становятся:

Blend = ( src.rgb ) + ( dest.rgb * (1-src.a) )
Add = ( src.rgb ) + ( dest.rgb )
Multiply = ( src.rgb * dest.rgb ) + (dest.rgb * (1-src.a) )
1 голос
/ 23 октября 2009

Звучит так, как вы хотите:

dst.rgb = (src.a * src.rgb) * ((1 - src.a) * dst.rgb)

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

0 голосов
/ 30 августа 2016
dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

сделает свое дело. Вы больше не можете использовать альфа из цвета диффузной вершины. Установка низкого альфа на цвета вершин фактически осветлит ваши наложенные пиксели.

0 голосов
/ 05 марта 2011

ОК, это не так просто, как вы думаете. Я бы использовал Effect & две renderTargets для этого ... Я забавляюсь, что вы используете один проход рендера, чтобы попытаться сделать это, но это не сработает. В Photoshop есть слои, и у каждого слоя есть альфа-канал. Кстати, было бы неплохо узнать, какое приложение вы делаете.

Итак, сначала в D3D я бы создал 2 объекта RGBA_32bit renderTarget того же размера, что и ваше окно, и очистил бы их до белого цвета. Сделайте это массивом так (new RenderTarget [2];) для замены.

Теперь установите состояние смешивания на (AlphaFunc = Add, Src = SrcAlpha, Dst = InvSrcAlpha). Для первого круга вы рисуете его в renderTarget [0], используя renderTarget [1] в качестве источника ввода текстуры / сэмплера. Вы будете визуализировать круг с эффектом, который примет цвет кругов и умножит его на цвет сэмплера renderTarget [1]. После того, как вы рисуете кружок один, вы заменяете renderTarget [0] на renderTarget [1] с помощью простого индексирования, поэтому теперь renderTarget [1] - это тот, который вы рисуете, а renderTarget [0] - тот, из которого вы производите выборку. Затем вы повторяете процесс рисования для круга 2 и т. Д.

После того, как вы рисуете любой кружок, вы копируете последний нарисованный объект renderTarget в backBuffer и представляете сцену.

Вот пример логически, как бы вы это сделали. Если вам нужна ссылка для кодирования http://www.codesampler.com/ это хорошее место.

void TestLayering()
{
bool rtIndex = false;
RenderTarget* renderTarget = new RenderTarget[2];
Effect effect = new Effect("multiplyEffect.fx");
effect.Enable();
BlendingFunc = Add;
BlendingSource = SrcAlpha;
BlendingDest = InvSrcAlpha;

for(int i = 0; i != circleCount; ++i)
{
  renderTarget[rtIndex].EnableAsRenderTarget();
  renderTarget[!rtIndex].EnableAsSampler();
  circle[i].Draw();
  rtIndex = !rtIndex;
}

//Use D3D9's StretchRect for this...
backBuffer.CopyFromSurface(renderTarget[rtIndex]);
}

//Here is the effects pixel shader
float4 PS_Main(InStruct In) : COLOR
{
float4 backGround = tex2D(someSampler, In.UV);
return circleColor * backGround;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...