本文主要介绍些在OGRE中创建Vertex Buffer和Index Buffer的主要流程。Vertex Buffer主要用来保存一组数据,这些数据可以包括顶点,顶点颜色,顶点法线或贴图坐标等等。Index Buffer是用来保存Vertex Buffer中对应顶点的索引。通过Vertex Buffer 和IndexBuffer 可以创建自己的Mesh并载入,以下是基本流程:

首先:定义你自己的顶点数据(Mesh的坐标数据)和对应的顶点索引(注意三角形顶点顺序)

例如:

float vertices[vbufCount] = {
-100.0,100.0,-100.0, //0 position
-sqrt13,sqrt13,-sqrt13, //0 normal
100.0,100.0,-100.0, //1 position
sqrt13,sqrt13,-sqrt13, //1 normal
100.0,-100.0,-100.0, //2 position
sqrt13,-sqrt13,-sqrt13, //2 normal
-100.0,-100.0,-100.0, //3 position
-sqrt13,-sqrt13,-sqrt13, //3 normal
-100.0,100.0,100.0, //4 position
-sqrt13,sqrt13,sqrt13, //4 normal
100.0,100.0,100.0, //5 position
sqrt13,sqrt13,sqrt13, //5 normal
100.0,-100.0,100.0, //6 position
sqrt13,-sqrt13,sqrt13, //6 normal
-100.0,-100.0,100.0, //7 position
-sqrt13,-sqrt13,sqrt13, //7 normal
};

    unsigned short faces[indexCount] = {
0,2,3,
0,1,2,
1,6,2,
1,5,6,
4,6,5,
4,7,6,
0,7,4,
0,3,7,
0,5,1,
0,4,5,
2,7,3,
2,6,7
};

其次,创建Mesh,此处有两种方法,首先介绍下用MeshManager来创建的方法(用ManualObject的方法,在本文的最后涉及)

 Ogre::MeshPtr msh = MeshManager::getSingleton().createManual("Name", "General");

SubMesh
* sub = msh->createSubMesh();

根据你的顶点数据和索引数据的信息,来确定msh参数的值,如下:

msh->sharedVertexData = new VertexData();
msh
->sharedVertexData->vertexCount = nVertices;

然后,声明每个点在你的Buffer中的具有的内部分布结构,也就是除了点坐标,还具有的其他能描述该点的信息。例如是法线信息,还是位置信息,还是纹理坐标等等。注意在Buffer中要使用正确的偏移量。

VertexDeclaration* decl = msh->sharedVertexData->vertexDeclaration;
 size_t offset = 0;
decl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
 offset += VertexElement::getTypeSize(VET_FLOAT3);
 decl->addElement(0, offset, VET_FLOAT3, VES_NORMAL);
 offset += VertexElement::getTypeSize(VET_FLOAT3);

接下来,就该创建和绑定这个Vertex Buffer了。

   HardwareVertexBufferSharedPtr vbuf = 
HardwareBufferManager::getSingleton().createVertexBuffer(
              offset, msh->sharedVertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
vbuf
->writeData(0, vbuf->getSizeInBytes(), vertices, true);
VertexBufferBinding
* bind = msh->sharedVertexData->vertexBufferBinding;
bind
->setBinding(0, vbuf);

这里详细介绍下createVertexBuffer的参数,offset是指在VertexBuffer中每个顶点间的偏移量,第二个参数指顶点的数量,第三个参数HBU_STATIC_WRITE_ONLY,是指你不需要经常更新缓存,并且你从不需要从缓存读取数据。然后writeData函数将vertices的数据写入vbuf,true参数的设置允许在写的时候,丢弃整个buffer的原有内容。最后将VB进行绑定。此处也可用另一种方法读取数据(用指针的方法填充VertexBuffer),而不是通过writeData。现在去掉writeData这行函数,加入如下代码。一下引用Snippet中的一些代码作为说明示例。

     float* pVertex = static_cast<float*>(vBuf->lock(HardwareBuffer::HBL_DISCARD));

for( int ring = 0; ring <= nRings; ring++ ) {
float r0 = r * sinf (ring * fDeltaRingAngle);
float y0 = r * cosf (ring * fDeltaRingAngle);

// Generate the group of segments for the current ring
for(int seg = 0; seg <= nSegments; seg++) {
float x0 = r0 * sinf(seg * fDeltaSegAngle);
float z0 = r0 * cosf(seg * fDeltaSegAngle);

// Add one vertex to the strip which makes up the sphere
*pVertex++ = x0;
*pVertex++ = y0;
*pVertex++ = z0;

Vector3 vNormal
= Vector3(x0, y0, z0).normalisedCopy();
*pVertex++ = vNormal.x;
*pVertex++ = vNormal.y;
*pVertex++ = vNormal.z;

*pVertex++ = (float) seg / (float) nSegments;
*pVertex++ = (float) ring / (float) nRings;
}

vBuf
->unlock();

这种方法需要给缓冲区加锁。

 

以上是我们对于VertexBuffer的创建过程。IndexBuffer的创建如出一辙。

    HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton().
createIndexBuffer(
HardwareIndexBuffer::IT_16BIT,
ibufCount,
HardwareBuffer::HBU_STATIC_WRITE_ONLY);

ibuf
->writeData(0, ibuf->getSizeInBytes(), faces, true);

/// Set parameters of the submesh
sub->useSharedVertices = true;
sub
->indexData->indexBuffer = ibuf;
sub
->indexData->indexCount = ibufCount;
sub
->indexData->indexStart = 0;

同样,也可以用指针的方法确定IndexBuffer中的内容。

  unsigned short* pIndices = static_cast<unsigned short*>(iBuf->lock(HardwareBuffer::HBL_DISCARD));
 // Generate the group of rings for the sphere
     for( int ring = 0; ring <= nRings; ring++ ) {
         float r0 = r * sinf (ring * fDeltaRingAngle);
         float y0 = r * cosf (ring * fDeltaRingAngle);
 
         // Generate the group of segments for the current ring
         for(int seg = 0; seg <= nSegments; seg++) {
             float x0 = r0 * sinf(seg * fDeltaSegAngle);
             float z0 = r0 * cosf(seg * fDeltaSegAngle);
 
             if (ring != nRings) {
                                // each vertex (except the last) has six indices pointing to it
                 *pIndices++ = wVerticeIndex + nSegments + 1;
                 *pIndices++ = wVerticeIndex;              
                 *pIndices++ = wVerticeIndex + nSegments;
                 *pIndices++ = wVerticeIndex + nSegments + 1;
                 *pIndices++ = wVerticeIndex + 1;
                 *pIndices++ = wVerticeIndex;
                 wVerticeIndex ++;
             }
         }; // end for seg
     } // end for ring
  vBuf->unlock();

最后,应该来确定你的创建的Mesh的包围盒,这样当你的Mesh处于相机之外的时候,将被裁减掉。包围和的创建要根据你的具体的mesh来确定,最后因为你的模型是自建的,需要load将模型载入,此处只给出snippet中一段简单的示例

 pSphere->_setBounds( AxisAlignedBox( Vector3(-r, -r, -r), Vector3(r, r, r) ), false );
pSphere
->_setBoundingSphereRadius(r);
pSphere
->load();

刚才提到用另种方法创建Mesh。应用ManualObject,思想与前面一样。代码如下:

     ManualObject * manual = sceneMgr->createManualObject(strName);
manual
->begin("BaseWhiteNoLighting", RenderOperation::OT_TRIANGLE_LIST);

float fDeltaRingAngle = (Math::PI / nRings);
float fDeltaSegAngle = (2 * Math::PI / nSegments);
unsigned
short wVerticeIndex = 0 ;

// Generate the group of rings for the sphere
for( int ring = 0; ring <= nRings; ring++ ) {
float r0 = r * sinf (ring * fDeltaRingAngle);
float y0 = r * cosf (ring * fDeltaRingAngle);

// Generate the group of segments for the current ring
for(int seg = 0; seg <= nSegments; seg++) {
float x0 = r0 * sinf(seg * fDeltaSegAngle);
float z0 = r0 * cosf(seg * fDeltaSegAngle);

// Add one vertex to the strip which makes up the sphere
manual->position( x0, y0, z0);
manual
->normal(Vector3(x0, y0, z0).normalisedCopy());
manual
->textureCoord((float) seg / (float) nSegments, (float) ring / (float) nRings);

if (ring != nRings) {
// each vertex (except the last) has six indicies pointing to it
manual->index(wVerticeIndex + nSegments + 1);
manual
->index(wVerticeIndex);
manual
->index(wVerticeIndex + nSegments);
manual
->index(wVerticeIndex + nSegments + 1);
manual
->index(wVerticeIndex + 1);
manual
->index(wVerticeIndex);
wVerticeIndex
++;
}
};
// end for seg
} // end for ring

以上是个人在学习过程中的心得体会,如有纰漏,望高手指教。

 

作者: apapaxionga 发表于 2011-07-31 00:26 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"