Alpha to multiple textures during render

Started by
9 comments, last by Fuzzy Mage 16 years, 11 months ago
I'm trying to create a radar to display for a UI that will have a grid that moves with the player as he walks. Since I am using a square grid texture to create this tile effect, I need to alpha out anything that isn't inside the radar frame. The problem I'm having is that not only do I have somehow apply an alpha map to this image during render, I've got to apply alpha maps to multiply images that were not created with alpha maps. I'm seeking some sort of explanation to solve my dilemma, or even better solution to my issue. The project is using managed directx in C#. Free Image Hosting at www.ImageShack.us For a better explanation, this picture shows the frame that will show the visible grid, the four grid images that will be moving up and left, and the opaque areas are what I do not want to be visible.
Advertisement
I assume that what you're asking for is to draw pixels only in a specific area, and not outside it, and not for "alpha" in particular.

You can achieve this using with the stencil buffer, by drawing a circle to set the stencil to a certain value in that area, then draw everything while testing for that value. You can also do a similar thing with the Z buffer (writing a certain Z, then comparing to it).
I've been researching the stencil buffer and am getting really hung up.

I've placed this code around the drawing that I want to be rounded off.

device.Clear(ClearFlags.Stencil, Color.Black, 1, 0);
device.RenderState.StencilEnable = true;
device.RenderState.StencilFunction = Compare.Always;
device.RenderState.StencilPass = StencilOperation.Replace;
device.RenderState.StencilWriteMask = -1;
device.RenderState.ZBufferWriteEnable = false;
device.RenderState.SourceBlend = Blend.One;
device.RenderState.DestinationBlend = Blend.One;
//Draw radar here
device.RenderState.ZBufferWriteEnable = true;
device.RenderState.StencilFunction = Compare.Equal;
device.RenderState.StencilPass = StencilOperation.Keep;
device.RenderState.AlphaBlendEnable = false;
device.RenderState.StencilEnable = false;

Unfortunately this code crashes unless I comment out the Clear call. This makes me think that I have to enable a stencil buffer when the device is created, but the way it's been created looks like this.

PresentParameters presParams = new PresentParameters();
presParams.SwapEffect = SwapEffect.Discard;
presParams.AutoDepthStencilFormat = DepthFormat.D16;
presParams.EnableAutoDepthStencil = true;
presParams.Windowed = true;
device = new Direct3D.Device( adapter , Direct3D.DeviceType.Hardware , this , createFlags , presParams );

I'm not quite sure how I add a stencil buffer to this device or if that autodepth is the stencil buffer. What other reasons could cause a call to clear to crash?

[Edited by - Fuzzy Mage on May 12, 2007 6:38:42 PM]
You have to have an 'S' in the depth-stencil format to get a stencil. It's best to use D24S8, since it's standard.
Assuming this is the correct way to do it, How would I draw a black image with a transparent circle in the center to the stencil buffer?
Then what renderstates do I need to initiate so that it knows I want only the pixels inside of the transparent area to be drawn?

Edit:
device.RenderState.StencilEnable = true; // Enable the use of the stencil
device.Clear(ClearFlags.Stencil, Color.Black, 0, 0); // Clear the stencil
device.RenderState.ColorWriteEnable=0; // Do not draw?
device.RenderState.StencilFunction = Compare.Always; // All pixels?
device.RenderState.StencilPass = StencilOperation.Replace; // Add to stencil?
drawSprite(device,radarAlpha,width,height,new Rectangle(new Point(200,200), new Size(64,64))); // Add this to stencil?
device.RenderState.ColorWriteEnable = ColorWriteEnable.RedGreenBlueAlpha; // reenable drawing
device.RenderState.StencilFunction = Compare.Equal; // tells it to not draw where there's stuff in stencil?
device.RenderState.StencilPass = StencilOperation.Zero; // tells it to not draw where there's stuff in stencil?
drawSprite(device,radarGrid,width,height,new Rectangle(new Point(225,225), new Size(64, 64))); // Draw this
device.RenderState.StencilEnable = false; // Stop using stencil

All this does is just draw both images which confuses me greatly since another post actually said this was working code.

[Edited by - Fuzzy Mage on May 13, 2007 2:21:26 AM]
A few problems and potential problems I see:

- I don't see the stencil ref value being set when writing to the stencil. Set it to a value different than 0, or else there won't be a way to distinguish between the cleared stencil value and the one you write.

- Is alpha testing enabled? If not, then all pixels will be written to the stencil, regardless of alpha, and you won't get the effect you're after.

- IMO StencilOperation.Keep should be used in the second drawing operation, since there's no reason to change the stencil.
Your posts have explained more about what this code does than anything I've googled in the past couple days. I think I'm coming to get a better grasp of what's going on, but I'm still hitting a dead end in that I'm not getting any result from the code.

device.RenderState.StencilEnable = true; // Kickstart the stencil buffer
device.Clear(ClearFlags.Stencil, Color.Black, 0, 0); // Make it clean

device.RenderState.StencilFunction = Compare.Always; // Read it all I suppose
device.RenderState.ReferenceStencil = 1; // Still not sure about this one
device.RenderState.AlphaTestEnable = true; // Make sure it knows what alpha is
//device.RenderState.ColorWriteEnable=0; // If this disables writing to the screen, it doesn't work.

drawSprite(device,radarAlpha,width,height,new Rectangle(new Point(200,200), new Size(64,64))); // My own code which I'll explain at the bottom

device.RenderState.AlphaTestEnable = false; // It's done reading alpha data
//device.RenderState.ColorWriteEnable = ColorWriteEnable.RedGreenBlueAlpha; //This was to reverse the =0 but that didn't work
device.RenderState.StencilFunction = Compare.Equal; // Checking if it's the same
device.RenderState.StencilPass = StencilOperation.Keep; // Keep what's drawn in stencil
drawSprite(device,radarGrid,width,height,new Rectangle(new Point(225,225), new Size(64, 64))); // Read the previous comment for this function
device.RenderState.StencilEnable = false; // We're done

All my functions do is send a call to Sprite.Begin() Sprite.Draw2D() and sprite.end(). I'm not sure if that could be the problem or not.

What's occuring is it's drawing my alpha image and my grid image without any "stenciling" or anything for that matter. Nothing I have altered has caused any effect except it blacked out everything but my ui when I passed ColorWriteEnable = 0 without resetting it to RGBA.
Somewhere you dropped

device.RenderState.StencilPass = StencilOperation.Replace; // Add to stencil?

which should be there before your first drawing operation.


Let me explain the stencil settings a little more:

StencilFunction is a function which can pass or reject pixels. Compare.Always passes all pixels. Compare.Equal passes only pixels that have the reference value (ReferenceStencil).

StencilPass tells how to change the stencil value when a pixel passes the Z test (which all your do). StencilOperation.Replace will set the stencil value for that pixel to the reference value. StencilOperation.Keep will not change the stencil.

For your first drawing operation, you want to set the stencil to a certain value only where you want to draw (or not to draw, depends on what you want). You clear the stencil to 0, set it to write 1 when a pixel which passes, and enable alpha testing, so that transparent pixels will pass, and won't set the stencil. ColorWriteEnable=0 makes sure that even pixels that pass won't be written, so that they'll only set the stencil.

Now that your stencil is full, you set the pass operation to Keep, so that it doesn't change further, and the function to Equal, so that the stencil will only pass those pixels where the stencil buffer has been set to 1.

I hope this makes it clearer.
I've got one little problem remaining. By setting ColorWriteEnable=0 it shouldn't draw anything to the screen from what everything is telling me. By forcing it to 0 everything on my screen goes black except my UI. I'm not sure why, but I'm hoping this is also the reason for why I'm still getting no results from my stencil buffer.


public void drawSprite(Texture texture, int width, int height, Rectangle pos)
{
sprite.Begin(SpriteFlags.AlphaBlend);

sprite.Draw2D(texture, Rectangle.Empty, pos.Size, pos.Point, Color.White);

sprite.End();
}

This is an example of how I'm drawing these sprites. I believe it has something to do with it being Draw2D.

Edit:
I've continued experimenting and discovered that I can stencil out the entire scene except my ui by modifying the initial values. I've even tried replacing my Draw2d with the Draw function with no luck.

[Edited by - Fuzzy Mage on May 14, 2007 5:12:56 AM]
Working beautifully now. All I had to add to SpriteFlags was DoNotModifyRenderState. This also forced me to disable lighting then re-enable it manually, but it's clipping great now.

This topic is closed to new replies.

Advertisement