3D系列3.8 使用Triangle Strips提高性能

本章与HLSL无关。我想让读者可以理解如何使用triangle strips。目前为止,我们只绘制了一个三角形。本章会绘制一个街道。我将本章分成5个部分:道路,人行道的左右两侧,人行道本身和墙。需要5个带纹理的矩形,即需要绘制10个带纹理的三角形。要绘制这10个三角形,你可以简单地定义30个顶点,每个顶点包含3D位置和2D纹理坐标,如下图所示:

TriangleList定义的三角形

在图中我只画出了部分顶点。每个三角形有3个顶点,每个顶点相对于相机都是以顺时针方向定义的。由上图你可以看到几乎每个顶点都定义了2到3次!这意味着我们发送到显卡的信息有冗余,所以应该找到一个方法可以减少发送的顶点数量。对这种情况,每个三角形共享前一个三角形的2个顶点,应该使用TriangleStrip,如下图所示:

TriangleStrip定义的三角形

使用TriangleStrips时你只需每个顶点定义1次。所以对第一个三角形,你需要定义顶点0,1和2。现在,对下一个三角形,你只需添加顶点3!XNA总是会使用最后三个顶点绘制三角形,所以第二个三角形使用顶点1, 2和3。

对第三个三角形,你只需添加顶点4,XNA会使用顶点2,3和4。通式是:第n个三角形由第(n-1),第n和第(n+1)顶点定义,n从1开始。通过这种方式,你可以看到顶点的数量大大减少!当然,在处理大量三角形时可以提高帧率,所以系列1中的地形也可以使用TriangleStrip定义(可参见教程5.8 基于一个顶点缓冲和一个索引缓冲创建一个地形)。

但还有一个问题,如果看一下红色箭头,你会发现不可能以顺时针方向定义所有的顶点,这种情况经常会遇到,所以当使用TriangleStrip时,规则是将顶点定义从顺时针方向切换到逆时针方向,然后在下一个三角形时再切换回来。

下面改变顶点定义:

MyOwnVertexFormat[] vertices = new MyOwnVertexFormat[12];
 
vertices[0] = new MyOwnVertexFormat(new Vector3(-20, 0, 10), new Vector2(-0.25f, 25.0f));
vertices[1] = new MyOwnVertexFormat(new Vector3(-20, 0, -100), new Vector2(-0.25f, 0.0f));
vertices[2] = new MyOwnVertexFormat(new Vector3(2, 0, 10), new Vector2(0.25f, 25.0f));
vertices[3] = new MyOwnVertexFormat(new Vector3(2, 0, -100), new Vector2(0.25f, 0.0f));
vertices[4] = new MyOwnVertexFormat(new Vector3(2, 1, 10), new Vector2(0.375f, 25.0f));
vertices[5] = new MyOwnVertexFormat(new Vector3(2, 1, -100), new Vector2(0.375f, 0.0f));
vertices[6] = new MyOwnVertexFormat(new Vector3(3, 1, 10), new Vector2(0.5f, 25.0f));
vertices[7] = new MyOwnVertexFormat(new Vector3(3, 1, -100), new Vector2(0.5f, 0.0f));
vertices[8] = new MyOwnVertexFormat(new Vector3(13, 1, 10), new Vector2(0.75f, 25.0f));
vertices[9] = new MyOwnVertexFormat(new Vector3(13, 1, -100), new Vector2(0.75f, 0.0f));
vertices[10] = new MyOwnVertexFormat(new Vector3(13, 21, 10), new Vector2(1.25f, 25.0f));
vertices[11] = new MyOwnVertexFormat(new Vector3(13, 21, -100), new Vector2(1.25f, 0.0f));

如你所见,只需用12个顶点就可以定义10个三角形。你应该注意到我使用的水平纹理坐标超出了[0,1]范围,比如-0.25f和1.25f。因为在shader中我们将AdressU和AdressV设置为Mirror,这些点会被映射到0.25f和0.75f,创建了一个镜像效果,如下图所示。相同的技巧也用于竖直坐标,纹理被镜像了25次!如果你没有镜像纹理,小图片会扩展覆盖在整个街道上。

镜像纹理

包含在顶点中的信息没有改变:我们仍然发送每个顶点的位置和纹理信息,所以无需改变VertexDeclaration。

在绘制前,我们改变了相机位置,让它更合理,所以需要在SetUpCamera方法中进行调整:

private void SetUpCamera()
{
    cameraPos = new Vector3(-25, 13, 18);
    viewMatrix = Matrix.CreateLookAt(cameraPos, new Vector3(0, 2, -12), new Vector3(0, 1, 0));
    projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 1.0f, 200.0f);
}

剩下要做的就是在Draw方法中指定要绘制的三角形数量,此处为10,我们使用三角形带定义这些三角形,替代三角形列表:

device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 10); 

好了!本章你学习了减少带宽的另一种方法。

程序截图

HLSL保持不变,所以我只列出了XNA代码:

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace XNAseries3
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        struct MyOwnVertexFormat
        {
            private Vector3 position;
            private Vector2 texCoord;

            public MyOwnVertexFormat(Vector3 position, Vector2 texCoord)
            {
                this.position = position;
                this.texCoord = texCoord;
            }

            public static VertexElement[] VertexElements =
            {
                new VertexElement(0, 0, VertexElementFormat.Vector3, VertexElementMethod.Default, VertexElementUsage.Position, 0),
                new VertexElement(0, sizeof(float)*3, VertexElementFormat.Vector2, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, 0),
            };

            public static int SizeInBytes = sizeof(float) * (3 + 2);
        }

        GraphicsDeviceManager graphics;
        GraphicsDevice device;
        
        Effect effect;
        Matrix viewMatrix;
        Matrix projectionMatrix;
        VertexBuffer vertexBuffer;
        VertexDeclaration vertexDeclaration;
        Vector3 cameraPos;

        Texture2D streetTexture;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);            
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            graphics.PreferredBackBufferWidth = 500;
            graphics.PreferredBackBufferHeight = 500;
            graphics.IsFullScreen = false;
            graphics.ApplyChanges();
            Window.Title = "Riemer's XNA Tutorials -- Series 3";

            base.Initialize();
        }

        protected override void LoadContent()
        {
            device = GraphicsDevice;

           effect = Content.Load ("OurHLSLfile");
           streetTexture = Content.Load ("streettexture");
           SetUpVertices();
           SetUpCamera();
       }

       private void SetUpVertices()
       {

            MyOwnVertexFormat[] vertices = new MyOwnVertexFormat[12];

            vertices[0] = new MyOwnVertexFormat(new Vector3(-20, 0, 10), new Vector2(-0.25f, 25.0f));
            vertices[1] = new MyOwnVertexFormat(new Vector3(-20, 0, -100), new Vector2(-0.25f, 0.0f));
            vertices[2] = new MyOwnVertexFormat(new Vector3(2, 0, 10), new Vector2(0.25f, 25.0f));
            vertices[3] = new MyOwnVertexFormat(new Vector3(2, 0, -100), new Vector2(0.25f, 0.0f));
            vertices[4] = new MyOwnVertexFormat(new Vector3(2, 1, 10), new Vector2(0.375f, 25.0f));
            vertices[5] = new MyOwnVertexFormat(new Vector3(2, 1, -100), new Vector2(0.375f, 0.0f));
            vertices[6] = new MyOwnVertexFormat(new Vector3(3, 1, 10), new Vector2(0.5f, 25.0f));
            vertices[7] = new MyOwnVertexFormat(new Vector3(3, 1, -100), new Vector2(0.5f, 0.0f));
            vertices[8] = new MyOwnVertexFormat(new Vector3(13, 1, 10), new Vector2(0.75f, 25.0f));
            vertices[9] = new MyOwnVertexFormat(new Vector3(13, 1, -100), new Vector2(0.75f, 0.0f));
            vertices[10] = new MyOwnVertexFormat(new Vector3(13, 21, 10), new Vector2(1.25f, 25.0f));
            vertices[11] = new MyOwnVertexFormat(new Vector3(13, 21, -100), new Vector2(1.25f, 0.0f));

            vertexBuffer = new VertexBuffer(device, vertices.Length * MyOwnVertexFormat.SizeInBytes, BufferUsage.WriteOnly);
            vertexBuffer.SetData(vertices);

            vertexDeclaration = new VertexDeclaration(device, MyOwnVertexFormat.VertexElements);
        }

        private void SetUpCamera()
        {
            cameraPos = new Vector3(-25, 13, 18);
            viewMatrix = Matrix.CreateLookAt(cameraPos, new Vector3(0, 2, -12), new Vector3(0, 1, 0));
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 1.0f, 200.0f);
        }

        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime gameTime)
        {            
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.DarkSlateBlue, 1.0f, 0);

            effect.CurrentTechnique = effect.Techniques["Simplest"];
            effect.Parameters["xViewProjection"].SetValue(viewMatrix * projectionMatrix);
            effect.Parameters["xTexture"].SetValue(streetTexture);
            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                device.VertexDeclaration = vertexDeclaration;
                device.Vertices[0].SetSource(vertexBuffer, 0, MyOwnVertexFormat.SizeInBytes);
                device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 10);
                pass.End();
            }
            effect.End();

            base.Draw(gameTime);
        }
    }
}
文件下载(已下载 1220 次)

发布时间:2010/3/24 下午12:05:32  阅读次数:7042

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号