PDA

View Full Version : [D3D] problems with transparency...



chronozphere
11-04-2006, 01:16 PM
Hi PGD people 8)

I bumped into a Transperancy problem.
I am currently using a 24 bit texture in my game with $000000ff (bleu) as transparent color.
The backbuffer format is D3DFMT_A8R8G8B8 and
i always use the backbuffer format for Texture initalisation (Just thought its the best way ;) ).

This works fine, but i want my engine to also use 16bit graphics. I think that is D3DFMT_R5G6B5.
But when i Initalize a 16 bit Texture,i have to pass a different transparent color because it has to be 16bit
(I though of $001F but it doesn't work :( ).
Maybe the actual data of the texture is changed, since its a 24 bit Bitmap loaded on a D3DFMT_R5G6B5 texture, so some color data might be lost. :?
I have not been able to find the correct value, so far. :cry:

Does anyone have a solution or a good color conversion routine???? :o

tpascal
11-04-2006, 06:08 PM
This works fine, but i want my engine to also use 16bit graphics. I think that is D3DFMT_R5G6B5.

I guess it should be D3DFMT_A1R5G5B5, so 1 bit for alpha, and 5 bits for each color component.

- with 8 bits you get values from 0 to 255 for each component.
- with 5 bits you get values from 0 to 31 for each component.

If you need to convert manually 8 bit color to 5 bit color you should do interpolation, somthing like 256/32=8

color5b = color8b/8;



tp.

chronozphere
11-04-2006, 07:52 PM
Thanx.. 8)

After a little research i found out that it is better to not always use the backbuffer format for textures.
Since the backbuffer doesnt need an alpha channel, you shouldn't give it one.

Okay this is how i've done it (so far):

In 32bit mode:

Initalize the device using D3DFMT_A8R8G8B8.
Textures also use D3DFMT_A8R8G8B8.

In 16 Bit mod:

Initalize the device using D3DFMT_R5G6B5.
Textures can be initalized using D3DFMT_A1R5G5B5 Or D3DFMT_A4R4G4B4. This depends on whether the user/gamedeveloper wants an alpha-channel in the texture.

If anyone has some tips ore suggestions, please tell me. :)

I didn't wrote a conversion routine yet. Its late (in holland 9:45 PM) so i won't write it today.
But i will try tomorrow. ;)

If any of you guys has one, please post it :razz:

Clootie
11-04-2006, 08:31 PM
Advice: Forget about 16bit color. :D

Why make an additional effort on something that will not be used widely. Except some special cases you will not get much performance increase, but quality degradation is usually clearly visible.

chronozphere
12-04-2006, 02:43 PM
Why make an additional effort on something that will not be used widely. Except some special cases you will not get much performance increase, but quality degradation is usually clearly visible.


yeah i get your point, but i think its better te let the user decide. :)

I wrote a routine wich converts colors from A8R8G8B8 format to A4R4G4B4:


function Convert8888To4444(Color: Cardinal): Word;
var R,G,B,A: byte;
begin
B := Byte(Color);
G := Byte(Color shr 8);
R := Byte(Color shr 16);
A := Byte(Color shr 24);

B := B div 16;
G := G div 16;
R := R div 16;
A := A div 16;

Result := (A shl 12) or (R shl 8) or (G shl 4) or B;
end;


For some reason it still doesn't work. I have made some very basic bitmaps for testing, with easy-to-use colors (like pure red and pure green) and calculated the colors with my routine.
but transparency still doesn't work. :(

Can someone help me?? :cry:

tpascal
12-04-2006, 04:22 PM
I have tried your function and seem it work great, i tried diferent components values from 0 to 255 and the function correctly return the value converted to 4 bit values. (255 become 15, 128 becom 8, 64 become 4, etc).

So i am sure your bitmap pixels are correct converted, so when you say alpha is not working i guess you mean when rendering alpha textures it dosent appear background transparent; if so then the problem could be somthing else like the way you are setuping alpha blending in your d3d app.





Why make an additional effort on something that will not be used widely. Except some special cases you will not get much performance increase, but quality degradation is usually clearly visible

maybe in USA or most European countries people currently have todays ATI or Nvidia videocards in theirs system, but if the app is going to be internet downloaded then a lot others countrys will have also access to the app and they not necesary are equiped with lastest videocards.

I live in Latin America (central america) and most office or home computer are equiped with average videocards; in my computer playing my dx8 app in 32bit color cuts down the frame rate to the half.

However...i think the 16 bit mode D3DFMT_A4R4G4B4 is too low, it just give 16 values components color and it is not faster than other 16 bit color modes, you should use D3DFMT_A1R5G5B5 wich gives 32 values components, in my experience for enabling background alpha transparency in sprites or textures you just need store 1 bit for opaque/transparent state.


good luck.

tp

Clootie
12-04-2006, 06:39 PM
chronozphere you should declare your "var R,G,B,A: byte;" variables as cardinals. Or better cast them to cardinals right before shl.

Another (should be faster) way:

function Convert8888To4444(Color: Cardinal): Word;
begin
Result :=
((Color and ($F shl 4)) shr 4) or // B
((Color and ($F shl 12)) shr 8) or // G
((Color and ($F shl 20)) shr 12) or // R
((Color and ($F shl 28)) shr 16); // A
end;

chronozphere
13-04-2006, 04:09 PM
Hi and thanx for your replies. :)



So i am sure your bitmap pixels are correct converted, .....


owh i didn't wrote a routine that converts bitmap pixels yet. I simply dont know how to convert a TBitmap to A1R5G5B5 and then convert it to a texture :(

I thought it would be good to load a texture and modify the alpha/transperacy bits for each pixel afterwards
Just walk through the pixels and make a pixel transparent when its color is equal to the transparent color (e.g black).

I wrote this method, but it doesn't work yet:

procedure TBF_Texture.ProcessTransparency(Transcol: Word);
var surf: IDirect3dSurface8;
HR : HResult;
D3DRect: _D3DLOCKED_RECT;
P: PWord; //pointer to the chunk of memory
x,y: integer; //loop counters
begin
HR := fTexture.GetSurfaceLevel(0,surf);
if Failed(HR) then
begin
ReportError(True,Self,E_TEXTURE_CANNOTGETLVL,HR);
Exit;
end;

HR := Surf.LockRect(D3DRect,nil,D3DLOCk_NOSYSLOCK);
if Failed(HR) then
begin
ReportError(True,Self,E_CANNOT_LOCK_SURFACE,HR);
Exit;
end else
try
for y:=0 to fHeight-1 do
begin
P := PWord(Dword(D3DRect.pBits) + D3DRect.Pitch * y);
for x:=0 to fWidth-1 do
begin
//if color is the transparent color (exclude alpha bit)
if ((P^ and $7FFF) = TransCol) then
P^ := (P^ and $7FFF) //set the first bit to zero (transparent)
else
P^ := (P^ or $8000); //set the first bit to one (non-transperant)

//select next color
P := PWord(Dword(P) + 1);
end;
end;
finally
Surf.UnlockRect;
end;
end


This method should set the first (alpha) pixel of a A1R5G5B5 texture to transparent, when its color is equal to the specified transparent color. But surface locking is a problem, it alwasy returns D3DERR_INVALIDCALL.

I know.. I know... The pointer stuff in the above routine aint very good. I'am no pointer guru. :roll:

When this method works i could modify it, so it can read the upperleft pixel and set all the pixels of the same color to transparent. Then the whole problem would be fixed :D.

So if someone could help me with my code, or with converting the bitmap, i would be very happy. :D


P.S. I also wrote a conversion routine for A1R5G5B5 colors. :lol: Here it is:


function Convert8888To1555(Color: Cardinal): Word;
var R,G,B,A: word;
begin
B := Word(Color) and $01ff;
G := Word(Color shr 8) and $01ff;
R := Word(Color shr 16) and $01ff;
A := Word(Color shr 24) and $0001;

B := B div 8;
G := G div 8;
R := R div 8;

Result := (A shl 15) or (R shl 10) or (G shl 5) or B;
end;


@Clootie: I know this method could be optimized, like you showed me, but i'am not a bitshifting pro yet. BTW I looked at your code and it's a brilliant piece of bitshifting, if it works (wich i assume) :D
I looked at

Clootie
13-04-2006, 05:49 PM
HR := Surf.LockRect(D3DRect, nil, D3DLOCk_NOSYSLOCK);

But surface locking is a problem, it alwasy returns D3DERR_INVALIDCALL.

Textures created with D3DPOOL_DEFAULT or with D3DUSAGE_RENDERTARGET are not lockable. Verify your creation flags. Additionally you should install DirectX debug runtime and look at it's error messages


@Clootie: I know this method could be optimized, like you showed me, but i'am not a bitshifting pro yet. BTW I looked at your code and it's a brilliant piece of bitshifting, if it works (wich i assume) :D
I looked at
I hope it works, but have not verified :)

chronozphere
13-04-2006, 07:13 PM
Textures created with D3DPOOL_DEFAULT or with D3DUSAGE_RENDERTARGET are not lockable.


Thanx for the hint..8) I used D3DPOOL_DEFAULT.
I changed it to D3DPOOL_MANAGED, but i think this is a bit inefficient because its now also stored in system memory. Are there any good alternatives??

Oke.. The locking and unlocking works now, but transparency doesn't work OK yet. I am now able to do some pixel-processing, and i noticed that i was only processing the left half of my texture.

I retrieved the width using:


fTexture.GetLevelDesc(0,Desc);
fWidth := Desc.Width;
fHeight := Desc.Height;


And i used it in my loop.
I tried this:


for x:=0 to fWidth*2-1 do
begin


And it worked... :D without any AV or other error.
So it looks like the texture is still using 32 bits per pixel because the routine uses WORD's.
Its very weird because i created it using A1R5G5B5.

How is this possible?? :scratch: :lol:

P.S i will continue, playing with this code. I will keep you informed. :)

Clootie
13-04-2006, 10:03 PM
Hint: UpdateTexture. :roll:

chronozphere
14-04-2006, 08:19 PM
I checked MSDN but i still dont understand the exact function of Updatetexture. Why are the source/destenation parameters nessecary and where to put it in the code. This may be a stupid question but i never worked with UpdateTexture. :(

Can someone expain how it works??

Clootie
14-04-2006, 08:45 PM
First you create texture in SYSTEMMEM pool; next you fill it with your data; create texture in POOL_DEFAULT; do pd3dDevice.UpdateTexture(source_tex_in_sys_mem, destin_tex_in_default_pool) call.

That's all! :)

chronozphere
15-04-2006, 05:46 PM
Thanx.. thats the info i needed. I implented Updatetexture and it seems to work now. :razz:

About the for i:=0 to fWidth*2-1 do thing.
I figured out why multiplying with 2 was nessecary.
It's just because i moved one byte instead of two (wich was nessecary because its 16bit mode 8) )

//original wrong code
P := PWord(Dword(P) + 1);

//solution
P := PWord(Dword(P) + 2);

I admit, it's a stupid mistake. :roll: :oops:

Okay.. Transparency seems to work now, even without my own fancy lock/unlock stuff.
Although it's not needed, i will keep it in my engine and use it for texture-pre-processing, 8)

The code below creates a texture from a given file, and sets transparency to the given color. you can also pass a PreProcessor to this procedure.
This allows you to easily manipulate the pixel data.

Here is the code (get ready... Its a lot) :twisted: :


//the procedure type
TBF_PPFunction = function (x,y: integer; TrueColor: boolean; color: cardinal): cardinal;

.....

procedure TBF_Texture.CreateFromFile(FileName: pChar; TransColor: cardinal; Preprocessor: TBF_PPFunction);
var HR: Hresult;
Desc: D3DSurface_Desc; //surface description
x,y: integer;
nFormat: TD3DFORMAT; //current color format
D3DRect: _D3DLOCKED_RECT;
P1: PWord; //pointer for 16 bit graphics
P2: PDword; //pointer for 32 bit graphics
Surf: IDirect3dSurface8; //Direct3d Surface
begin
if FileExists(FileName) then
begin

//determine texture format
if not (fParent.fDisplayMode in [dm640x480x16,dm800x600x16,dm1024x768x16]) then
nFormat := D3DFMT_a8r8g8b8
else
nFormat := D3DFMT_A1R5G5B5;

//create sysmem texture
HR := D3DXCreateTextureFromFileEx(fParent.fD3DDevice,Fil eName,0,0,0,0,
nFormat,D3DPOOL_SYSTEMMEM,D3DX_FILTER_NONE,
D3DX_DEFAULT,Transcolor,nil,nil,fSysTexture);
if Failed(HR) then
begin
ReportError(True,Self,E_SYSTEXTURE_CANNOTMAKE,0);
Exit;
end;

//Retrieve size
fSysTexture.GetLevelDesc(0,Desc);
fWidth := Desc.Width;
fHeight := Desc.Height;

if @Preprocessor <> nil then
begin

//retreving surface level
HR := fSysTexture.GetSurfaceLevel(0,surf);
if Failed(HR) then
begin
ReportError(True,Self,E_TEXTURE_CANNOTGETLVL,HR);
Exit;
end;

//locking surface
HR := Surf.LockRect(D3DRect,nil,D3DLOCk_NOSYSLOCK);
if Failed(HR) then
begin
ReportError(True,Self,E_CANNOT_LOCK_SURFACE,HR);
Exit;
end else
if nFormat = D3DFMT_A1R5G5B5 then
begin
//when in 16 bit mode, use words instead of Dwords
Log('start manipulating pixels');
try
for y:=0 to fHeight do
begin
P1 := PWord(Dword(D3DRect.pBits) + D3DRect.Pitch * y);
for x:=0 to fWidth do
begin
P1^ := Convert8888to1555(Preprocessor(x,y,False,Convert15 55to8888(P1^)));
P1 := PWord(Dword(P1) + 2);
end;
end;
finally
//finally unlock it
Surf.UnlockRect;
end;
end else begin
//do the same when using 32 bit mode
try
for y:=0 to fHeight do
begin
P2 := PDWord(Dword(D3DRect.pBits) + D3DRect.Pitch * y);
for x:=0 to fWidth do
begin
P2^ := Preprocessor(x,y,True,P2^);
P2 := PDWord(Dword(P2) + 4);
end;
end;
finally
Surf.UnlockRect;
end;
end;
end;

//create videomem texture
HR := D3DXCreateTexture(fParent.fD3DDevice,fWidth,fheigh t,0,0,nFormat,D3DPOOL_DEFAULT,fTexture);
if Failed(HR) then
begin
ReportError(True,Self,E_TEXTURE_CANNOTMAKE,0);
Exit;
end;

//update texture
HR := fParent.fD3DDevice.UpdateTexture(fsysTexture,fText ure);
if Failed(HR) then
begin
ReportError(True,Self,E_TEXTURE_UPDATE_FAILED,HR);
Exit;
end;

end else begin
ReportError(True,Self,E_FILE_NOT_FOUND,0);
Exit;
end;
end;

//----------------------------------------------
//the preprocessing function
//----------------------------------------------

function Process(x,y: integer; TrueColor: boolean; color: cardinal): cardinal;
var A,R,G,B: byte;
begin
//if color is transparent, leave it alone
if color = $00000000 then
A := 0
else
//when 32bit mode is active, use the alpha-channel for special fx.
if TrueColor then
A := Byte(x*4)
else
A := 255;

R := GetRValue(Color);
G := GetGValue(Color);
B := GetBValue(Color);
//make the final color
Result := MakeColor(a,r,g,b);
end;

//-----------------------------------------------
//the needed conversion functions
//-----------------------------------------------

function Convert8888To1555(Color: Cardinal): Word;
var R,G,B,A: word;
begin
B := Word(Color) and $01ff;
G := Word(Color shr 8) and $01ff;
R := Word(Color shr 16) and $01ff;
A := Word(Color shr 24) and $0001;

B := B div 8;
G := G div 8;
R := R div 8;

Result := (A shl 15) or (R shl 10) or (G shl 5) or B;
end;

function Convert1555to8888(Color: Word): cardinal;
begin
//checking whether transparent
if ((Color and $8000) = $8000) then
Result := $ff000000
else
Result := $00000000;
//convert RGB data
Result := Result or ((Color and $7C00) shl 9);
Result := Result or ((Color and $03E0) shl 6);
Result := Result or ((Color and $001F) shl 3);
end;

//----------------------------------------------------
//Some other stuff needed in the "Process" function
//WARNING: these functions only work for D3DFMT_A8R8G8B8 color-format
//----------------------------------------------------

//get R component of a color
function GetRValue(Color: Cardinal): byte;
begin
Result := Byte(Color shr 16);
end;

//get G component of a color
function GetGValue(Color: Cardinal): byte;
begin
Result := Byte(Color shr 8);
end;

//get b component of a color
function GetBValue(Color: Cardinal): byte;
begin
Result := Byte(Color);
end;

//make a color from single RGB values
function MakeColor(a,r,g,b: byte): Cardinal; overload;
begin
Result := (a shl 24) or (r shl 16) or (g shl 8) or (b);
end;


You should do this:


InitalizeFromFile('c:\texture.bmp',$ff0000ff,@Proc ess);
//or
InitalizeFromFile('c:\texture.bmp',$ff0000ff,nil);

//use the last one if you dont want to use
//preprocessing


This code still contains some of my own error checking, wich you should remove before using.

This code has also some nice color manipulation and conversion routines, I have done some tests and they seem to work quite well.
feel free to use them for your games. 8)

If anyone has a suggestion, or improvement Please post :razz: