Creating a 2D GUI using Direct3D

This is a discussion on Creating a 2D GUI using Direct3D within the Game Programming forums, part of the General Programming Boards category; I'm creating a 2D GUI using Direct3D to take advantage of things like hardware acceleration and hardware alpha blending.
For ...

Creating a 2D GUI using Direct3D

I'm creating a 2D GUI using Direct3D to take advantage of things like hardware acceleration and hardware alpha blending.

For anyone that's got experience with 2D rendering using Direct3D, I've got a couple of questions.

My GUI elements are packed into square number textures and I draw them to the screen by specifying a source rect from that texture and render them to texured quads. I wanted to have a go at making the rendered windows have an alpha transparency value. It turns out that because at the moment I'm rendering each element of a window directly from a Direct3D texture to the back buffer, I can't specify an alpha value for the whole window easily (only the individual elements). At first you might think all I have to do is specify the same alpha value for each element and it'll work. The problem occurs because some of the areas of a window are drawn over more than once (e.g a button drawn on top of a window background). So if you can imagine, this makes it difficult for me to make the entire window have an alpha value which is consistent.

Does anyone have any ideas on how to do this? I was thinking of maybe contructing the window on an off-screen surface and then rendering it to the back buffer from there, using one alpha value for the entire window. Wouldn't this be quite costly and slow?

I'm pretty much a beginner to D3D so I'm just looking for someone with a bit more experience than me to give me a few pointers.

Are you using the ID3DXSprite Interface? You can try composing your GUI of the Sprite interfaces. Then when you render these elements you can easily specify the alpha value as one of the Draw Method's parameters.

"...the results are undefined, and we all know what "undefined" means: it means it works during development, it works during testing, and it blows up in your most important customers' faces." --Scott Meyers

I decided against using the ID3DXSprite interface since I thought it would be worth learning how to do it manually first.

I can do the rendering to the back buffer with alpha blending fine. My problem is that I have a window made up of various sprites/texture quads (corners, title bar, sides, etc). These are all packed into a texture and I display a window of whatever size I like using these elements to contruct it. I'd like the whole window to have the same alpha value, which is hard to achieve when you're writing to the same screen area twice once you start doing things like drawing controls onto a window (re-drawing over the same area more than once). So I was thinking that I need to be able to draw the pre-constructed window to the back backbuffer in one hit somehow so the whole window can have the same alpha value.

Ok, fair enough. I can respect trying to do it without the D3DX helper library. As long as you don't think it's "too slow" or some other weak excuse. To do it the other way you will need to use some handy alpha render states. Look into these:

D3DRS_ALPHATESTENABLE
D3DRS_ALPHAREF
D3DRS_ALPHAFUNC

First you set the AlphaTestEnable state to TRUE. This will enable per-pixel testing of alpha components. Then you simply set the reference value you are checking against and set the alpha func to whatever is appropriate. That way you won't get artifacts for rendering more than once. Good luck and try to figure this part out on your own. If you try and get stuck let me know and I can help you.

"...the results are undefined, and we all know what "undefined" means: it means it works during development, it works during testing, and it blows up in your most important customers' faces." --Scott Meyers

Thanks for the info MrWizard. I know what you mean about people saying that they don't use utility functions because they're slow... I just wanted to learn how to do textured quads first.

I've got my render states set, and I can render a GUI element from a PNG file with an 8 bit alpha channel ok. I can also set an alpha value for copying my sprites to the back buffer. I've still got the last problem which is a hard one to explain.

[edit] After thinking about what you've said a little more and having a read through the SDK some more (there's so much alpha blending info it's mind boggling...) I think you might have answered this already. I'm still working on it But here goes anyway because I'm not sure.... [/edit]

What I'm trying to do now is to enable my GUI to have window transparency like the Windows 2K/XP GUI can do. So in simplified psuedo-code I'm looking to have a function like this

DrawWindow(int width, int height, int alpha)

and by changing the alpha value it should render the entire window including everything inside it, with 'int alpha' transparency. I can do that fine until I end up drawing over the top of something that's already there. One thing I think I should mention is all my window element textures are all seperate so that I can render any size of window with any controls on it at runtime.

Take this an example. Someone passes a value of 100 for 'int alpha' to DrawWindow. So it copies all the textures for the corners, sides, and centre of the window, etc with an alpha value of 100 to the screen. All is good so far, I've got a translucent window. Then I start drawing the buttons onto the window with an alpha value of 100. Problem is where the buttons are the alpha value has added with what's already rendered behind it. So I can see the window background through the button. What I'm trying to do is make it so the whole window (including everything drawn in the client area of it) has the same alpha value with respect to what's behind it. So where a button is, I should be able to see what's behind the window, but not the background of it's parent window.

I hope that explains it a little better. Sorry for the bad explanations.

I think I understand what you are saying. Let's pretend you don't render any buttons. You just render the GUI 'plain jane' style. This gives you correct results? Then adding the buttons messes it up right because there is already an alpha value there that is getting re-blended. If that is your problem then you need to look at the render states I previously gave you, the answer lies there. If not then I am misunderstanding what you are trying to do.

"...the results are undefined, and we all know what "undefined" means: it means it works during development, it works during testing, and it blows up in your most important customers' faces." --Scott Meyers

You must first check to see if the window in question overlaps another windows extents. If it does then you must turn additive alpha blending off or you will get a double blend. The easiest way to do this is to set a permanent alpha blend value and use that for the entire interface rather than constantly changing alpha values inside of the class. Direct3D allows you to blend according to many many different rules and one of them is constant alpha. Then you could include a slider in the options menu that allowed the user to adjust the opaqueness of all controls.

But your problem right now is you are double blending. The SDK covers some of this and how to get around it - also there are several books on amazon.com which address this problem as well. There is also a way to use the stencil buffer for this...if you are not using it for shadow's etc.

I would attempt to get all menu's up and running prior to doing the alpha blend thing. It's hard enough to get all the buttons to work w/o messing with alpha. Set your system up as I said before - where every menu item is alpha blended according to a pre-set user defined value. Having different alphas for different menus is not a good idea.