まず、「グループ化されたモデル」の意味を明確にします。これの標準的な用語が何であるかは実際にはわかりません。レンダリング呼び出しの数を減らすために、複数のモデルを 1 つのモデルにグループ化し、glDrawElements への 1 回の呼び出し (VBO を使用) で全体をレンダリングしています。私のコードでは、これを ModelGroup と呼んでいます。さまざまなことに使用しますが、特に幾何学的に単純なオブジェクトの大規模なグループ (都市の建物や粒子など) に使用します。
ModelGroups のレンダリングが非常に遅いという問題が最近表面化しました。タイマーを配置することで、glDrawElements への実際の呼び出しのスローダウンを分離しました。たとえば、私のパーティクルは、約 2 ミリ秒で (インスタンス化せずに) 約 10,000 のパーティクルをレンダリングしていました。正確な数は思い出せませんが、現在のようにレンダリングがボトルネックではなかったとだけ言っておきましょう。現在のところ、10k のパーティクルを含む glDrawElements を 1 回呼び出すと、約 256 ミリ秒かかります。このパフォーマンスは、glDrawElements を個別に呼び出してオブジェクトをレンダリングするよりもわずかに優れています。したがって、何らかの理由で GPU に大きな負荷がかかっていることは明らかです。
エンジンの変更点: 最近 XCode を更新し、EAGLView の使用から GLKViewController の使用に変更しました。これら 2 つの非常に異なるパフォーマンス状態の間でコードを変更したことはありません。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 ステートメントを挟む 2 つのステートメントは、イベント間の時間を測定するために使用したタイマーです。
また、デバイスと iPad シミュレーター 6.1 の両方で同じ結果が得られたことを付け加えたいと思います。シミュレーターは、複数の描画呼び出しを実行すると遅くなりますが、ModelGroup の glDrawElements を呼び出すとどちらも同じくらい遅くなります。ハードウェア アクセラレーションに関する限り、このパフォーマンス ヒットがアクセラレーションの不足による副作用ではないことを確認しました。1024 個のキューブ (都市の ModelGroup に似ています) を含むファイルから読み込まれたモデルをレンダリングしましたが、問題なくレンダリングされました (ModelGroup の 1000 個のキューブのように 20 ミリ秒の遅延はありません)。