5
\$\begingroup\$

I'm trying to render a tree made out of 2 rectangles intersecting in the center at a 90degree angle. The texture has an alpha channel but which ever rectangle gets rendered second causes a weird problem with the transparency.

Here is my rendering code:

public override void render(GraphicsDevice gd, Camera camera)
{
    RasterizerState rs = new RasterizerState();
    rs.CullMode = CullMode.None;
    gd.RasterizerState = rs;
    gd.BlendState = BlendState.AlphaBlend;

    effect.CurrentTechnique = effect.Techniques["TexturedNoShading"];
    effect.Parameters["xWorld"].SetValue(Matrix.CreateTranslation(position));
    effect.Parameters["xView"].SetValue(camera.getViewMatrix());
    effect.Parameters["xProjection"].SetValue(camera.getProjectionMatrix());
    effect.Parameters["xTexture"].SetValue(m_texture);

    foreach (EffectPass pass in effect.CurrentTechnique.Passes)
    {
        pass.Apply();

        gd.Indices = m_indexBuffer;
        gd.SetVertexBuffer(m_vertexBuffer);
        gd.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, m_vertices.Length, 0, m_indices.Length / 3);
    }

    rs = new RasterizerState();
    rs.CullMode = CullMode.CullCounterClockwiseFace;
    gd.RasterizerState = rs;

    gd.BlendState = BlendState.Opaque;
}
\$\endgroup\$

3 Answers 3

5
\$\begingroup\$

It looks to me like depth write is enabled when you draw the trees. The problem is that whichever quad draws first lays down depth as if the whole quad was opaque, when it's not, due to the alpha-blended texture. When the second quad draws, parts of it are culled away that should be visible.

There are a few possible approaches to fix this, each with its own pros and cons:

  • Disable depth writes while drawing alpha-blended geometry. This will fix one artifact while creating another: then the quads will blend together in whatever order you draw them, rather than in back-to-front depth order. Therefore you will have to sort objects and quads back-to-front yourself and draw them in the right order every frame to get correct results. This is expensive and is not always possible to get right, such as when you have intersecting quads.
  • Switch to using alpha test instead of alpha blending. This makes every pixel either completely opaque or completely transparent, depending on whether its alpha is above or below a certain threshold, say 50%. You will get correct depth sorting but all the vegetation will lose the soft edges and have hard, pixellated edges instead, which looks worse (although antialiasing can be applied to these edges).
  • A combination of both of the above: draw the geometry with depth write enabled and a fairly high alpha test, such as 80%, then draw again with depth write disabled and using normal alpha blending. You'll get correct depth sorting for the pixels that are above 80% alpha, which is most of them, plus the soft edges that alpha blending gives you, but if you look closely there will be artifacts where the soft edges are not depth sorted correctly, causing hard edges to reappear. These artifacts may not be very noticable, though, depending on your scene.
  • Use a full-powered order-independent transparency technique, such as depth peeling. You can read about these on the web; just search "order-independent transparency". These tend to be very expensive, but they will give you correct sorting for every pixel with full alpha blending support.
\$\endgroup\$
5
\$\begingroup\$

Your problem lies in the Z-Buffer. When you draw transparent polygons, depth writing must be disabled to achieve correct transparency, or the polygons must be ordered back-to-front so they correctly overlap. But, since you are drawing a concave object (one plane intersects the other) and it has transparent and opaque sections, both of these methods can't be used.

What you are looking for is Alpha Testing, a GPU mode that performs the same operations as Depth Testing, but based on the pixel's transparency, not depth. By the syntax of your code, I assume you are using Riemer's tutorials, and he has written one about alpha blended billboards. That tutorial was made for XNA 3.1, and since RenderState.AlphaBlendEnable was removed in 4.0, you must use your own alpha testing shader or use the default one.

\$\endgroup\$
2
\$\begingroup\$

Thanks Nathan and r2d2rigo, the alphatest did the trick. And for the googlers the code that worked is:

    public override void render(GraphicsDevice gd, Camera camera)
    {
        AlphaTestEffect e = (AlphaTestEffect)effect;

        RasterizerState rs = new RasterizerState();
        rs.CullMode = CullMode.None;
        gd.RasterizerState = rs;

        e.View = camera.getViewMatrix();
        e.Projection = camera.getProjectionMatrix();
        e.World = Matrix.CreateTranslation(position);
        e.Texture = m_texture;

        foreach (EffectPass pass in e.CurrentTechnique.Passes)
        {
            pass.Apply();

            gd.Indices = m_indexBuffer;
            gd.SetVertexBuffer(m_vertexBuffer);
            gd.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, m_vertices.Length, 0, m_indices.Length / 3);
        }



        rs = new RasterizerState();
        rs.CullMode = CullMode.CullCounterClockwiseFace;
        gd.RasterizerState = rs;
    }

effect is an AlphaTestEffect that was cast to an Effect and back to an AlphaTestEffect

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Remember to upvote and accept answers that help. Also it is better to post your correct code at the bottom of your question. \$\endgroup\$ Commented Dec 26, 2011 at 11:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .