如何使用预乘alpha实现D3D输出以与WPF中的D3DImage一起使用?

时间:2013-09-20 13:59:48

标签: wpf direct3d alphablending

我们在WPF中使用D3DImage来显示在WPF中创建的背景上使用Direct3D呈现的部分透明3D内容。所以,我们有这样的事情:

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

现在,我们遇到的问题是抗锯齿边缘无法正确显示。我们在某些边缘处获得某种白色“发光”效果,类似于第一张图片中显示的文字效果:

(摘自http://dvd-hq.info/alpha_matting.php

原因是我们从DirectX获得的数据显然是使用alpha通道而不是预乘。但是WPF和D3DImage期望这是他们的输入(请参阅CodeProject上的"Introduction to D3DImage",“简要介绍D3D互操作要求”部分。)

我通过将每个像素乘以C#中的alpha值并使用自定义像素着色器来验证这实际上是个问题。结果都很好。但是,我想直接从DirectX获得此结果。

如何直接从DirectX获取预乘数据?我可以在调用CreateRenderTarget时以某种方式指定它吗?我不是DirectX专家,所以请原谅解决方案是否明显......

1 个答案:

答案 0 :(得分:1)

为了让您的DirectX内容正确预乘,您需要一个自定义ID3D11BlendState。 D3D11_BLEND_DESC结构应该按如下方式设置:

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;

使用ID3D11Device::CreateBlendState创建混合状态,然后使用ID3D11DeviceContext::OMSetBlendState进行设置。然后,正常设置Direct3D管道的其余部分,并且渲染目标的输出应该具有正确的alpha通道输出的预乘alpha,用于Direct2D,WPF或任何其他需要预乘的内容的API

请注意,如果您使用的是Direct3D 9,则概念相同,但API不同。如果您使用9,请查看here以获得开始的好地方。