首先,让我澄清我的意思是“分组模型”。我不确定这是什么标准术语。为了减少渲染调用次数,我将多个模型分组到一个模型中,并使用一次调用glDrawElements(使用VBOs)来渲染整个模型。在我的代码中,我称之为ModelGroup。我将它用于各种事物,但特别适用于大量几何简单物体(如城市中的建筑物或粒子)。分组模型在iOS中缓慢渲染(OpenGL ES 2.0)
这个问题最近出现在我的ModelGroup渲染速度非常慢的地方。我已经通过在其周围放置一个计时器来隔离实际调用glDrawElements的放缓。例如,我的粒子用于在约2ms左右渲染〜10k粒子(没有实例化)。我不记得确切的数字,但让我们说,渲染绝对不是目前的瓶颈。到目前为止,一次调用glDrawElements包含10k个粒子的时间约为256ms。这种性能仅比使用单独调用glDrawElement来渲染对象稍微好一些。所以,出于某种原因,GPU显然存在巨大的负担。
我的引擎发生了什么变化: 我最近更新了XCode,并从使用EAGLView更改为使用GLKViewController。在我的代码中,我没有改变这两种完全不同的表现状态。我会说,为了迁移到GLKViewController的使用,我完全重新创建了我的项目并添加了所有的源文件。然后我重写了我的游戏循环,并由GLKViewController的更新函数进行更新。然而,这是一个非常小的变化。
为了完全清楚我的ModelGroup类的功能,我将发布将所添加的模型编译到呈现的显示模型中的函数。
-(bool) compileDisplayModelWithNormals:(bool)updateNormals withTexcoords:(bool)updateTexcoords withColor:(bool)updateColor withElements:(bool)updateElements
{
modelCompiled = YES;
bool initd = displayModel->positions;
// set properties
if(!initd)
{
displayModel->primType = GL_UNSIGNED_SHORT;
displayModel->elementType = GL_TRIANGLES;
displayModel->positionType = GL_FLOAT;
displayModel->texcoordType = GL_FLOAT;
displayModel->colorType = GL_FLOAT;
displayModel->normalType = GL_FLOAT;
displayModel->positionSize = 3;
displayModel->normalSize = 3;
displayModel->texcoordSize = 2;
displayModel->colorSize = 4;
// initialize to zero
displayModel->numVertices = 0;
displayModel->numElements = 0;
displayModel->positionArraySize = 0;
displayModel->texcoordArraySize = 0;
displayModel->normalArraySize = 0;
displayModel->elementArraySize = 0;
displayModel->colorArraySize = 0;
// sum the sizes
for(NSObject<RenderedItem> *ri in items)
{
GLModel *model = ri.modelAsset.model.displayModel;
displayModel->numVertices += model->numVertices;
displayModel->numElements += model->numElements;
displayModel->positionArraySize += model->positionArraySize;
displayModel->texcoordArraySize += model->texcoordArraySize;
displayModel->normalArraySize += model->normalArraySize;
displayModel->elementArraySize += model->elementArraySize;
displayModel->colorArraySize += model->colorArraySize;
}
displayModel->positions = (GLfloat *)malloc(displayModel->positionArraySize);
displayModel->texcoords = (GLfloat *)malloc(displayModel->texcoordArraySize);
displayModel->normals = (GLfloat *)malloc(displayModel->normalArraySize);
displayModel->elements = (GLushort *)malloc(displayModel->elementArraySize);
displayModel->colors = (GLfloat *)malloc(displayModel->colorArraySize);
}
// update the data
int vertexOffset = 0;
int elementOffset = 0;
for(int j = 0; j < [items count]; j++)
{
NSObject<RenderedItem> *ri = (GameItem *)[items objectAtIndex:j];
GLModel *model = ri.modelAsset.model.displayModel;
if(!ri.geometryUpdate)
{
vertexOffset += model->numVertices;
continue;
}
// reset the update flag
ri.geometryUpdate = NO;
// get GameItem transform data
rpVec3 pos = [ri getPosition];
rpMat3 rot = [ri orientation];
int NoV = model->numVertices;
int NoE = model->numElements;
for(int i = 0; i < NoV; i++)
{
// positions
rpVec3 r = rpVec3(model->positions, model->positionSize * i);
// scale
rpVec3 s = ri.scale;
r.swizzleLocal(s);
// rotate
r = rot * r;
// translate
r.addLocal(pos);
int start = model->positionSize * (vertexOffset + i);
for(int k = 0; k < model->positionSize; k++)
displayModel->positions[start + k] = r[k];
if(updateTexcoords)
{
// texcoords
start = model->texcoordSize * (vertexOffset + i);
if(model->texcoords)
for(int k = 0; k < model->texcoordSize; k++)
displayModel->texcoords[start + k] = model->texcoords[model->texcoordSize * i + k];
}
if(updateNormals)
{
// normals (need to be rotated)
if(model->normals)
{
for(int k = 0; k < model->normalSize; k++)
{
rpVec3 vn = rpVec3(model->normals, model->normalSize * i);
rpVec3 vnRot = rot * vn;
start = model->normalSize * (vertexOffset + i);
displayModel->normals[start + k] = vnRot[k];
}
}
}
if(updateColor)
{
if(model->colors)
{
start = model->colorSize * (vertexOffset + i);
displayModel->colors[start] = ri.color.r;
displayModel->colors[start + 1] = ri.color.g;
displayModel->colors[start + 2] = ri.color.b;
displayModel->colors[start + 3] = ri.color.a;
}
}
}
if(updateElements)
{
for(int i = 0; i < NoE; i++)
{
// elements
displayModel->elements[elementOffset + i] = model->elements[i] + vertexOffset;
}
}
vertexOffset += NoV;
elementOffset += NoE;
}
return YES;
}
只要是完整的,这里是我如何渲染粒子。里面的粒子场平局功能:
glBindVertexArray(modelGroup.displayModel->modelID);
glBindTexture(GL_TEXTURE_2D, textureID);
// set shader program
if(changeShader) glUseProgram(shader.programID);
[modelViewStack push];
mtxMultiply(modelViewProjectionMatrix.m, [projectionStack top].m, [modelViewStack top].m);
glUniformMatrix4fv(shader.modelViewProjectionMatrixID, 1, GL_FALSE, modelViewProjectionMatrix.m);
[DebugTimer check:@"particle main start"];
glDrawElements(GL_TRIANGLES, modelGroup.displayModel->numElements, GL_UNSIGNED_SHORT, 0);
[DebugTimer check:@"particle main end"];
[modelViewStack pop];
夹着glDrawElements语句中的两个语句是我用来测量事件之间的时间计时器。
此外,我只是想补充一点,我已经在设备和iPad模拟器6.1上运行,结果相同。模拟器在执行多个绘图调用时速度较慢,但对于ModelGroup调用glDrawElements同样缓慢。就硬件加速而言,我已经检查过,以确保这种性能下降不会成为缺乏加速的一些副作用。我从一个包含1024个立方体(类似于城市的ModelGroup)的文件中读入一个模型,该模型没有任何问题(与ModelGroup中的1000个立方体没有20毫秒延迟)呈现。