質問

I have a textured cube in XNA which I have taken from a tutorial online. The only thing is, every time I render many of these cubes, it lags. A friend told me I could reduce the number of vectors I am using for my cube to 8 (one for each corner) and use indices to create the triangles of the cube. So i tried to do this, however, I admit I don't really have a clue how, especially when it's asking for a Vector2 for the texture.

I have backface culling enabled for the graphics device.

I've checked out Riemer's tutorials but they don't help me. I'm a visual learner, and reading through a long article on vertices and indices is not as enticing as watching a YouTube video with explanations. Please can someone advise me on what to do to:

  • Improve the performance of my game when rendering more cubes.
  • Render cubes with multiple textures.
  • "Combine" cubes into a "Chunk" so as to reduce the number of vertices that are drawn per cube.

My cube grabs a texture from a texture map depending on the ID I specify, and this works fine. I've used a tutorial titled "Constructing, Drawing and Texturing a cube with vertices in XNA on Windows Phone 7"; a google search shows the majority of the code I am using to create my cube.

I've wasted a majority of the day staring blankly and generating a headache trying to get the whole code going through my head. The concept is simple, but the implementation is difficult (for me at least).

EDIT: I have reduced the number of vectors to 8 successfully, and have drawn the cube. However, I cannot draw a texture to each vector, so that scratches that idea. My alternative to this method is using color instead of texture, but that's not something I want to resort to just yet. Drawing each cube with just 8 vectors still resulted in the same amount of lag... So for now, I've gone back to using 36 vectors to keep my textures where they should be. I'd still like to do the 3 things i listed above.

EDIT 2: As requested, my rendering code is shown below. I am passing a texture to each cube in its constructor using the following:

public Block(Vector3 position, int BlockID)
{
    Position = position;
    Texture = Assets.GetBlockTexture(@"Textures/terrain", BlockID);
    Size = new Vector3(1, 1, 1);
}

The Assets.GetBlockTexture basically returns a specific 16x16 "block" texture from a larger 256x256 texture map, based on the block ID passed to it (yes, very much like Minecraft before 1.4)

In my game's Draw method is the following:

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    var s = new DepthStencilState {DepthBufferEnable = true};
    spriteBatch.Begin(SpriteSortMode.Texture, BlendState.AlphaBlend, SamplerState.PointClamp, s, RasterizerState.CullCounterClockwise);

    foreach (var block in blocks) block.Draw(GraphicsDevice, camera, effect);

    spriteBatch.End();

    base.Draw(gameTime);
}

Which calls each blocks' Draw method:

public void Draw(GraphicsDevice device, Camera camera, BasicEffect effect)
{
    effect = new BasicEffect(device)
                    {
                        TextureEnabled = true,
                        Texture = Texture,
                        View = camera.View,
                        Projection = camera.Projection,
                        World = Matrix.Identity
                    };

    effect.EnableDefaultLighting();

    foreach (var pass in effect.CurrentTechnique.Passes)
    {
        pass.Apply();
        Render(device);
    }
}

Which then calls the block's Render method:

public void Render(GraphicsDevice graphicsDevice)
{
    if (!isConstructed) ConstructBlock();

    using (var buffer = new VertexBuffer(graphicsDevice, VertexPositionNormalTexture.VertexDeclaration, NUMVERTICES, BufferUsage.WriteOnly))
    {
        buffer.SetData(vertices);
        graphicsDevice.SetVertexBuffer(buffer);
    }

    graphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, NUMTRIANGLES);
}

The ConstructBlock method creates the 36 vertices with the correct texture co-ordinates:

private void ConstructBlock()
{
    vertices = new VertexPositionNormalTexture[NUMVERTICES];

    // Calculate the position of the vertices on the top face.
    var topLeftFront = Position + new Vector3(-1.0f, 1.0f, -1.0f) * Size;
    var topLeftBack = Position + new Vector3(-1.0f, 1.0f, 1.0f) * Size;
    var topRightFront = Position + new Vector3(1.0f, 1.0f, -1.0f) * Size;
    var topRightBack = Position + new Vector3(1.0f, 1.0f, 1.0f) * Size;

    // Calculate the position of the vertices on the bottom face.
    var btmLeftFront = Position + new Vector3(-1.0f, -1.0f, -1.0f) * Size;
    var btmLeftBack = Position + new Vector3(-1.0f, -1.0f, 1.0f) * Size;
    var btmRightFront = Position + new Vector3(1.0f, -1.0f, -1.0f) * Size;
    var btmRightBack = Position + new Vector3(1.0f, -1.0f, 1.0f) * Size;

    // Normal vectors for each face (needed for lighting / display)
    var normalFront = new Vector3(0.0f, 0.0f, 1.0f) * Size;
    var normalBack = new Vector3(0.0f, 0.0f, -1.0f) * Size;
    var normalTop = new Vector3(0.0f, 1.0f, 0.0f) * Size;
    var normalBottom = new Vector3(0.0f, -1.0f, 0.0f) * Size;
    var normalLeft = new Vector3(-1.0f, 0.0f, 0.0f) * Size;
    var normalRight = new Vector3(1.0f, 0.0f, 0.0f) * Size;

    //UV texture coordinates
    var textureTopLeft = new Vector2(1.0f * Size.X, 0.0f * Size.Y);
    var textureTopRight = new Vector2(0.0f * Size.X, 0.0f * Size.Y);
    var textureBottomLeft = new Vector2(1.0f * Size.X, 1.0f * Size.Y);
    var textureBottomRight = new Vector2(0.0f * Size.X, 1.0f * Size.Y);

    //FRONT face.
    vertices[0] = new VertexPositionNormalTexture(topLeftFront, normalFront, textureTopLeft);
    vertices[1] = new VertexPositionNormalTexture(btmLeftFront, normalFront, textureBottomLeft);
    vertices[2] = new VertexPositionNormalTexture(topRightFront, normalFront, textureTopRight);
    vertices[3] = new VertexPositionNormalTexture(btmLeftFront, normalFront, textureBottomLeft);
    vertices[4] = new VertexPositionNormalTexture(btmRightFront, normalFront, textureBottomRight);
    vertices[5] = new VertexPositionNormalTexture(topRightFront, normalFront, textureTopRight);

    //BACK face.
    vertices[6] = new VertexPositionNormalTexture(topLeftBack, normalBack, textureTopRight);
    vertices[7] = new VertexPositionNormalTexture(topRightBack, normalBack, textureTopLeft);
    vertices[8] = new VertexPositionNormalTexture(btmLeftBack, normalBack, textureBottomRight);
    vertices[9] = new VertexPositionNormalTexture(btmLeftBack, normalBack, textureBottomRight);
    vertices[10] = new VertexPositionNormalTexture(topRightBack, normalBack, textureTopLeft);
    vertices[11] = new VertexPositionNormalTexture(btmRightBack, normalBack, textureBottomLeft);

    //TOP face.
    vertices[12] = new VertexPositionNormalTexture(topLeftFront, normalTop, textureBottomLeft);
    vertices[13] = new VertexPositionNormalTexture(topRightBack, normalTop, textureTopRight);
    vertices[14] = new VertexPositionNormalTexture(topLeftBack, normalTop, textureTopLeft);
    vertices[15] = new VertexPositionNormalTexture(topLeftFront, normalTop, textureBottomLeft);
    vertices[16] = new VertexPositionNormalTexture(topRightFront, normalTop, textureBottomRight);
    vertices[17] = new VertexPositionNormalTexture(topRightBack, normalTop, textureTopRight);

    //BOTTOM face. 
    vertices[18] = new VertexPositionNormalTexture(btmLeftFront, normalBottom, textureTopLeft);
    vertices[19] = new VertexPositionNormalTexture(btmLeftBack, normalBottom, textureBottomLeft);
    vertices[20] = new VertexPositionNormalTexture(btmRightBack, normalBottom, textureBottomRight);
    vertices[21] = new VertexPositionNormalTexture(btmLeftFront, normalBottom, textureTopLeft);
    vertices[22] = new VertexPositionNormalTexture(btmRightBack, normalBottom, textureBottomRight);
    vertices[23] = new VertexPositionNormalTexture(btmRightFront, normalBottom, textureTopRight);

    //LEFT face.
    vertices[24] = new VertexPositionNormalTexture(topLeftFront, normalLeft, textureTopRight);
    vertices[25] = new VertexPositionNormalTexture(btmLeftBack, normalLeft, textureBottomLeft);
    vertices[26] = new VertexPositionNormalTexture(btmLeftFront, normalLeft, textureBottomRight);
    vertices[27] = new VertexPositionNormalTexture(topLeftBack, normalLeft, textureTopLeft);
    vertices[28] = new VertexPositionNormalTexture(btmLeftBack, normalLeft, textureBottomLeft);
    vertices[29] = new VertexPositionNormalTexture(topLeftFront, normalLeft, textureTopRight);

    //RIGHT face. 
    vertices[30] = new VertexPositionNormalTexture(topRightFront, normalRight, textureTopLeft);
    vertices[31] = new VertexPositionNormalTexture(btmRightFront, normalRight, textureBottomLeft);
    vertices[32] = new VertexPositionNormalTexture(btmRightBack, normalRight, textureBottomRight);
    vertices[33] = new VertexPositionNormalTexture(topRightBack, normalRight, textureTopRight);
    vertices[34] = new VertexPositionNormalTexture(topRightFront, normalRight, textureTopLeft);
    vertices[35] = new VertexPositionNormalTexture(btmRightBack, normalRight, textureBottomRight);

    isConstructed = true;
}

There are only 288 triangles being rendered on the screen with 10 blocks, yet it's drawing at a whopping 1-3FPS. I know it's not my graphics card causing the problem, seeing as it's able to play many 3D games without any problem.

Updated code as requested by Nico:

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    var s = new DepthStencilState {DepthBufferEnable = true};
    spriteBatch.Begin(SpriteSortMode.Texture, BlendState.AlphaBlend, SamplerState.PointClamp, s, RasterizerState.CullCounterClockwise);

    foreach (var block in blocks) block.Draw(camera, effect);

    spriteBatch.DrawString(Content.Load<SpriteFont>("SpriteFont1"), "Number of triangles: " + triangles, new Vector2(10, 10), Color.White);

    spriteBatch.End();

    base.Draw(gameTime);
}

Block.cs:

private VertexPositionNormalTexture[] vertices;
private VertexBuffer buffer;
private GraphicsDevice device;

public Block(GraphicsDevice device, Vector3 position, int BlockID)
{
    this.device = device;
    Position = position;
    Texture = Assets.GetBlockTexture(@"Textures/terrain", BlockID);
    Size = new Vector3(1, 1, 1);
    buffer = new VertexBuffer(device, VertexPositionNormalTexture.VertexDeclaration, NUMVERTICES, BufferUsage.WriteOnly);
}

public void Render()
{
    if (!isConstructed) ConstructBlock();

    buffer.SetData(vertices);
    device.SetVertexBuffer(buffer);

    device.DrawPrimitives(PrimitiveType.TriangleList, 0, NUMTRIANGLES);
}

public void Draw(Camera camera, BasicEffect effect)
{
    effect = new BasicEffect(device)
                 {
                     TextureEnabled = true,
                     Texture = Texture,
                     View = camera.View,
                     Projection = camera.Projection,
                     World = Matrix.Identity,
                     PreferPerPixelLighting = false
                 };

    effect.EnableDefaultLighting();

    foreach (var pass in effect.CurrentTechnique.Passes)
    {
        pass.Apply();
        Render();
    }
}

The rest of the code is pretty much the same.

役に立ちましたか?

解決

I finally managed to overcome this problem, with such a simple solution.

I created one BasicEffect in the main game class, which would be passed on to my block class. This didn't work at first because I was unsure as to how BasicEffect would work, and so I wasn't aware that you could declare the view, projection and texture differently when it was passed.

So, to start, I initialized the effect's basic modifiers:

effect = new BasicEffect(GraphicsDevice)
{
    TextureEnabled = true,
    World = Matrix.Identity,
    PreferPerPixelLighting = false
};

effect.EnableDefaultLighting();

And in my block's draw method, after passing the effect to it, i did the following:

effect.Texture = Texture;
effect.View = camera.View;
effect.Projection = camera.Projection;

So a new effect is not created every frame

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top