Вопрос

We are using a D3DImage in WPF to display partly transparent 3D content rendered using Direct3D on a background which is created in WPF. So, we have something like this:

<UserControl Background="Blue">
    <Image>
        <Image.Source>
            <D3DImage .../>
        </Image.Source>
    </Image>
</UserControl>

Now, we have the problem that anti-aliased edges are not displayed properly. We get some sort of white "glow" effect at some edges, similar to the effect around the text shown in the first picture:

(taken from http://dvd-hq.info/alpha_matting.php)

The reason is that the data we get from DirectX obviously is not premultiplied with the alpha channel. But WPF and the D3DImage expect this as their input (see "Introduction to D3DImage" on CodeProject, section "A Brief Look at D3D Interop Requirements").

I verified that this is actually the problem by multiplying every pixel by its alpha value in C# and using a custom pixel shader. The results were both fine. However, I would like to get this result from DirectX directly.

How can I get premultiplied data from DirectX directly? Can I somehow specify it in the call to CreateRenderTarget? I am no DirectX expert, so please excuse if the solution might be obvious...

Это было полезно?

Решение

In order to have your DirectX content properly pre-multiplied, you will need a custom ID3D11BlendState. The D3D11_BLEND_DESC struct should be set up as follows:

AlphaToCoverageEnable = FALSE;
IndependentBlendEnable = FALSE;
RenderTarget[0].BlendEnable = TRUE;

// dest.rgb = src.rgb * src.a + dest.rgb * (1 - src.a)
RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;

// dest.a = 1 - (1 - src.a) * (1 - dest.a) [the math works out]
RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA;
RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;

RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;

Create the blend state with ID3D11Device::CreateBlendState, then set it with ID3D11DeviceContext::OMSetBlendState. Then, just set up the rest of your Direct3D pipeline as normal, and the output to the render-target should have pre-multiplied alpha with correct alpha channel output, for use with Direct2D, WPF, or any other API requiring pre-multiplied content.

Note that if you are using Direct3D 9, the concepts are the same but the API is different. Look here for a good place to get started if you're using 9.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top