Drawing lots of identical models using vertex buffers?

I ran into a problem that many developers may have found a solution. I have a small project with a floor designed with small cubes (100X100). If I exceed this limit, my game has suffered severe recessions and leagues!

This is how I paint my floor:

//Function to draw my ground ( 100 X 100 X 1)
    public void DrawGround(GameTime gameTime)
    {
        // Copy any parent transforms.
        Matrix[] transforms = new Matrix[this.model.Bones.Count];
        this.model.CopyAbsoluteBoneTransformsTo(transforms);

        //loop 1 cube high
        for (int a = 0; a < 1; a++)
        {
            //loop 100 along cube
            for (int b = 0; b < 100; b++)
            {
                //loop 100 cubic wide
                for (int c = 0; c < 100; c++)
                {
                    // Draw the model. A model can have multiple meshes, so loop.
                    foreach (ModelMesh mesh in this.model.Meshes)
                    {
                        // This is where the mesh orientation is set, as well 
                        // as our camera and projection.
                        foreach (BasicEffect effect in mesh.Effects)
                        {
                            effect.EnableDefaultLighting();
                            effect.World = transforms[mesh.ParentBone.Index] * Matrix.CreateTranslation(this.position);
                            effect.View = this.view;
                            effect.Projection = this.projection;


                        }

                        // Draw the mesh, using the effects set above.
                        mesh.Draw();
                    }
                }
            }
        }
    }

      

I think it's better to use [VertexBuffer] memory on the graphics card, but I haven't found a tutorial for that, what I want to do ...

Can you just give an example of using the VertexBuffer function in my "DrawGround" function? enter image description here Thank you very much!

+3


source to share


1 answer


TL; DR;

Use Hardware Instancing to draw cubes. Use View Frustum Culling if desired to avoid drawing invisible instances. Get started with vertex and index buffers by reading the Rimer tutorial .


Instancing

The idea is that the vertex and index buffers are bound to the device once and then drawn multiple times in the same callback. An instance is a small package of data that contains only information that is unique to a model instance. This instance data is written to an optional vertex buffer and mapped to the device, along with buffers containing the actual vertices and indices.

How can you use this?

In your case, you will have the following setup:

  • IndexBuffer containing the indices of the cube cell
  • VertexBuffer containing the vertices of the mesh cube
  • second VertexBuffer containing the transformation of all cubes you want to draw
  • a custom input layout taking into account the instance data

Since your cubes only differ in position, you don't even need to send complete transformation matrices to the shader. Just send the position of the instance and add it to the position of the vertex. You can also send other data such as color for each instance.

How do you implement it?

A quick google search gave me tons of results for xna instancing, so you should get started. Here are two random results that seem promising:


Refusal of frust

Only part of the meshes in the scene can be visible at any time. The object can be covered by another object or completely out of the player's field of view. The process of removing these invisible meshes before drawing them is called culling. View Frustum Culling refers to the second case:

All volumes that do not overlap with the chamber volume are invisible.

How large is the chamber? The orthographic projection has a limited rectangle shape. With perspective projection, this box narrows, a pyramid that is clipped by the near and far plane; hence, look at truncation.

How can you use this?

Use truncation to identify instances that are not visible in the current frame. Select only those cubes that intersect with your camera's cropping view. Your render load can be reduced by up to 100% when the player is looking at the sky .; -)

You could combine it with a spatial hierarchy ( quadtree or octree ) to reduce the amount of computation of intersecting boxes with fugongs.



How do you implement it?

Xna provides the BoundingBox Structure as well as the BoundingFrustum class . All you have to do is use them.


Small flaw

Combining Frustum Culling and Hardware Instancing can be tricky. Calling instances means you also have to remove them from the instance buffer => which means re-creating everything. Perhaps consider calculating the visibility of the cube and updating the instance buffer only every few frames or when the camera is moving quickly. How and in what form you finally implement these techniques depends on your needs.


I just realized that you want to know how vertex buffers work:

Vertex buffers

Before the triangle can be drawn on the screen, the graphics device must be prepared. This includes binding shaders, samplers, textures and of course geometry data binding. When draw call , the graphics device runs the shaders and writes the output to the render target.

You are currently using built-in XNA abstractions that simplify the process (and take all control). Linking shaders, constants, input layout, etc. Currently hidden in your Effect class, and the Mesh class takes care of the side geometry.

What does mesh.Draw () do?

When creating a mesh, two buffers are created: VertexBuffer and IndexBuffer . These buffers are expensive to create, so it's a good idea to only do this once. When you call the draw method, the buffers are bound to the device and draw is called. The effect states must be set first, as you commented correctly in your code.

But you can also create your own vertex and index buffers:

// Instantiate the VertexBuffer
var vertexBuffer = new VertexBuffer(
    device, 
    VertexPositionColorNormal.VertexDeclaration, 
    vertices.Length, 
    BufferUsage.WriteOnly
);

// Write data to the buffer
vertexBuffer.SetData(vertices);

// Instantiate the IndexBuffer
var indexBuffer = new IndexBuffer(
    device, 
    typeof(int), 
    indices.Length,      
    BufferUsage.WriteOnly
);

// Write data to the buffer
indexBuffer.SetData(indices);

      

... and bind them manually to the device:

device.Indices = myIndexBuffer;
device.SetVertexBuffer(myVertexBuffer);

      


This code was taken directly from the Riemer XNA Tutorials :

Check out the site, it helped me a lot when I first started graphical programming.

+7


source







All Articles