在前面我们学习OpenGL时,不管绘制如球,立方体,平面,地面,动画模型中最常用的几个操作有创建缓冲区,写入缓冲区.在Axiom中,相关的操作被整合与组织到VertexData,IndexData中,所以在后面,我们会看到Axiom里元素如果要绘画在屏幕上的元素,几乎都会包含这二个类的实体.
VertexData主要包含二个类的实体,一个是VertexDeclaration,另一个是VertexBufferBinding.
VertexDeclaration是用来指定数据的组成结构,比如在前面我们使用OpenGL的混合顶点数组时,会用T2F_N3F_V3F指定对应数组每八个浮点数据一组,前二个指定纹理坐标,中间三个数据指定法线,最后三个数据指定顶点.VertexDeclaration类似这个T2F_N3F_V3F,他包含一系列VertexElement,每个VertexElement指定上面的T2F,N3F,V3F这种类型,请看下面一段代码:
1 var decl = mesh.SharedVertexData.vertexDeclaration; 2 var binding = mesh.SharedVertexData.vertexBufferBinding; 3 4 var offset = 0; 5 decl.AddElement( 0, offset, VertexElementType.Float3, VertexElementSemantic.Position ); 6 offset += VertexElement.GetTypeSize( VertexElementType.Float3 ); 7 decl.AddElement( 0, offset, VertexElementType.Float3, VertexElementSemantic.Normal ); 8 offset += VertexElement.GetTypeSize( VertexElementType.Float3 ); 9 decl.AddElement( 0, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 0 ); 10 offset += VertexElement.GetTypeSize( VertexElementType.Float2 );
1 var vertices = new float[32] 2 { 3 -100, -100, 0, // pos 4 0, 0, 1, // normal 5 0, 1, // texcoord 6 100, -100, 0, 0, 0, 1, 1, 1, 100, 100, 0, 0, 0, 1, 1, 0, -100, 100, 0, 0, 0, 1, 0, 0 7 }; 8 var vbuf = HardwareBufferManager.Instance.CreateVertexBuffer( decl, 4, BufferUsage.StaticWriteOnly ); 9 binding.SetBinding( 0, vbuf ); 10 vbuf.WriteData( 0, vbuf.Size, vertices, true );
看过OpenGL绑定过程的,一定看起来很熟悉是不是,实际差不多,在CreateVertexBuffer里,首先会在HardwareVertexBuffer结合decl与顶点个数4.这里为什么是4,其实大家可以算一下,decl里声明的是P3F_N3F_T2F,意思是每个顶点有八个数据来指明相关属性,而vertices的长度是32,就是指明这个数据只包含了4个顶点.然后我们可以计算我们需要申请的显存长度,sizeInBytes=4*8*sizeof(float)=128.然后如果我们使用OpenGL渲染,就会调用GLHardwareVertexBuffer,这个类的初始化里就使用了glGenBuffers.最后vbuf.WriteData操作就会调用glBufferData把相关数据写入到显存里.
很明显,对比原来我们常用的OpenGL操作如下:
1 this.vboID = glGenBuffers(1) 2 glBindBuffer(GL_ARRAY_BUFFER,this.vboID) 3 glBufferData (GL_ARRAY_BUFFER, len(vdata)*4, vdata, GL_STATIC_DRAW) 4 this.eboID = glGenBuffers(1) 5 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,this.eboID) 6 glBufferData (GL_ELEMENT_ARRAY_BUFFER, len(vIndex)*4, vIndex,GL_STATIC_DRAW)
1 var ibuf = HardwareBufferManager.Instance.CreateIndexBuffer( IndexType.Size16, 6, BufferUsage.StaticWriteOnly ); 2 3 var faces = new short[6] 4 { 5 0, 1, 2, 0, 2, 3 6 }; 7 sub.IndexData.indexBuffer = ibuf; 8 sub.IndexData.indexCount = 6; 9 sub.IndexData.indexStart = 0; 10 ibuf.WriteData( 0, ibuf.Size, faces, true );
会发现不一样的是,顶点数据用的是HardwareVertexBuffer,而顶点索引用的HardwareIndexBuffer,基本用法一样,不同的调用的CreateIndexBuffer,结合前面与调试在相关位置,发现还是和前面一样,一样会调用glGenBuffers,glBindBuffer,glBufferData.不同前面是GL_ARRAY_BUFFER,这里是GL_ELEMENT_ARRAY_BUFFER.
我们知道,在底层不管是OpenGL或是D3D,他们都是C,C++及的语言,针对底层的指针操作,在C#中都由上面的BufferBase的派生类包装GCHandle提供相关的非托管内存访问托管对象的方法,能创建防GC回收托管对象.详细请看GCHandle 结构.在HardwareBuffer里的相关和内存,显存有关的操作全是用BufferBase来完成.具体请看Axiom3D:Buffer漫谈整个代码是创建一个四个点的面,分别创建顶点与顶点索引缓冲区,相对整个部分代码如下:
1 /// <summary> 2 /// Creates a plane as a submesh of the given mesh 3 /// </summary> 4 [OgreVersion( 1, 7, 2 )] 5 private static void _createPlane( Mesh mesh ) 6 { 7 var sub = mesh.CreateSubMesh(); 8 var vertices = new float[32] 9 { 10 -100, -100, 0, // pos 11 0, 0, 1, // normal 12 0, 1, // texcoord 13 100, -100, 0, 0, 0, 1, 1, 1, 100, 100, 0, 0, 0, 1, 1, 0, -100, 100, 0, 0, 0, 1, 0, 0 14 }; 15 16 mesh.SharedVertexData = new VertexData(); 17 mesh.SharedVertexData.vertexCount = 4; 18 var decl = mesh.SharedVertexData.vertexDeclaration; 19 var binding = mesh.SharedVertexData.vertexBufferBinding; 20 21 var offset = 0; 22 decl.AddElement( 0, offset, VertexElementType.Float3, VertexElementSemantic.Position ); 23 offset += VertexElement.GetTypeSize( VertexElementType.Float3 ); 24 decl.AddElement( 0, offset, VertexElementType.Float3, VertexElementSemantic.Normal ); 25 offset += VertexElement.GetTypeSize( VertexElementType.Float3 ); 26 decl.AddElement( 0, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 0 ); 27 offset += VertexElement.GetTypeSize( VertexElementType.Float2 ); 28 29 var vbuf = HardwareBufferManager.Instance.CreateVertexBuffer( decl, 4, BufferUsage.StaticWriteOnly ); 30 binding.SetBinding( 0, vbuf ); 31 32 vbuf.WriteData( 0, vbuf.Size, vertices, true ); 33 34 sub.useSharedVertices = true; 35 var ibuf = HardwareBufferManager.Instance.CreateIndexBuffer( IndexType.Size16, 6, BufferUsage.StaticWriteOnly ); 36 37 var faces = new short[6] 38 { 39 0, 1, 2, 0, 2, 3 40 }; 41 sub.IndexData.indexBuffer = ibuf; 42 sub.IndexData.indexCount = 6; 43 sub.IndexData.indexStart = 0; 44 ibuf.WriteData( 0, ibuf.Size, faces, true ); 45 46 mesh.BoundingBox = new AxisAlignedBox( new Vector3( -100, -100, 0 ), new Vector3( 100, 100, 0 ) ); 47 mesh.BoundingSphereRadius = Utility.Sqrt( 100*100 + 100*100 ); 48 }