Semi transperent object

Dec 30, 2008 at 6:37 PM
Edited Dec 30, 2008 at 6:38 PM
Hi! I am trying to draw palm. ( model + texture shared here: http://www.fastshare.org/download/Palm.rar )
So i am setting following settings:
            state.RenderState.AlphaBlend = new AlphaBlendState();                                   
            state.RenderState.AlphaBlend.Enabled = true;                            
            state.RenderState.AlphaBlend.SourceBlend = Blend.One;                
            state.RenderState.AlphaBlend.DestinationBlend = Blend.InverseSourceAlpha;
            state.RenderState.AlphaBlend.BlendOperation = BlendFunction.Subtract;

            state.RenderState.AlphaTest.Enabled = true;
            state.RenderState.AlphaTest.AlphaTestFunction = CompareFunction.Greater;

But i have some odd result:
Free Image Hosting at www.ImageShack.us
Any ideas how to fix this?

Sorry if this question is like dumb, but i am new in XNA and Gamedev.



Coordinator
Dec 30, 2008 at 11:35 PM
Edited Dec 30, 2008 at 11:40 PM

Hi.

You have a few problems there.
The first problem is the little black specs at the edge of the leaves. These are simply because of how the texture has been applied to the model, and I can't really do anything about it. It's because the texture has two plam ferns side by side, so you are getting a little bit of overlap.

I'm guessing you are trying to get the tree looking it's best, with soft transparency.
If you look at the texture (say, in photoshop or a similar program) you will see it has 4 channels. The RGB colour channels and an alpha channel. The alpha in this texture is there to say how transparent the parts of the tree should be.

You use 'source alpha blending' (see the documentation for AlphaBlendState structure) when you want to fade between the background pixels (the destination) and the tree pixels being drawn (the source).

 

When AlphaBlending is enabled, the hardware uses the following blend equation:
written pixel = (source pixels * SourceBlend) BlendOperation (destination pixel * DestinationBlend)
So, if you take the values you are using, you get:
written pixel = (tree texture colour * One) Subtract (light blue background colour * InverseSourceAlpha)

or...

written pixel = tree green - light blue * (1 - tree alpha)

In the centre of the leaf, the tree colour is dark green, and the alpha value is 1.

written pixel = dark green - light blue * (1 - 1)
written pixel = dark green - light blue * 0
written pixel = dark green

Does that make sense?

 

So. What you want is a nice transition.

In this case, where the alpha is 1, you want just the tree to show, where the alpha is 0, you want just the background. This is a linear interpolation:

written pixel = tree * tree alpha + background * (1- tree alpha).
You can confirm this for the cases where alpha = 1 and alpha = 0.

 

So, to get that result, you go back to the blend equation:

written pixel = (source pixels * SourceBlend) BlendOperation (destination pixel * DestinationBlend)

You should be able to infer that:

SourceBlend = SourceAlpha
BlendOperation = Add
DestinationBlend = InverseSourceAlpha

 

If you use these settings, you should get the result you want. As a shortcut, the static AlphaBlendState.AlphaBlend has this mode already setup for you:

state.RenderState.AlphaBlend = AlphaBlendState.AlphaBlend; 

 

With that said, if you are trying to do pre-multiplied alpha (are you?) then you need to edit your source image. The idea with pre-multipled alpha is that you you multiply the RGB in the original image with it's own Alpha. I can tell you, this isn't the case with your current image. (Note that 'AlphaBlendState.PremodulatedAlpha' is already setup with this mode)

see here: (the differences are subtle, but important)
http://hungryspoon.com/random/pmalpha.jpg

Dec 31, 2008 at 3:52 AM
Hello!
Thanks for awesome reply!
But it not works correctly :(
Even when i tried to edit sourceimage to be suitable for premodulated alpha i've got bad result.



Any ideas how to fix that? In 3ds max it wokrs perfectly. But after .x export i got that result.
Dec 31, 2008 at 8:11 AM
Ah! I just need depth sorting :)
Thx for answer anyway!
Coordinator
Dec 31, 2008 at 8:19 AM

Yes, that is a depth issue. The transparent leaves still write depth.

There aren't too many ways to fix it (I'm not sure how you have managed it...)

A hacky way is to draw the geometry twice. The first time, disable blending, and pump up the alpha test reference (to say ~240), then draw again as you are currently drawing it (with blending but with depth writes disabled).

So you get 'solid' areas writing to depth, but transparent areas not (and not correctly depth sorted).

Jan 1, 2009 at 10:51 AM
Way how to manage it i found here: http://ziggyware.com/readarticle.php?article_id=164
But i don't know how to convert it to xen. There is rendertarget, and i don't know how to bind my shader to it before rendering my palm.
Ah jee, i am so newbie.
Coordinator
Jan 1, 2009 at 11:05 AM
Edited Jan 1, 2009 at 11:24 AM

That 'article' is a load of rubbish :-) It's the equivalent of doing this:

state.RenderState.AlphaTest.Enabled = true;

But with a shader. I'd highly recommend giving what I suggested above, drawing twice (It's a good compromise - you will never get a perfect result, as per pixel depth sorting is pretty much impossible at runtime). Or just go on to something different and more interesting :-)

With that said, using a custom shader with a model is quite tricky at the moment (it's something I'm looking at making easier).

Jan 1, 2009 at 11:32 AM
Yeah, thank you very much! Your solution is working perfectly!
Here is my final drawing code for palm:

 public void Draw( DrawState state )
        {
            state.PushWorldMatrix( ref _world );

            if(_model.CullTest( state) )
            {
                DeviceRenderState currentState = state.RenderState;
                state.RenderState.AlphaTest.Enabled = true;
                state.RenderState.AlphaTest.ReferenceAlpha = 100;
                state.RenderState.AlphaTest.AlphaTestFunction = CompareFunction.Greater;

                _model.Draw( state );

                state.RenderState.AlphaBlend = AlphaBlendState.Alpha;
                state.RenderState.DepthColourCull.DepthWriteEnabled = false;

                _model.Draw( state );

                state.SetRenderState(ref currentState);
            }

            state.PopWorldMatrix();
        }
/// eof Facepalm.jpg

And here is visual result:



Thanks for help!


Coordinator
Jan 1, 2009 at 12:13 PM
Edited Jan 1, 2009 at 9:56 PM

Great :-)

However, the second time you draw, the AlphaTest reference is still high. So I'd set it back down to 0 before the second draw call. This should make the alpha blending show up, and make the edges nice and feathered. You will then be able to bump up the first pass alpha test a bit higher (say, 150-200). What you are seeing now is the equivalent of just the first render (so just alpha testing).

Also, PushRenderState() and PopRenderState() are easier than using 'currentState'. :-)

Jan 1, 2009 at 10:47 PM
Thanks for tip!
I will use that.
Apr 6, 2009 at 4:36 PM
Hi.  
Does alpha blending work when rendering multiple instances using BatchModel?  Alpha blending seems to work great for me when I just render one ModelInstance but not when I render a BatchModelInstance (using the same ModelData).  Any help would be much appreciated.
Many thanks.
Coordinator
Apr 7, 2009 at 12:03 AM

All actual drawing takes when the BatchModel draws, not when the BatchModelInstance's draw... (When a BatchModelInstance draws, it simply adds itself to the BatchModel object).

So if you are changing the blend model for a BatchModelInstance, then nothing will happen.