0

I've been at this for nearly 10 hours going in circles trying to get Alpha transparency working with the depth buffer. I'm fairly certain what's causing my issue is that I need to draw my transparent models from back-to-front based on how close they are to the camera, but I can't ever seem to get it working properly. Any help would be appreciated, but please, do not offer order-independent transparency.

Image of what it looks like: https://i.stack.imgur.com/UGR3j.png

As you can see, the sphere on the left is bleeding through the observing sphere, and the sphere on the right is blending as it should. The right-sided sphere was drawn first, the observing drawn second, and the left-sided drawn last.

Snips of code:

Each entity has it's own draw function as follows:

public void Draw()
{
    var worldMatrix = Transform *  MathConverter.Convert(entity.WorldTransform);

    model.CopyAbsoluteBoneTransformsTo(boneTransforms);
    foreach (ModelMesh mesh in model.Meshes)
    {
        foreach (BasicEffect effect in mesh.Effects)
        {
            effect.Alpha = alpha;

            //If we have a texture enable it and set it, then remove colour
            if (texture != null)
            {
                effect.TextureEnabled = true;

                //Texture2D ModifiedTexture = new Texture2D()
                effect.Parameters["Texture"].SetValue(texture);

                //But only if colour isn't set
                if (colour == null)
                    effect.AmbientLightColor = Color.White.ToVector3();
            }
            else
            {
                effect.TextureEnabled = false;
                effect.AmbientLightColor = Color.Brown.ToVector3();
            }

            //If colour is set and isn't what we want, thrn set it to the lighting of the model
            if (colour != null && effect.AmbientLightColor != colour.ToVector3())
                effect.AmbientLightColor = colour.ToVector3();

            effect.World = boneTransforms[mesh.ParentBone.Index] * worldMatrix;
            effect.View = MathConverter.Convert((Game as Game1).camera.ViewMatrix);
            effect.Projection = MathConverter.Convert((Game as Game1).camera.ProjectionMatrix);
        }
        mesh.Draw();
    }
}

Draws are called from the main Draw function:

foreach (EntityModel mdl in Globals.MainObjects)
{
    if (mdl.alpha >= 1)
    {
        GraphicsDevice.DepthStencilState = DepthStencilState.Default;
        GraphicsDevice.BlendState = BlendState.Opaque;

        mdl.Draw();
    }
    else
    {
        GraphicsDevice.DepthStencilState = DepthStencilState.Default;
        GraphicsDevice.BlendState = BlendState.AlphaBlend;

        mdl.Draw();
    }
}

And the LinkedList of entities to draw is currently sorted like so:

if (MainObjects.Count() >= 1)
    MainObjects = new LinkedList<EntityModel>(MainObjects.OrderByDescending(i => i.alpha).ThenBy(l => l.entity.InstanceId));

TL;DR I need to sort the models by their depth from the camera but don't know how to do so and am looking for either help in what needs to be done and how it could be done or raw code that can do this.

edit: Currently made a small amount of progress facing a different problem. Sorting by DepthIndex (see below) causes some form of clipping to happen based on the camera's angle.

DepthIndex = Microsoft.Xna.Framework.Vector3.Transform(MathConverter.Convert(entity.Position), MathConverter.Convert((Game as Game1).camera.ViewMatrix * (Game as Game1).camera.ProjectionMatrix)).Z;

Each column is of the same frame with a block that is either rendered with transparency or not at all. When there is transparency some are cut, but when there is not all appear fine.

edit2: Source project by request: (removed)

It's written in VS2013 XNA4.0 .NET-4.5.

Zoryn
  • 21
  • 5

1 Answers1

1

Draw opaque model first and forgot ordering, the dephtbuffer will manage it:

GraphicsDevice.DepthStencilState = DepthStencilState.Default;
GraphicsDevice.BlendState = BlendState.Opaque;

foreach (EntityModel mdl in Globals.MainObjects.Where(m => m.alpha>=1)
{
    mdl.Draw();
}

Use depthstate.Read for draw transparent:

GraphicsDevice.DepthStencilState = DepthStencilState.Read;
GraphicsDevice.BlendState = BlendState.AlphaBlending;

foreach (EntityModel mdl in Globals.MainObjects
      .Where(m => m.alpha < 1 && m.alpha > 0)
      .OrderBy(m => m.DepthIndex))
        {
            mdl.Draw();
        } 

EntityModel.cs / Update method

var worldMatrix = Transform * MathConverter.Convert(entity.WorldTransform);

var camera = (Game as Game1).camera;

DepthIndex = Matrix.Multiply(worldMatrix,MathConverter.Convert(camera.ViewMatrix))
                   .Translation.Z;
Blau
  • 5,742
  • 1
  • 18
  • 27
  • Thanks but I've tried this. It causes some of the models to draw over the transparent wall. (Image: http://i.imgur.com/yU8ljr9.png ) – Zoryn Sep 07 '14 at 15:36
  • And with no sorting what-so-ever I'm back to my original problem. (Image: http://i.imgur.com/6CtvMdi.png ) – Zoryn Sep 07 '14 at 15:52
  • But are some of the models over the transparent wall? are you sure that image is no right? – Blau Sep 07 '14 at 16:21
  • I think when you blend models the order doesn't matter, because pixel final color will be a blend two pixels from each model. You only need to draw opaque first, to fill depth buffer, and draw transparent models at the end with depth buffer in read mode to hide pixels that are behind opaque models. – Blau Sep 07 '14 at 16:24
  • Without using any form of sorting with the code above this is what happens: http://i.imgur.com/Z1nSFn9.png As you can clearly see, the sphere on the right should be **behind** the central sphere, but it is rendering on top. – Zoryn Sep 07 '14 at 16:35
  • it depends on the sphere positions a camera positions, are you sure that right sphere is not in front of the central sphere? Can you share the project? – Blau Sep 07 '14 at 17:21
  • it works well. You was adding new spheres with 0.8f alpha so always will be transparent. Watch this video with mixed opaque and transparent spheres https://www.youtube.com/watch?v=2ruIyEqcOjg#t=70 – Blau Sep 07 '14 at 19:56
  • Would you mind uploading the edited source that you have? Exactly what changes did you make? – Zoryn Sep 07 '14 at 20:05
  • I have commented some code, changed how the camera works, added some debug code,..., but it's similar ... Keys 5 to 0 rotate the model itself.. keys from 1 to 4 rotate the camera... https://dl.dropboxusercontent.com/u/59983887/Survival%20Island.rar – Blau Sep 07 '14 at 20:24
  • The problem with this is that the camera should be first-person and not control the world's rotation matrices. While the alpha blending is solved, that camera won't work for my intentions unless I'm overlooking something. – Zoryn Sep 07 '14 at 20:40
  • The code that change world entity matrices it's not related with the camera, it's placed in the draw code, and it's there to check easilly that enities are drawing well because I didn't get the mouse handling and it was embarrasing to me, but the final result is the same, depth buffer is working well, and alpha model are draw right... you can comment it or set world to matrix.Identity – Blau Sep 07 '14 at 20:53
  • It still seems to blend incorrectly for some reason... I just reenabled the mouse camera and this happens: http://i.imgur.com/lpcjjAV.png and http://i.imgur.com/FhGU60d.png – Zoryn Sep 07 '14 at 21:15
  • sorry, but with 2D images is impossible to me to know if it's doing right, a need a video and rotate or move the scene to take some reference... because that images maybe seems to you that are not right because some perspective effect, but they can be right. – Blau Sep 07 '14 at 21:21
  • See https://www.youtube.com/watch?v=fhR0RrpCiyo - If you compare what it looks like from the starting side (newest models) to what it looks like at the end of the video (oldest models) you can see that the depth buffer is wrong. – Zoryn Sep 07 '14 at 21:34
  • Is true that the result is a bit different but I think is due to light calculations.. it seems that from one side it receives more specular than from the other side... I'm looking for some info and Shawn mentioned in this article the right is sort transparent models from back to front... http://blogs.msdn.com/b/shawnhar/archive/2009/02/18/depth-sorting-alpha-blended-objects.aspx – Blau Sep 07 '14 at 23:04
  • I'm thinking and the reason is that if you panit from front to back, the the far object get more obscured. If you use addtive blending it should works fine... – Blau Sep 07 '14 at 23:06
  • I've looked at that article before and if you re-read the OP I was asking for a method to **sort from back-to-front based on the camera**. I haven't the slightest idea how to properly calculate which models are closest to the camera's view matrix. – Zoryn Sep 07 '14 at 23:09
  • but if you want normal blending you have to sort them by z-index, transform the center of the sphere with the camera view to get the right z in camera space – Blau Sep 07 '14 at 23:11
  • I understand that but **how** do I get their z-index, transform it's center by the camera space, then pass that as something that a LinkedList can sort by? IE a Float – Zoryn Sep 07 '14 at 23:20
  • I've edited the code, and I have added calculation of z, I've supposed that model center is (0,0,0). ( Vertex positions are relative to (0,0,0) ) – Blau Sep 07 '14 at 23:45
  • The model center can by found from EntityModel.entity.CollisionInformation.Position or EntityModel.pos How did you calculate z based on the camera's view? – Zoryn Sep 07 '14 at 23:55
  • is in the edited code... mutiplying by the world matrix by the view matrix and taking translation.z.. I'm not used to calculate Z-Index in camera space but I think is done that way, you can print the z of the entities and check it. – Blau Sep 08 '14 at 00:03
  • Sorry, didn't see it up there. Using that gets me back to my previous problem where their depths still refuse to work behind the transparent floor. See: https://www.youtube.com/watch?v=iUpcNb83EfU PS: Sorry for horrid quality – Zoryn Sep 08 '14 at 00:33
  • That problem is hard to resolve, because the models intersection and that you are testing order against the models centers and the terrain will have different Z's at the coner. One solution maybe divide the terrain into smaller blocks... aonther maybe using depth-peeling http://stackoverflow.com/a/13940142/906404 – Blau Sep 08 '14 at 00:51
  • I've seen that post as well. The terrain cannot be divided into smaller blocks, and no block will have a constant size, texture, position, or colour. I have absolutely no idea how to write a custom HLSL shader or any shader/effect in general, and I'd rather stay away from OIT because it's apparently taxing on performance. I'll look around for OIT effect shaders, thank you for all of your help. – Zoryn Sep 08 '14 at 01:04