I. インデキシング
インデックスの概念
前回の記事では、頂点データチャネルを開くときに、6頂点を2つの三角形に分割して描画するように設定しました。しかし、2つの三角形は頂点が重複していることがわかり、グラフが複雑な場合にはパフォーマンスの無駄となります。
例えば、四角錐をインターフェイス上に描く必要があります:
頂点データは合計5個必要で、以前の頂点データの書き方に従えば、6 * 3 = 18個の頂点が必要です。インデックス描画を使えば、必要なのは5つの頂点とインデックスの配列だけです。
データは以下の通り:
//頂点データ テクスチャ座標
GLfloat attrArr[] =
{
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f,//1
0.5f, 0.5f, 0.0f, 1.0f, 1.0f,//2
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f,//3
0.5f, -0.5f, 0.0f, 1.0f, 1.0f,//4
0.0f, 0.0f, 1.0f, 1.0f, 0.0f//5
};
//インデックス配列
GLuint indices[] =
{
0, 3, 2,
0, 1, 3,
0, 2, 4,
0, 4, 1,
2, 3, 4,
1, 4, 3,
};
ヒントデータは、描画前に対応する頂点データを見つけるために使用され、重複する頂点データを多数定義する必要性を低減します。
索引付き図面の使用
OpenGL ESはインデックス描画の機能を提供します:
/*
パラメータ1:要素の組み立て方法
パラメータ2:インデックスの数
パラメータ3:データ型
パラメータ4:インデックス配列
*/
void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices);
そのため、描画時には glDrawArrays() を glDrawElements() に変更する必要があります 。
II.インデックスを使った四角錐の描画
四角錐は3次元の図形なので、見やすくするためにこの要素に対して行列変換が行われます。そのためGLSLでは、投影行列とビューモデル行列の2つのチャンネルも追加する必要があります:
頂点シェーダー:
//VSH
attribute vec4 position;//頂点座標
attribute vec2 textCoordinate;//テクスチャ座標
varying lowp vec2 varyTextCoord;//テクスチャ座標はスライス要素に渡される。
uniform mat4 projectionMatrix;//投影行列
uniform mat4 modelViewMatrix;//ビュー・モデル行列
void main()
{
varyTextCoord = textCoordinate;
vec4 vPos;
vPos = projectionMatrix * modelViewMatrix * position;
gl_Position = vPos;
}
チップシェーダー:
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main()
{
gl_FragColor = texture2D(colorMap, varyTextCoord);
}
次に、描画メソッドで、頂点データチャンネルを開いた後、投影行列とビューモデル行列を設定する必要があります。
ここでは、行列演算に2つのサードパーティ・クラスを使用しています:
- #import "GLESMath.h"
- #import "GLESUtils.h"
投影行列とビューモデル行列を設定する方法は、最新のOpenGLの章に酷似しています。
- (void)renderLayer {
//...省略
//mvpを取得する
GLuint projectionMatrixSlot = glGetUniformLocation(self.myPrograme, "projectionMatrix");//投影行列
GLuint modelViewMatrixSlot = glGetUniformLocation(self.myPrograme, "modelViewMatrix");//ビュー・モデル行列
float width = self.frame.size.width;
float height = self.frame.size.height;
//投影行列を設定する
KSMatrix4 _projectionMatrix;//行列を宣言する
ksMatrixLoadIdentity(&_projectionMatrix);//セル行列としてロードする
float aspect = width / height;//
ksPerspective(&_projectionMatrix, 30.0f, aspect, 5.0f, 20.0f);//視点を30に設定する°
glUniformMatrix4fv(projectionMatrixSlot, 1, GL_FALSE, (GLfloat *)&_projectionMatrix.m[0][0]);
//ビューモデル行列を設定する
KSMatrix4 _modelViewMatrix;//行列を宣言する
ksMatrixLoadIdentity(&_modelViewMatrix);//セル行列としてロードする
ksTranslate(&_modelViewMatrix, 0, 0, -10.0f);//最終結果は次のようになる。
//回転マトリックス
KSMatrix4 _rotationMatrix;//行列を宣言する
ksMatrixLoadIdentity(&_rotationMatrix);//セル行列としてロードする
ksRotate(&_rotationMatrix, xDegree, 1, 0, 0);//どの軸を中心にビューを回転させるか
ksRotate(&_rotationMatrix, yDegree, 0, 1, 0);
ksRotate(&_rotationMatrix, zDegree, 0, 0, 1);
//行列の乗算
ksMatrixMultiply(&_modelViewMatrix, &_rotationMatrix, &_modelViewMatrix);
glUniformMatrix4fv(modelViewMatrixSlot, 1, GL_FALSE, (GLfloat *)&_modelViewMatrix.m[0][0]);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
//...テクスチャの読み込みを省略する
//インデックス描画
glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(indices[0]), GL_UNSIGNED_INT, indices);
//ローカル・ウィンドウ・システムからOpenGL ESレンダリングを要求する。
[self.myContext presentRenderbuffer:GL_RENDERBUFFER];
}
見やすくするために、異なる軸を中心にビューを回転させるための3つのボタンがインターフェイス上に定義されています:
#pragma mark - XYClick
- (IBAction)XClick:(id)sender {
//タイマーをオンにする
if (!myTimer) {
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(reDegree) userInfo:nil repeats:YES];
}
//XまたはYを更新する
bX = !bX;
}
- (IBAction)YClick:(id)sender {
//タイマーをオンにする
if (!myTimer) {
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(reDegree) userInfo:nil repeats:YES];
}
//XまたはYを更新する
bY = !bY;
}
- (IBAction)ZClick:(id)sender {
//タイマーをオンにする
if (!myTimer) {
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(reDegree) userInfo:nil repeats:YES];
}
//XまたはYを更新する
bZ = !bZ;
}
-(void)reDegree
{
//X軸の回転を止めると、X軸は回転しない。= 0程度は一時停止前と同じである。.
//更新度
xDegree += bX * 5;
yDegree += bY * 5;
zDegree += bZ * 5;
//再レンダリング
[self renderLayer];
}
最終的な結果は以下の通り:




