[Solved] Z-Fighting With Epsilon Perspective Planes, Depth Bug

I’m struggling to find discussion on how DrawPrimitives is intended to be used, particularly in complex uses with varied primitive types and am running into what looks like some depth issues

In my engine I’m batching together all primitives of a particular type and then passing each batch to DrawPrimitives. So all my texture+lighting polys get to DrawPrimitives in one batch, and then all my coloured polys in a separate call, and then all my coloured lines, and so on for each combination of primitive properties I could be using

As it stands my engine is putting out the following

The lines are spatially above the textures but sometimes appear to be occluded, or even glitching like they’re overlapping? The line flickering isn’t the gif compression, it’s like they’re on the same plane but they’re not

And I’m experiencing the same problem for polys too, not just lines

When the other circles grow larger, they are spatially closer to the camera than the purple circle which should be completely occluded

I’ve tried a lot of different settings, trying all combinations of DepthBufferEnable and DepthBufferWriteEnable to no effect, so my assumption has become that I’ve misunderstood the way DrawPrimitives works and it does no batching behind the scenes. Therefore the separate calls render in the order they’re called. But I’m unsure how to proceed in choosing an alternative

If I have to order every primitive manually then wouldn’t I also have to roll my own shader as well or I’ll get errors when polys intersect using BasicEffect?
But that would make BasicEffect kinda useless unless you were using only one primitive type that isn’t textured? On account of having to set effect.Texture before each DrawPrimitive call. And that doesn’t sound right to me so I must be missing something? Is that Texture setting behaviour even ordinary?

This is my main render method where I’m mostly just messing around with settings atm

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.DarkBlue);
    GraphicsDevice.RasterizerState = new RasterizerState()
    {
        FillMode = FillMode.Solid,
        //CullMode = CullMode.None
    };
    GraphicsDevice.BlendState = BlendState.AlphaBlend;
    //GraphicsDevice.DepthStencilState = new DepthStencilState()
    //{
    //    DepthBufferEnable = true,
    //    DepthBufferWriteEnable = true
    //    //DepthStencilState.Default;//new DepthStencilState()
    //};
    //Graphics.ApplyChanges();
    //{
    //    DepthBufferEnable = false,
    //    DepthBufferWriteEnable = false,
    //    CounterClockwiseStencilFunction = CompareFunction.Always,
    //    StencilFunction = CompareFunction.Always,
    //    StencilFail = StencilOperation.IncrementSaturation,
    //    StencilPass = StencilOperation.IncrementSaturation,
    //    CounterClockwiseStencilFail = StencilOperation.IncrementSaturation,
    //    CounterClockwiseStencilPass = StencilOperation.IncrementSaturation,
    //    ReferenceStencil = 0,
    //    StencilEnable = true,
    //    StencilMask = 0,
    //};
    Renderer.Render(Graphics.GraphicsDevice);
    base.Draw(gameTime);
}

And these are each snipped from where we render each primitive type batch
Polys that have texture, colour and normal properties

            if (PolyTexturesPositionColourNormalTexture != null)
            {
                effect.TextureEnabled = true;
                effect.LightingEnabled = true;
                effect.VertexColorEnabled = true;
                renderer.Indices = PolyIndicesPositionColourNormalTexture;
                renderer.SetVertexBuffer(PolyVerticesPositionColourNormalTexture);
                Nullable<TextureReference> currentTexture;
                currentTexture = PolyTexturesPositionColourNormalTexture[0];
                if (currentTexture.HasValue)
                    effect.Texture = currentTexture.Value.Texture;
                int currentOffset = 0;
                //renderer.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, PolyVerticesPositionColourNormalTexture.VertexCount, 0, PolyVerticesPositionColourNormalTexture.VertexCount / 3);
                for (int i = 1; i < PolyTexturesPositionColourNormalTexture.Length; i++)
                {
                    currentTexture = PolyTexturesPositionColourNormalTexture[i];
                    if (currentTexture.HasValue)
                    {
                        foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                        {
                            pass.Apply();
                            renderer.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, currentOffset, (i - currentOffset) / 3);
                        }
                        effect.Texture = currentTexture.Value.Texture;
                        currentOffset = i;
                    }
                }
            }

Polys with just colour

            if (PolyVerticesPositionColourCountSampled > 0)
            {
                effect.TextureEnabled = false;
                effect.LightingEnabled = false;
                effect.VertexColorEnabled = true;
                renderer.Indices = PolyIndicesPositionColour;
                renderer.SetVertexBuffer(PolyVerticesPositionColour);
                //renderer.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, PolyVerticesPositionColour.VertexCount, 0, PolyVerticesPositionColour.VertexCount / 3);
                foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                    renderer.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, PolyVerticesPositionColourCountSampled / 3);
                }
            }

Lines with just colour

            if (LineVerticesPositionColourCountSampled > 0)
            {
                effect.TextureEnabled = false;
                effect.LightingEnabled = false;
                effect.VertexColorEnabled = true;
                renderer.Indices = LineIndicesPositionColour;
                renderer.SetVertexBuffer(LineVerticesPositionColour);
                //renderer.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, LineVerticesPositionColour.VertexCount, 0, LineVerticesPositionColour.VertexCount / 2);
                foreach (EffectPass pass in effect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                    renderer.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, LineVerticesPositionColourCountSampled / 2);
                }
            }

Is my use case roll-my-own-shader territory? Am I doing completely everything wrong? Is the problem elsewhere?
I’m not finding much material talking about using a lot of different primitive types in tandem so struggling to figure out the way forward. Thanks for any help

It should work if you set DepthStencilState = DepthStencilState.Default

What is your camera NearField/FarField? Very extreme values will cause issues like that because you reach the limits of floating point resolution. Try values that bound your world objects as close as possible.

1 Like

That. Was. It!

I was using epsilon as my near plane. Had me doubting my whole renderer! Thanks so much!

For any others, I found a good explanation of why this is the case here:
https://www.sjbaker.org/steve/omniv/love_your_z_buffer.html

1 Like