您的位置:首页 > 其它

[翻译]XNA 3.0 Game Programming Recipes之thirty-four

2009-08-08 21:28 423 查看
PS:自己翻译的,转载请著明出处格 5-12 创建你自己的顶点格式问题
顶点被用来保存数据,你发送从你的XNA工程到你的图形卡中的着色器中。一个顶点格式包含一个数据存储描述在你的顶点中。XNA框架伴随一些默认的顶点格式,从简单的VertexPositionColor到VertexPositionNormalTexture格式。
不过,如果你已经编写了很多高级HLSL效果(如果爆炸效果,蒙皮,粒子效果等),你将会发现每个顶点将需要保存一些额外的信息,如切线在顶点或者某些时间数据。包装每个顶点的额外数据要求你去顶点一个自定义顶点格式。因此,你需要去定义一个自定义顶点格式只在当编写代码自定义顶点和象素着色器。
解决方案
顶点格式定义哪种数据被存储在每个顶点内,因而哪种数据将会很容易的给你用顶点着色器。例如,当你使用VertexPositionColor顶点,这个VertexPositionColor结构的顶点格式通知你的图形卡,所有传入的顶点将会携带位置和颜色数据,并指定这些数据能在数据流中找到。
这顶点格式是连接在你XNA代码的顶点和你的顶点着色器之间。首先,顶点格式描述哪个数据应该被布置在数据流中。这是显示在图5-24灰度曲线,指示哪里的位置和颜色顶点数据被布置在数据流中。
每当显卡需要去绘制三角形从你的顶点,这图形卡需要能够分开这个数据流回到顶点。
此外,它同样需要分每个顶点回到位置和颜色数据。再一次,这个顶点格式通知这个图形卡,它应该剪去数据流。这是被低灰度曲线在图5-24显示那样。
最后。为每一个重建顶点,这顶点着色器被调用用可用到的顶点的位置和颜色数据到顶点着色器中。


它是如何工作的
在这节,你将创建你自己的VertexPositionColor版本结构去学会基础。在第二部分,你将会扩展它成为一个新的,自定义顶点格式。
Re-creating the VertexPositionColor Struct
在这部分,你将会创建MyVertexPositionColor结构(它是和VertexPositionColor结构相同的)去了解基本知识。
VertexPositionColor结构被设计,这样它有保存所有必要的信息的能力,这节会有描述。它由三部分组成:
1。为每一顶点它自己的数据,它是一个Vector3去保存位置数据和一个颜色。
2。一个顶点的总大小,这样你的图形卡能很漂亮的剪切数据流分成单独的顶点。
3。VertexElements的描述,它是需要被通知图形卡哪种数据是包含在每个顶点和图形卡应该被剪切在顶点内去获得这些数据。
第一步
开始添加结构:
1 public struct MyVertexPositionColor
2 {
3 public Vector3 Position;
4 public Color Color;
5 public MyVertexPositionColor(Vector3 position,Color color)
6 {
7 Position=position;
8 Color=color;
9 }
10 } 这个结构有存储一个Vector3的能力为位置和颜色。这可能是足够去创建顶点和传送它们到图形卡中。
第二步
想象一个顶点流传送到图形卡中,你的图形卡可能有一个相当困难时期试图找出哪里分字节流成顶点。它需要知道字节的数量被一个顶点所占用,这样它能很好剪切流成为独立的顶点。添加这行到你的结构中:

1 public static readonly int SizeInBytes=sizeof(float)*(3+1); 由于这些信息是你基于这个结构创建的所有顶点都是相同的,你只需要读它们,你可以使它static readonly。
每个顶点包含一个Vector3和一个颜色。一个Vector3是有三个浮点型组成,同时一个颜色被存储成一个浮点型。这样,字节的总数占据一个顶点相当于(一个浮点所占用的字节数量)*(3+1)。
注意:一个浮点占据4个字节,这样你同样能取代这个值用4*(3+1)=16。确保你看着图5-24去核实顶点包含16字节和每个新顶点开始一个字节数量,这是16的倍数。显卡如何知道哪里去剪切字符流成单一的顶点。
由于第二的结果,你的图形卡将会知道如何去裁减字符流,它接收划分成独立的顶点。
第三步
最后,添加这个代码到你的结构中:
1 public static readonly VertexElement[] VertexElement=
2 {
3 new VertexElement(0,0,VertexElementFormat.Vector3,VertexElementMethod.Default,VectorElementUsage.Position,0),new VertexElement(0,sizeof(float)*3,VertexElementFormat.Color,VertexElementMethod.Default,VertexElementUsage.Color,0),
4 }; 这个信息描述数据的种类包含在一个顶点里,在顶点字节它应该被布置在哪。第一个条目表明每个顶点有位置数据,第二表明每个顶点有颜色数据。让我们一个个讨论每一行的变量。
这一个参数表明该数据流上的数据能被发现。只有高级程序会使用多个顶点流,所以通常你在这里使用0。
提示:多顶点流是非常有用的。例如,如果颜色数据要求经常更新同时位置数据保持不变。由于分开数据成两个顶点流,位置数据能被保持不变在GPU中,所以你必须转换颜色数据从CPU到GPU。
第二个参数描述哪里能找到数据。它是数据的索引在一个顶点中。由于位置数据是第一个数据在每个顶点中,你表明它能被索引0发现。颜色数据,尽管,在位置数据能被发现,所以你需要知道多少字节位置数据被占用。这个位置数据占用三个浮点型,这样你指示(float)*3的大小(由于一个浮点占据4个字节,你同样能指示12)。看看图5-24:颜色数据从12字节开始。
第三个参数表明哪种格式数据需要被保存在数据流中,是基本数据类型。为了位置,你声明Vector3,为了颜色你指示颜色类型。
下一个参数是被用来只在高级和特别硬件扩展中,如N-Patching,一些三角形的几何结构进行调整的基础上,使正常的整体着色质量几何可以增加。
第五个参数非常重要,由于它指示哪个顶点着色器的输入这数据应该被连接。再看看图5-24,注意输入顶点着色器的参数。两个参数被POSITION0和COLOR0所followed.在这个例子中,你连接第一部分的数据,包含位置数据,到POSITION0句法。第二数据部分连接到COLOR0句法。
最后一个参数允许你指定多个每个句法版本。它实际上引用0在POSITION0的结束和COLOR0句法。这方面的一个例子显示这节的第二部分。
作为第3步的结果,你的图形卡将知道哪里去找到每个顶点内的数据。在这个例子中,它将传递字节0到11(3个浮点)到POSITION0顶点着色器的输入。字节12到15(1浮点)将会被传递到COLOR0顶点着色器的输入。
The Complete MyVertexPositionColor Struct
此时这是你应该有的:
1 public struct MyVertexPositionColor
2 {
3 public Vector3 Position;
4 public Color Color;
5 public MyVertexPositionColor(Vector3 position,Color color)
6 {
7 Position=position;
8 Color=color;
9 }
10 public static readonly VertexElement[] VertexElement=
11 {
12 new VertexElement(0,0,VertexElementFormat.Vector3,VertexElementMethod.Default,VertexElementUsage.Position,0),
13 new VertexElement(0,sizeof(float)*3,VertexElementFormat.Color,VertexElementMethod.Default,VertexElement.Color,0)
14 };
15 public static readonly int SizeInBytes=sizeof(float)*(3+1);
16 }Using the MyVertexPositionColor Struct
随着结构准备被使用,你能定义顶点和返回它们到一个VertexBuffer流,如5-4节所解释。这时,你已经使用你的自己顶点格式:
1 private void InitVertices()
2 {
3 myVertexDeclaration=new VertexDeclaration(device,MyVertexPositionColor.VertexElements);
4 MyVertexPositionColor[] vertices=new MyVertexPositionColor[3];
5 int i=0;
6 vertices[i++]=new MyVertexPositionColor(new Vector3(1,1,-1),Color.Red);
7 vertices[i++]=new MyVertexPositionColor(new Vector3(3,5,-1),Color.Green;
8 vertices[i++]=new MyVertexPositionColor(new Vector3(5,1,-1),Color.Blue);
9 vertBuffer=new VertexBuffer(device,MyVertexPositionColor.SizeInBytes*vertices.Length,BufferUsage.WriteOnly);
10 vertBuffer.SetData<MyVertexPositionColor>(vertices,0,vertices.Length);
11 } 这第一行创建VertexDeclaration,没有任何比你在前面第三步编写的多。它将会被传递到你的图形卡在绘制这三角形之前。
方法的中间部分创建一个数组保持有三个MyVertexPositionColors,定义另外的没用的三角形。每一个顶点,你存储位置和颜色。为了创建一个VertexBuffer基于这个数组,你需要指定一个顶点占用多少字节,所以你传递在MyVertexPositionColor.SizeInBytes里。
当它来自绘制这个三角形从VertexBuffer,你需要这个代码,如5-4节所解释:
1 basicEffect.Begin();
2 foreach(EffectPass pass in basicEffect.CurrentTechnique.Passes)
3 {
4 pass.Beign();
5 device.VertexDeclaration=myVertexDeclaration;
6 device.Vertices[0].SetSource(verBuffer,0,MyVertexPositionColor.SizeInBytes);
7 device.DrawPrimitives(PrimitiveType.TriangleList,0,1);
8 pass.End();
9 }
10 basicEffect.End(); 在你绘制三角形之前,你需要传递VertexElements到图形卡,这样它知道如何正确分字节流成有用的数据。
自定义顶点格式
作为第二个例子,你将创建一个新的顶点格式,保存一个位置的能力,一个纹理坐标,和一个额外的Vector4.这允许你去包装四个额外值用每个顶点传送从你的XNA工程到你的顶点着色器;图5-25显示两个这样的顶点作为字节流。



这是新的结构,在这里,您可以再次确定的三个主要部分组成:
1 public struct MyCustomVertexFormat
2 {
3 public Vector3 Position;
4 public Vector2 TexCoords;
5 public Vector4 Extra;
6 public MyCustomVertexFormat(Vector3 Position,Vector2 TexCoords,Vector4 Extra)
7 {
8 this.Position=Position;
9 this.TexCoords=TexCoords;
10 this.Extra=Extra;
11 }
12 public static readonly VertexElement[] VertexElements=
13 {
14 new VertexElement(0,0,VertexElementFormat.Vector3,VertexElementMethod.Default,VertexElementUsage.Position,0),
15 new VertexElement(0,sizeof(float)*3,VertexElementFormat.Vector2,VertexElementMethod,Default,VertexElementUsage.TextureCoordinate,0),
16 new VertexElement(0,sizeof(float)*(3+2),VertexElementFormat.Vector4,VertexElementMethod.Default,VertexElementUsage.TextureCoordinate,1),
17 };
18 public static readonly int SizeInBytes=sizeof(float)*(3+2+4);
19 } 顶部部分允许每个顶点保存一个位置,一个纹理坐标,一个额外的Vector4.
下一步,你连接它们到你的顶点着色器的输入。你指示这第一Vector3应该被连接到POSITION0语法。由于它是第一个数据项目,它能被发现在0字节(第二个参数)。
第二行指示Vector2包含纹理坐标应该连接到TEXCOORD0顶点着色器的输入。这位置占用三个浮点,所以纹理坐标能被找到在sizeof(float)*3=12,在图5-25中被核实。
第三行连接额外的Vector4到另外的纹理句法,由于这些能被用来传递额外数据,直到TEXTURE0 intrinsic已经被使用,你连接它到TEXTURE1 intrinsic通过指定1作为最后的参数。
这Vector4是之前的位置和纹理坐标数据,占据三和两个浮点,分别的,所以额外的Vector4能被发现在字节数据sizeof(float)*(3+2)=20,它再次显示在图5-25中。
最后,你指示一个顶点占据一总数sizeof(float)*(3+2+4)=36字节(位置的三个浮点,两个纹理坐标,四额外的Vector4)
Defining Vertices of Your Custom Format
这个代码创建VertexDeclaration和一个VertexBuffer包含你自定义顶点格式的三个顶点:
1 myVertexDeclaration=new VertexDeclaration(device,MyCustomVertexFormat.VertexElements);
2 MyCustomVertexFormat[] vertices=new MyCustomVertexFormat[3];
3 int i=0;
4 vertices[i++]=new MyCustomVertexFormat(new Vector3(1,1,-1),new Vector2(0,1),new Vector4(-1.0f,0.5f,0.3f,0.2f));
5 vertices[i++]=new MyCustomVertexFormat(new Vector3(3,5,-1),new Vector2(0.5f,0),new Vector4(0.8f,-0.2f,0.5f,-0.2f));
6 vertices[i++]=new MyCustomVertexFormat(new Vector3(5,1,-1),new Vector2(1,1),new Vector4(2.0f,0.6f,-1.0f,0.4f));
7 verBuffer=new VertexBuffer(device,MyCustomVertexFormat.SizeInBytes*vertices.Length,ResourceUsage.WriteOnly);
8 verBuffer.SetData<MyCustomVertexFormat>(vertices,0,vertices.Length,SetDataOptions.None); 每个顶点要求一个Vector3,Vector2和Vector4.
Defining a Vertex Shader Capable of Interfacing with Your Custom Vertex Format
作为你努力的结果,你的顶点着色器将会接收数据从它的POSITION0,TEXCOORD0,和TEXCOORD1句法。它能存取在下面的代码中:

1 //-----------Technique:CustomVertexShader------
2 CVVertexToPixel CVVertexShader(float3 inPos:POSITION0,float2 inTexCoord:TEXCOORD0,float4 inExtra:TEXCOORD1)
3 {
4 CVVertexToPixel Output=(CVVertexToPixel)0;
5 float4 origPos=float4(inPos,1);
6 float4*4 preViewProjection=mul(xView,XProjection);
7 float4*4 preWorldViewProjection=mul(xWorld,preViewProjection);
8 Output.Position=mul(origPos,preWorldViewProjection);
9 Output.Extra=sin(xTime*inExtra.xyz);
10 Output.TexCoord=inTexCoord;
11 Output.TexCoord.x+=sin(xTime)*inExtra.w;
12 Output.TexCoord.y-=inExtra.w;
13 return Output;
14 } 它并不是不管你如何使用额外输入;最重要的事情是,你可以利用它们在你的顶点着色器中。这个例子顶点着色器首先映射3D位置到2D屏幕坐标。然后它用这三个第一浮点额外Vector4作为频率调试器,保存这个结果在输出的结构。最后,纹理坐标是移动的,同时最后的额外Vector4的浮点是用来调节力量的转变。
下面是一个示例像素着色器,它使用转移纹理坐标样品纹理,并增加了这三个值Extra变量最终颜色通道:
1 CVPixelToFrame CVPixelShader(CVVertexToPixel PSIn):COLOR0
2 {
3 CVPixelToFrame Output=(CVPixelToFrame)0;
4 Output.Color=tex2D(TextureSampler,PSIn.TexCoord);
5 Output.Color.rgb+=PSIn.Extra.rgb;
6 return Output;
7 }Rendering from Your Vertices
此代码设置效果变量和从你的顶点绘制三角形:
1 effect.Parameters["xWorld"].SetValue(Matrix.Identity);
2 effect.Parameters["xView"].SetValue(fpsCam.ViewMatrix);
3 effect.Parameters["xProjection"].SetValue(fpsCam.ProjectionMatrix);
4 effect.Parameters["xTexture"].SetValue(texture);
5 effect.Parameters["xTime"].SetValue(time);
6 effect.Begin();
7 foreach(EffectPass pass in effect.CurrentTechnique.Passes)
8 {
9 pass.Begin();
10 device.VertexDeclaration=myVertexDeclaration;
11 device.Vertices[0].SetSource(verBuffer,0,MyCustomVertexFormat.SizeInBytes);
12 device.DrawPrimitives(PrimitiveType.TriangleList,0,1);
13 pass.End();
14 }
15 effect.End();代码
参看上面代码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: