Possible to set colour key on a texture made with d3dDevice-CreateTexture?

Started by
6 comments, last by Mythar 16 years, 4 months ago
I'm using a texture created with d3dDevice->CreateTexture(...); Is there a way of setting a colour key or make the white pixels transparent when it is used to draw? Thanks btw I'm drawing on the texture with gdi commands, maybe there could be something done from there?
Advertisement
Color keying as such doesn't exist within Direct3D, but you can use several tricks to achieve the same thing. Probably the most prominent of which is alpha testing. I haven't used GDI in combination with a Direct3D texture, so I can't help you out there, but in general in comes down to this:
1) When you have created a texture (using a format with an alpha channel, such as D3DFMT_A8R8G8B8), lock its surface to obtain access to its pixels;
2) Any pixel you want to be transparent should then have an alpha value of 0;
3) Use the texture in combination with alpha testing.
d3dDevice->SetRenderState( D3DRS_ALPHAREF, (DWORD)1 );d3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );d3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );

HTH

PS. It's been awhile now, but if I remember correctly GDI (not plus) doesn't natively support alpha, unless you work on a pixel level (through DIB sections for example). So you could perform normal drawing first, then lock the surface and mask out any pixels you want to turn transparent.

unsigned char * bits = static_cast<unsigned char *>( lockedRect.pBits );for( int y = 0; y < Height; y++ ){  for( int x = 0; x < Width; x++ )  {    // if color == solid green    if( bits[1] == 0xff )    {      // set alpha to zero      bits[3] == 0;    }    // goto next pixel (you should probably make use of the provided pitch)    bits += 4;  }}


EDIT: Added some sample code


[Edited by - Todo on December 22, 2007 8:05:49 AM]
When I tried using the D3DFMT_A8R8G8B8 format it has a problem:
Quote:
GetDC only supports D3DFMT_R5G6B5, D3DFMT_X1R5G5B5, D3DFMT_R8G8B8, and D3DFMT_X8R8G8B8.


And from what I've checked, none of those formats it specifies supports the alpha channel.

A weird thing I noiced was that just by locking and unlocking the rect, it sets the colour of the surface from default black to white.
D3DLOCKED_RECT cardSurfaceLockedRect;cardSurface->LockRect(&cardSurfaceLockedRect, 0, D3DLOCK_DISCARD);cardSurface->UnlockRect();




I think what I want to do is set the entire colour of the surface to transparent using the alpha, and then draw the gdi stuff ontop of it.
Of which I tried that but the background just turns out white.
	D3DLOCKED_RECT cardSurfaceLockedRect;	cardSurface->LockRect(&cardSurfaceLockedRect, 0, D3DLOCK_DISCARD);	unsigned char * bits = static_cast<unsigned char *>( cardSurfaceLockedRect.pBits );	for(int y=0;y<128;y++) {		for(int x=0;x<128;x++) {			bits[3] = 0;			bits += 4;		}	}	cardSurface->UnlockRect();


I'm not sure how to do that with the format problem above.

Thanks.
True, GDI drawing isn't compatible with alpha in surfaces. Options I see are to either copy that render target into one which does have alpha, and set alpha, or use a pixel shader to clip() out white pixels.
If you want to use the GDI to Draw on your texture, use D3DFMT_X8R8G8B8.

Create aTexture using : D3DUSAGE_DYNAMIC and D3DFMT_X8R8G8B8

Get aSurface : aTexture.GetSurfaceLevel(0, aSurface)

Get aDC : aSurface.GetDC(aDC)

Using this aDC you can create a GDI Canvas to Draw on.

Remember to release the aDC : aSurface.ReleaseDC(aDC)

Now use a shader to clip() then transparent pixels.
What both Mythar and ET3D hinted at is effectively the programmable version of the original solution, albeit more flexible. You get to select which fragments to cull (or clip()) yourself. I also didn't realize that particular limitation of surface formats, sorry :-).

For what it's worth, the color change from black to white is most likely garbage as a result of discarding the previous contents of the surface (D3DLOCK_DISCARD). I guess you shouldn't rely on this always being the case (white...).
On second thought I'd actually suggest not using clip() for this purpose. Assuming that you use GDI infrequently, then render the result quite a few times, it should be more efficient to do the alpha conversion once, and not clip() every time.
Depends, if you don't need to update the "GDITexture" (D3DFMT_X8R8G8B8) - sure convert it to an alpha texture.

However if you need to update the GDI Texture, you don't want to do convertions to an alpha texture.

Insted (if you dont want to use clip()) you can "cheat" like this:

sampler sTexture = sampler_state{  Texture   = <someTexture>;  MipFilter = NONE;  // Set Min/MagFilter to POINT for PixelPerfect transparency  MinFilter = POINT;  MagFilter = POINT;  AddressU  = WRAP;  AddressV  = WRAP;};float4 PShader(VS_OUTPUT i) : COLOR{  float4 o;  o = tex2D(sTexture, i.uv);  // Assumes transparent color is 0(Zero)  if ((o.r + o.g + o.b) > 0.0) o.a = 1.0; else o.a = 0.0;  return o;}technique FakeAlpha{  pass P0  {    AlphaBlendEnable = true;    SrcBlend = SrcAlpha;    DestBlend = InvSrcAlpha;    pixelshader = compile ps_1_4 PShader();    vertexshader = compile vs_1_1 VShader();  }}

This topic is closed to new replies.

Advertisement