Alpha Question

Dec 15, 2008 at 10:38 AM
Edited Dec 15, 2008 at 10:44 AM
I have a specific question about using alpha blending.

I'm trying to setup a simple particle system, so I have an RGB texture with a standard circle gradient.

I setup a custom shader such as the one from Tutorial 09, but I've had the pixel shader return the following:

----

float4 color = tex2D(DisplayTextureSampler, texCoord);

//source color, so black = transparent
//since we're using shades of gray, just using .r will do
color.a = color.r;    
return color;

----

That should return a gray with the appropriate alpha component. However, I have no idea how to get this to blend properly using Xen. I defined some custom vertices with position, color and texture coordinate:

public struct ParticleVertex
    {
        public Vector3 position;
        public Vector4 colour;
        public Vector2 tex;

        //constructor
        public ParticleVertex(Vector3 position, Vector4 colour, Vector2 tex)
        {
            this.position = position;
            this.colour = colour;
            this.tex = tex;
        }
    }

But when I render this, the triangles are always rendered black with the color of the pixelshader on top of that, thus not alpha-blending at all. I have called 'state.RenderState.AlphaBlendState = AlphaBlendState.Alpha', and have also tried different combinations of Source/Destination alpha, all with undesirable results.

Here's a screenshot: Image
Coordinator
Dec 15, 2008 at 10:47 AM
Edited Dec 15, 2008 at 10:51 AM

Hi.

I'd suggest you try the following:

Do not enable blending, and then set the pixel shader to output '1.0f - colour.r' as RGB, this way, you will be able to see if the values you are calculating for alpha are what you expect them to be (so black would be alpha of 0, transparent, and white would be 1.0, opaque).

Also, what order are you drawing the objects, transparent objects (such as your quad) should be drawn after all solid objects. It's possible the quad is being drawn first, and blending with the black background. If you have depth writes enabled, then it will be occluding the models behind it. (normally you would turn depth writes off when drawing particle effects, as they are transparent and shouldn't occlude what is behind them.)

Can you show me the code where you set the states and draw the object.

Let me know if this helps, I don't think this is a problem with xen, it's probably just a draw order issue or the alpha values aren't what you expect them to be.

Thanks!

Dec 15, 2008 at 11:16 AM
I'm pretty sure that is the problem.

The alpha appears to work, but occluded the larger model, whilst the alpha blending works over the other two.

I've turned off depth writing as you said and that's at least fixed some of the problem. There's still the issue of the order of drawing I think, since the larger model is always in front of the particles. Here's a screenshot of the current problem:

Image2

As you can see, both smaller models are behind the particle effects, and the pixels are blended correctly. The larger model is also below and behind the particles and other models, but it's pixels are not blended correctly, causing the strange white silhouette of the smaller model, and the particles appear behind the model.

Here's the code I use to draw the particles:

----

public void Draw(DrawState state)
        {
            state.PushRenderState();

            //enable alpha blending
            state.RenderState.AlphaBlend = AlphaBlendState.AdditiveSaturate;
            state.RenderState.DepthColourCull.DepthWriteEnabled = false;      

            //bind the shader
            m_cShader.Bind(state);

            for (int i = 0; i < m_cParticles.Count; i++)
            {
                if (!m_cParticles[i].IsDead())
                {
                    m_cParticles[i].Draw(state);
                }
            }

            state.PopRenderState();
        }

(...)

//particle draw
 Matrix world = Matrix.CreateScale(m_flSize) * Matrix.CreateTranslation( m_vPosition );
 state.PushWorldMatrix( ref world );          

 //draw vertices
 vertices.Draw(state, indices, PrimitiveType.TriangleList);

 state.PopWorldMatrix();

----

How am I supposed to change the order in which objects are drawn btw? All of these objects are in separate IDraw classes, which are added to the DrawTargetScreen dynamically.
Dec 15, 2008 at 12:01 PM
Edited Dec 15, 2008 at 2:42 PM
I've currently resolved the issue by making sure the particle system is added to the draw target last. However, once the game increases in complexity, this will require me to always create 'manager' classes for any object created during runtime (enemies, particle effects, actually pretty much everything) and to make sure I add those managers in the correct drawing order (ergo, the particle manager last, and everything else arbitrarily before)

Is that how you would suggest controlling draw order?

-----

EDIT: Here's what I'm using to implement the previous:

    public class ObjectManager<T>: List<T>, IDraw
    {
        public void Draw(DrawState state)
        {
            for (int i = 0; i < Count; i++)
            {
                ((IDraw)this[i]).Draw(state);
            }
        }

        //this object is never actually drawn, but
        //it's draw call must always be called
        public bool CullTest(ICuller culler)
        {
            return true;
        }
    }

It's not perfect, since people could concievably template the list using non-IDraw derived classes, which would crash most likely. However, it's the only way I could wrap such a list and still derive it from IDraw (for some reason, I can't template IDraw and derive from it at the same time)
Coordinator
Dec 15, 2008 at 8:33 PM

As you might have guessed, I'm leaving it up to to the programmer to implement their own rendering order code. I feel it's something that is very much app-specific.

I know, it's a bit of a cop out, but I feel that enforcing some method would annoy more people than it helps. I like things to be explicit I guess.

As for your list, you could put in 'where T : IDraw', which would make sure all T items are IDraw, or just extend List<IDraw>. The other option is to keep the list as a private member of the class, and just have Add/Remove methods.

See, I would have never though of doing that (extending List<T>) which is a nice example :-)