在前面我們學習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 }
