Skip to main content
gamedev.net
🔒 Locked

Sprite Transparency

Started by TriMorph Mar 10, 2005 at 5:12 PM 9 replies 3.4k views
Original Post
TriMorph
TriMorph
Hey folks, My son is learning to program in DirectX, which is finally pushing me to learn it too! Unfortunately, we’re having trouble getting sprite transparency to work. We’re trying to import a .bmp into a simple game, and we want black to be transparent when we render the sprite image. I’ve done an awful lot of web searching to try to figure this out, but everything I find says we’re doing this correctly (though we’re obviously not, because it's not working!). Here are some snippets of the code we’re trying to use to do this (from Wendy Jones' intro to DirectX book, with minor modifications): To load the surface from the .bmp file we’re using the function:

IDirect3DSurface9* dxManager::getSurfaceFromBitmap(std::string filename)
{
	HRESULT hResult;
	IDirect3DSurface9* surface = NULL;
	D3DXIMAGE_INFO imageInfo;

	// Get the width and height info from this bitmap
	hResult = D3DXGetImageInfoFromFile(filename.c_str(), &imageInfo);
	if FAILED (hResult)
		return NULL;

	hResult = pd3dDevice>CreateOffscreenPlainSurface(imageInfo.Width, 
                    imageInfo.Height, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT,
                    &surface, NULL);
	if (FAILED(hResult))
		return NULL;

	hResult = D3DXLoadSurfaceFromFile(surface, NULL, NULL, 
                    filename.c_str(), NULL, D3DX_DEFAULT,
                    D3DCOLOR_ARGB(0xff,0,0,0), NULL);
	if (FAILED(hResult))
		return NULL;

	return surface;
}

Note that the offscreen surface uses D3DFMT_A8R8G8B8, and that when we load the surface from the file we set the color key using D3DCOLOR_ARGB(0xff,0,0,0). My understanding is that this should make opaque black in our original .bmp image appear as transparent when we render it. We then render the sprite (inside another function) using:

    // turn on alpha blending      
    pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true ); 
    pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCCOLOR);
    pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCCOLOR);

 
    // blit (using separate getBackBuffer function for the back buffer)      
    pd3dDevice->StretchRect(srcSurface, srcRect, getBackBuffer(), 
                            destRect, D3DTEXF_NONE);

    // present the backbuffer contents to the display   
    pd3dDevice->Present( NULL, NULL, NULL, NULL );
    
I apologize for giving so much code, but I’ve as yet been unable to find a complete, working example that does this simple thing. Can anyone see what we’re doing wrong here? Thanks in advance for your help, TriMorph
Scet
Scet
You're using alpha blending when (I think) it should use alpha testing.

Try this:
pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);pd3dDevice->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);
eFoDay
eFoDay
here is the easiest way to display practically any image file with or without alpha using the alpha channel of the source image

// loadLPD3DXSPRITE sprite;LPDIRECT3DTEXTURE9 texture; D3DXCreateSprite(device, &sprite); D3DXCreateTextureFromFileEx(device, "image.png", D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT, D3DX_FILTER_NONE, D3DX_FILTER_NONE, 0, NULL, NULL, &texture); // loopsprite->Begin(D3DXSPRITE_ALPHABLEND);sprite->Draw(texture, NULL, NULL, NULL, D3DCOLOR_RGBA(255,255,255,255));sprite->End(); // unloadtexture->Release();sprite->Release();


edit:

now of course .bmp files have no alpha so you would need to specify a color key when calling D3DXCreateTextureFromFileEx
MasterWorks
MasterWorks
Color keying is so limited and outdated... why not use PNG (or at least TGA)? Even with transparency the PNG file is going to be smaller than the BMP 99.9% of the time...
DrunkenHyena
DrunkenHyena
The problem with the original code is this:
pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCCOLOR);
pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCCOLOR);

Rather than blending by the colour, you should be using the alpha for blending:

pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);


In response to some of the other posts: There is nothing wrong with using a colour key like this. There may be ways that are more optimal in various ways, but for now I'd recommend focusing on keeping it simple. You can worry about optimizing things when you have a firmer foundation in the technology.
Stay Casual,KenDrunken Hyena</FON
TriMorph
TriMorph
Folks,

Thanks for the prompt replies! We did try using alpha test instead of alpha blend without success; the same applies for using SRCALPHA and INVSRCALPHA instead of SRCCOLOR and INVSRCCOLOR. I didn't pursue the other options because I agree with DrunkenHyena that we should get this (theoretically) very simple thing working before stretching to more complicated approaches.

Sorry to post the entire program below, but there's got to be something silly we're doing somewhere that we just can't figure out.

Any and all help figuring our what we're doing wrong in the code would be greatly appreciated!

Thanks,
TriMorph

#include <windows.h>#include <d3d9.h>#include <d3dx9tex.h>#include <string>HINSTANCE hInst;HWND wndHandle;bool initWindow(HINSTANCE hInstance);LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);// DirectX functions and global variablesHRESULT                 lastResult;LPDIRECT3D9             pD3D;LPDIRECT3DDEVICE9       pd3dDevice;IDirect3DSurface9*		bkgrd;bool initDirect3D(HWND hwnd);IDirect3DSurface9* getSurfaceFromBitmap(std::string filename);void Render(void);int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){	if (!initWindow(hInstance))		return false;		if (!initDirect3D(wndHandle))		return false;	bkgrd = getSurfaceFromBitmap("./background.bmp");	// Main message loop:	// Enter the message loop    MSG msg;     ZeroMemory( &msg, sizeof(msg) );    while( msg.message!=WM_QUIT )    {		if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )        {			TranslateMessage( &msg );            DispatchMessage( &msg );        }		else		{			// call our render function			Render();		}    }	if( pd3dDevice != NULL) 	{        pd3dDevice->Release();		pd3dDevice = NULL;	}    if( pD3D != NULL)	{        pD3D->Release();		pD3D = NULL;	}	return (int) msg.wParam;}bool initWindow(HINSTANCE hInstance){	WNDCLASSEX wcex;	wcex.cbSize = sizeof(WNDCLASSEX);	wcex.style			= CS_HREDRAW | CS_VREDRAW;	wcex.lpfnWndProc	= (WNDPROC)WndProc;	wcex.cbClsExtra		= 0;	wcex.cbWndExtra		= 0;	wcex.hInstance		= hInstance;	wcex.hIcon			= 0;	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);	wcex.lpszMenuName	= NULL;	wcex.lpszClassName	= "DirectXExample";	wcex.hIconSm		= 0;	RegisterClassEx(&wcex);	wndHandle = CreateWindow("DirectXExample", 							 "DirectXExample", 							 WS_OVERLAPPEDWINDOW,							 CW_USEDEFAULT, 							 CW_USEDEFAULT, 							 640, 							 480, 							 NULL, 							 NULL, 							 hInstance, 							 NULL);   if (!wndHandle)      return false;      ShowWindow(wndHandle, SW_SHOW);   UpdateWindow(wndHandle);   return true;}LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){	switch (message) 	{		case WM_DESTROY:			PostQuitMessage(0);			break;	}	return DefWindowProc(hWnd, message, wParam, lParam);}bool initDirect3D(HWND hwnd){	if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )	{		lastResult = E_FAIL;		return false;	}	D3DPRESENT_PARAMETERS d3dpp;     ZeroMemory( &d3dpp, sizeof(d3dpp) );    d3dpp.Windowed = TRUE;    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;	d3dpp.BackBufferCount  = 1;	d3dpp.BackBufferHeight = 480;	d3dpp.BackBufferWidth  = 640;	d3dpp.hDeviceWindow    = hwnd;    if( FAILED( pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, hwnd,                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,                                      &d3dpp, &pd3dDevice ) ) )    {		lastResult = E_FAIL;        return false;    }	return true;}IDirect3DSurface9* getSurfaceFromBitmap(std::string filename){	HRESULT hResult;	IDirect3DSurface9* surface = NULL;	D3DXIMAGE_INFO imageInfo;	// Get the width and height info from this bitmap	hResult = D3DXGetImageInfoFromFile(filename.c_str(), &imageInfo);	if FAILED (hResult)		return NULL;	hResult = pd3dDevice->CreateOffscreenPlainSurface(imageInfo.Width, imageInfo.Height, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT, &surface, NULL);	if (FAILED(hResult))		return NULL;	// color key is black for transparency	hResult = D3DXLoadSurfaceFromFile(surface, NULL, NULL, filename.c_str(), NULL, D3DX_DEFAULT, D3DCOLOR_RGBA(0,0,0,255), NULL);	if (FAILED(hResult))		return NULL;	return surface;}void Render(void){	if( NULL == pd3dDevice )        return;	// enable aplpha blending	pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true );     pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);    pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);    // Clear the backbuffer to a blue color    pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );		IDirect3DSurface9* backbuffer;	pd3dDevice->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO, &backbuffer);	HRESULT hResult = pd3dDevice->StretchRect(bkgrd, NULL, backbuffer, NULL, D3DTEXF_NONE);	if (hResult != D3D_OK)		MessageBox(NULL,"failed", "error", MB_OK);	// Present the backbuffer contents to the display    pd3dDevice->Present( NULL, NULL, NULL, NULL );}
DrunkenHyena
DrunkenHyena
Okay, 2 things:

1) It looks like you changed your surface allocation since your original post.
CreateOffscreenPlainSurface is being passed D3DFMT_X8R8G8B8 which means it does not have an alpha channel to store the colour-key-generated alpha. Change that to D3DFMT_A8R8G8B.

2) StretchRect does not support alpha blending. Think of it not as a drawing call, but as a memory copying method. If that books says it does, it's a BAD BAD book and the author needs to be spanked.

The Forum FAQ has a good list of resources to get you going with the proper way to render. This tutorial covers rendering a textured quad.
Stay Casual,KenDrunken Hyena</FON
Battagline
Battagline
This may be a dumb question. But have you considered loading a image file with an alpha channel like .dds or .png? If you do this and set D3DRS_ALPHABLENDENABLE back to true, I think that will work for you. But I use Sprite objects to render my sprites rather than StretchRect(). Out of curiosity, what version of DirectX are you using?
Susan-gt
Susan-gt
IF you check the documentation you will find that it says strechrect doesn't
support color keys.

tell your son not to get frustrated for I have programming for 15 years
and I am having trouble with transparancey. It is basically microsoft
doing what is best for them. THey want one way to handle transparency
and that is using textures with the 3d pipeline. THe good news is they
have create basically a wrapper class around textures that make sprites
easy (IDXSPRITE). THe bad news is in the last 14 months the interface
has changed 3 times

9.0 draw uses 5 parameters
9.0b draw uses 7 parameters
9.0c draw uses 5 parameters (make up your mind already)

actually IDXSPRITE is most the straight forward thing I have found for it handles all renderstates for you. If you want more info post a reply.
TriMorph
TriMorph
Folks,

Thanks to everyone for your posts. We'll go look this stuff over in more detail and come back later if we can't get it working.

Thanks for the support -- we won't give up!

TriMorph

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.