blog

iOSビジュアル (XII) -- GLSLを初めて使う (II): インデックス付きキューブ描画

前節では、頂点データチャネルを開くと、2つの三角形で6頂点の描画になります。しかし、三角形のうちの2つは頂点が重複しており、グラフが複雑すぎる場合にはパフォーマンスの無駄となります。そこで、OpenG...

Aug 9, 2020 · 4 min. read
シェア

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]; }

最終的な結果は以下の通り:

Read next

キャッシュの基本概念

キャッシュは、CPUとDRAMの間に位置する小型の高速アクセスメモリで、通常はSRAMで構成されます。 現在、キャッシュの概念が拡張され、CPUとメインメモリ間だけでなく、メモリとハードディスク間、さらにはハードディスクとネットワーク間でも、キャッシュの感覚があります - インターネット一時的なとして知られています...

Aug 9, 2020 · 2 min read