本文主要介绍些在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