Advertisement Jump to content
Sign in to follow this  
Freakill

Transparency problems with D2D over D3D

This topic is 1863 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi,

 

I'm developing a Kinect game where the player will see himself in the virtual environmnet. I've been developing the game in 3D with C++ and D3D11, and when the time to integrate Kinect arrived, I used the code from Kinect SDK examples where they render the user by creating a bitmap and rendering it in D2D.

 

Searching I found out this post http://www.gamedev.net/topic/639578-d2d-d3d11-help-me-to-make-them-work-together/ which helped a lot to set up the renderer. My problem now is that when I render the bitmap, what should be the transparent part is black:

 

kinect_rendering.png

 

Here is the initialization of the D2D renderer:

 

D2D1_FACTORY_OPTIONS options;

options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, options, &D2DFactory_);
 
    D2D1_SIZE_U size = D2D1::SizeU(colorWidth_, colorHeight_);
 
IDXGISurface *dxgiBackbuffer;
swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackbuffer));
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED));
D2DFactory_->CreateDxgiSurfaceRenderTarget(dxgiBackbuffer, props, &renderTarget_);
dxgiBackbuffer->Release();
dxgiBackbuffer = 0;
 
    if(FAILED(hr))
    {
MessageBox(NULL, L"Could not create render target.", L"Draw Error", MB_OK);
        return false;
    }
 
    // Create a bitmap that we can copy image data into and then render to the target
    hr = renderTarget_->CreateBitmap(size, 
D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)),
&bitmap_);

 

And when I render the image I just call the following code, like in the Kinect SDK example:

 

renderTarget_->BeginDraw();

 
    // Draw the bitmap stretched to the size of the window
    renderTarget_->DrawBitmap(bitmap_);
            
    hr = renderTarget_->EndDraw();

 

I honestly don't know where to look now. I am able to render text in the screen rendering bitmaps with transparency, but following the same procedure of enabling AlphaBlending in the D3D renderer, and then rendering the bitmap, I get nothing.

 

I would like to know where should I be looking at to achieve transparency smile.png

 

Thanks for your help!

Share this post


Link to post
Share on other sites
Advertisement

D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)),
&bitmap_);

That won't do. For transparency, your bitmap should have a pixel format with an ALPHA value, most likely D2D1_ALPHA_MODE_STRAIGHT.

You haven't posted the code where the bitmap is filled... does your source image data (that you get from Kinect) have an alpha value? By the looks of it, it should, but you're just D2D1_ALPHA_MODE_IGNORE'ing it.

Edited by tonemgub

Share this post


Link to post
Share on other sites

Hi tonemgub,

 

thanks for your reply. The problem is that for DXGI Surface Renderer I cannot get DXGI_FORMAT_B8G8R8A8_UNORM with D2D1_ALPHA_MODE_STRAIGHT (just IGNORE or PERMULTIPLIED, which leaves me the image semitransparent in all the surface) regarding this page (and if I try I get a runtime error): http://msdn.microsoft.com/en-us/library/windows/desktop/dd756766(v=vs.85).aspx#supported_formats_for_dxgi_surface_render_target

 

Example PERMULTIPLIED:

kinect_rendering_permultiplied.png

 

The loading of data I do it following Kinect SDK's example:


 

NUI_BACKGROUND_REMOVED_COLOR_FRAME bgRemovedFrame;

 
hr = backgroundRemovalStream_->GetNextFrame(0, &bgRemovedFrame);
    if (FAILED(hr))
    {
        return hr;
    }
 
    const BYTE* pBackgroundRemovedColor = bgRemovedFrame.pBackgroundRemovedColorData;
 
    int dataLength = static_cast<int>(colorWidth_) * static_cast<int>(colorHeight_) * 4;
    BYTE alpha = 0;
    const int alphaChannelBytePosition = 3;
    for (int i = 0; i < dataLength; ++i)
    {
        if (i % 4 == 0)
        {
            alpha = pBackgroundRemovedColor[i + alphaChannelBytePosition];
        }
 
        if (i % 4 != alphaChannelBytePosition)
        {
            outputRGBX_ = static_cast<BYTE>(
                ( (UCHAR_MAX - alpha) * backgroundRGBX_ + alpha * pBackgroundRemovedColor ) / UCHAR_MAX
                );
        }
    }
 
    hr = backgroundRemovalStream_->ReleaseFrame(&bgRemovedFrame);
    if (FAILED(hr))
    {
        return hr;
    }

 

 

And then I copy the outputRGBX_ buffer to the bitmap with this call before drawing it:


 

hr = bitmap_->CopyFromMemory(NULL, outputRGBX_, colorWidth_*4);

Edited by Freakill

Share this post


Link to post
Share on other sites

Ok, I didn't know that DXGI doesn't support D2D1_ALPHA_MODE_STRAIGHT. Sorry.

 

I think your problem lies here:

 


            outputRGBX_ = static_cast<BYTE>(
                ( (UCHAR_MAX - alpha) * backgroundRGBX_ + alpha * pBackgroundRemovedColor ) / UCHAR_MAX
                );

 

 

This basically blends the incoming Kinect frame with whatever RGB values you already have in backgroundRGBX_, and then it stores them back into backgroundRGBX_. And you did not post the code where you create and initialize backgroundRGBX_ either, but if you're simply allocating it with new, or malloc, and not  filling it with anything, then in debug builds, it will be filled with 0xCDCDCDCD (which is something like a 85% transparent almost-fully-white color - the same as the background from the PREMULTIPLIED example image you posted).

 

So instead of blending and then storing the RGB values, you have to just pre-multiply and store them. If you delete (or comment out) the part that I bolded in the quote, you will get rid of the blending part.

 

Also, I'm not sure if this is required (it probably is, because the alpha value is needed in the alpha-blending formula, to get the destination blending factor), but the standard pre-multiplied format usually also preserves the original alpha value, so you should also store the incoming alpha value in your backgroundRGBX_ buffer as well:

        if (i % 4 == alphaChannelBytePosition)
        {
            outputRGBX_[i] = static_cast<BYTE>(alpha);
        }

If you make all the changes I mentioned so far, you should get correct transparency, with D2D1_ALPHA_MODE_PREMULTIPLIED.

 

 

 

Another, even better possibility is for you to use D2D1_ALPHA_MODE_IGNORE. Then, all you have to do is just this:

hr = bitmap_->CopyFromMemory(NULL, pBackgroundRemovedColor, colorWidth_*4);

This will be faster because you won't need to use the intermediary backgroundRGBX_ buffer anymore - you can remove it, and you also won't need to pre-multiply the RGB values. This will work, because according to the article you posted, D2D1_ALPHA_MODE_IGNORE should do alpha blending correctly treating the source bitmap as non-premultiplied. I guess DXGI only looks at the D2D1_ALPHA_MODE parameter to know if it should treat the bitmap premultiplied or not, but if the bitmap's pixel format has an alpha channel, and you use D2D1_ALPHA_MODE_IGNORE, it will still use the alpha channel for normal alpha blending. (I might be wrong here, so if this doesn't work, then just use the premultiplied alpha method smile.png ).

Edited by tonemgub

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!