3D系列3.8 使用Triangle Strips提高性能
本章与HLSL无关。我想让读者可以理解如何使用triangle strips。目前为止,我们只绘制了一个三角形。本章会绘制一个街道。我将本章分成5个部分:道路,人行道的左右两侧,人行道本身和墙。需要5个带纹理的矩形,即需要绘制10个带纹理的三角形。要绘制这10个三角形,你可以简单地定义30个顶点,每个顶点包含3D位置和2D纹理坐标,如下图所示:
在图中我只画出了部分顶点。每个三角形有3个顶点,每个顶点相对于相机都是以顺时针方向定义的。由上图你可以看到几乎每个顶点都定义了2到3次!这意味着我们发送到显卡的信息有冗余,所以应该找到一个方法可以减少发送的顶点数量。对这种情况,每个三角形共享前一个三角形的2个顶点,应该使用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文件下载(已下载 1220 次)("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); } } }
发布时间:2010/3/24 下午12:05:32 阅读次数:7120