[SOLVED] draw order when rendering several instances of a model.

Hello world!

When I draw several instances of my 3d models, like a grid-formation of square terrain pieces, the models are drawn overlapping each other in the order I call draw on them, rather than overlapping as a result of distance to camera…

Do I need to re-sequence my terrainpiece draws to match camera position, by manually drawing the most distant pieces first,
-OR-
is there a clever way of adding each model to a combined scene, which I can then draw?
-I figure there must be a way to draw a combined scene, so scene-wide lights and shadows can work…

-I would be gratefull for any help or relevant links, as I have found it difficult to sniff out good tutorials on this stuff…

Sounds like your depth buffer states are not being set correctly. Try doing as Tom suggests in this thread:

That is the “clever way”, ie make use of the GPU’s built-in depth read/write functionality. The most efficient way to render opaque objects is to sort them from closest to furthest. This is because the pixel shader does not need to run for any pixels already written to the screen with a smaller depth value.

For semi-opaque objects you need to use alpha blending and render them from furthest to closest to avoid sorting artefacts. It’s common to store scene geometry in multiple lists and sort them relative to the camera depending on their opaque/transparent state.

Thank you, but I think I’m un-able to derive anything usefull from the linked posts, other than that the problem in the picture looks familiar compared to mine…

Looking at the discussed tutorial, I see they are working with programmed terrain ie generated vertices… -I am working with a collection of pre-built blender models…

I have tried adding the line by Tom to my code, but it doesnt seem to have any effect…
I can still only render my scene from a certain range of angles… Other-wise things are drawn in reverse, back to front and inverted looking…

I think my problem is I am drawing each model individually in a for-loop…
-I need something like a buffer or complete scene to send them to, which I can then draw after my for-loop…

How do I make a model in the foreground block the view of a model in the back ground?
As soon as I move on to my second model, its like it forgets the first models exsists :slight_smile:

It shouldn’t matter whether the terrain geometry is generated at runtime or comes in as multiple models. The normal way to draw your scene is to loop through your objects and draw them one at a time - the graphics device handles the depth sorting for you (although as I said before you can sort things to help it out).

I think we’ll need to see some of your code, particularly the initialisation and draw routines. So long as you are creating your back buffer with a correct DepthFormat and setting the DepthStencilState before drawing your 3D models, it should be working.

Thank you for your response… I have tried lots of stuff, that I have removed again… Back to square one, here are the relevant code pieces I use for the 3d stuff…
-This code reads my 2d grid map, and places a model for each grid in a square formation in 3d…
You will notice my code has no mention of z-depth buffers or stencils, as I can find no examples with code that even effects my draw method…

LOAD CONTENT:
small_model = content.Load(“small”);
world = Matrix.CreateTranslation(new Vector3(0, 0, 0));
view = Matrix.CreateLookAt(new Vector3(15, 50, -15),new Vector3(0,0,0), Vector3.UnitY);
projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45f),Game1.cam_device.GraphicsDevice.Viewport.AspectRatio, 0.1f, 1000);

DRAW :

cam_device.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);

        for (int y = 1; y< Level.level_height - 1 ; y++) // for (int y = Level.level_height - 1; y > 0; y--)
        {
            for (int x = Level.level_width - 3; x>1; x--)
            {
                {
                    world = Matrix.CreateRotationY(Level.level_Grid[x, y].my_rotation - (float)Math.PI/2) * Matrix.CreateTranslation(new Vector3(15 - x, 0, 15 - y));
                    small_model.Draw(world, view, projection);
                }
            }
        }

To improve your code formating in your posts, prefix all code lines with at least 4 spaces.

You haven’t posted your initialisation routine - I’m now suspecting it’s something to do with that - you need to make sure the device is created with a depth buffer attached. You must set up the presentation parameters before creating (or resetting) the device: http://stackoverflow.com/questions/12427561/xna-4-0-and-unsolvable-by-me-depth-curious-rendering

Other than that, I don’t see the DepthStencilState being set before you render the models. This is a good idea because using SpriteBatch can unset it.

Thanks for the pasting tip…

So, I dont have any code in my initialize methods besides trivial things like variable assignments and such…
All I do relating to graphics is change my back-buffer dimensions…

and in my game1 constructor method, i have:
graphics = new GraphicsDeviceManager(this);

Now, I know how to create presentation parameters, but I cant figure out how to apply them…
-When I try, it tells me the property ‘GraphicsDevice’ is read only…

How exactly do I create (or reset) a device using these presentationparameters?

Sure, this is some of the code from an Android game. I checked and it appears that actual XNA games didn’t need to do this by default, so in theory MonoGame games should not either.

    // Constructor
    public Game()
    {
        graphics = new GraphicsDeviceManager(this);
        graphics.IsFullScreen = true;
        graphics.PreferredBackBufferWidth = Window.ClientBounds.Width;
        graphics.PreferredBackBufferHeight = Window.ClientBounds.Height;
        graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);
    }

    void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
    {
        e.GraphicsDeviceInformation.PresentationParameters.BackBufferFormat = SurfaceFormat.Color;
        e.GraphicsDeviceInformation.PresentationParameters.DepthStencilFormat = DepthFormat.Depth24;
    }

Also, if using SpriteBatch, please make doubly sure you are setting the DepthStencilState right before drawing your terrain models.

Ok, thanks so much, got the constructor and event-handler… Now I just need to implement what you wrote in your last sentance…
Below is the code I use for drawing things. Could you help point out where and how to “set depthstencilstate” ?
-I have been through some trial and error, getting me nowhere…

In draw method, have:

graphics.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
        graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
        graphics.GraphicsDevice.SetRenderTarget(renderTarget);
        renderBatch.Begin();

And then, still in draw, this code for every object/model i want displayed, in a for loop:

     world = Matrix.CreateRotationY(-Level.level_Grid[x, y].my_rotation + (float)Math.PI / 2) * Matrix.CreateTranslation(new Vector3(15 - x, 0, 15 - y));

  graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;

  small_model.Draw(world, view, projection);

And then wrap up the draw-method up with:

 renderBatch.End();
        graphics.GraphicsDevice.SetRenderTarget(null);

        spriteBatch.Begin();
        spriteBatch.Draw(renderTarget,windowSize,Color.White);

        spriteBatch.End();

… Its just drawing things as I call them out, rather than as they should appear in a combined scene… Sheesh:-)

ok, so it draws correctly now… I commented out everything that used my renderBatch (a spriteBatch) and it seems to work…

This has occupied me for a few days now… many hours…

Son of a Batch!

Solved…!

Glad you got it working finally!

Since you already had this line before your model drawing, removing spritebatch calls should not have been necessary. So it would seem something strange is going on.

graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;

This is what Shawn “Teh Shawn” Hargreaves has to say on the matter: http://blogs.msdn.com/b/shawnhar/archive/2010/06/18/spritebatch-and-renderstates-in-xna-game-studio-4-0.aspx

I recommend reading through pretty much all of his blog posts by the way… lots of useful info there: http://www.shawnhargreaves.com/blogindex.html

I think basically it all boils down to me rendering my models to a renderTarget…

When I remove that, I can draw 2d and 3d just fine…

-So this is just a new chapter for me, no longer a problem… Thanks for all the noob-food, now if you can just tell me how to mark this as solved…?

Ah that’s the first I’ve heard of you using a Render Target! You probably need to explicitly give the render target a depth buffer. The constructor with less arguments does not create one by default. Something like:

renderTarget0 = new RenderTarget2D(device, w, h, false, SurfaceFormat.Color, DepthFormat.Depth24, 0, RenderTargetUsage.DiscardContents);

There is no built in way to mark the thread as resolved, but I’ve seen people rename them with a “[SOLVED]” prefix.

Cool, thanks a lot… I’ll do do both :slight_smile:

Yeah, sorry I didnt think that was relevant… Lesson learned… -I tried to spare you as much code as possible, as I am building on top of a larger 2d game… ITs a bit of a mess :slight_smile:

For future people with scimilar problems:
Follow link below for a clean example of drawing various things to various rendertargets…
Shows how to setup the rendertargets with all the correct depth settings… EASY.