首先,讓我澄清我的意思是「分組模型」。我不確定這是什麼標準術語。爲了減少渲染調用次數,我將多個模型分組到一個模型中,並使用一次調用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毫秒延遲)呈現。