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 ("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 阅读次数:7741
