本文主要介绍些在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
以上是个人在学习过程中的心得体会,如有纰漏,望高手指教。