blog

インデックス付き描画とカスタムシェーダ 円錐を描画する

インデックス付き描画は、頂点が接続される順序が開発者によって指定される描画のタイプです。利点は、頂点を再利用でき、メモリ使用量を削減できることです。 例えば、ピラミッドの3Dグラフィックを描画する場合...

Aug 28, 2020 · 6 min. read
シェア

インデックスマッピング

インデックス描画とは、頂点の接続順序を開発者が指定するタイプの描画です。利点は、頂点を再利用できるため、メモリ使用量を削減できることです。例えば、ピラミッドの3Dグラフィックを描画する場合、三角形の帯や三角形の組み合わせを使用すると、複数の頂点が重複してしまいますが、インデックス描画を使用すると、5つの点だけで済み、それらを再利用し、接続する順序を指定することで、ピラミッドの3Dグラフィック描画を実現できます。

インデックス描画+カスタムシェーダー


- (void)renderDisplay
{
 
 glClear(GL_COLOR_BUFFER_BIT);
 glClearColor(0.4, 0.6, 0.7, 1);
 
 CGFloat scale = [[UIScreen mainScreen] scale];
 // ビューポートを設定する
 CGPoint orgin = self.frame.origin;
 CGSize size = self.frame.size;
 glViewport(orgin.x * scale, orgin.y * scale, size.width * scale, size.height * scale);
 
 // 頂点シェーダ、スライスシェーダファイルリテラルを取得する
 NSString * shaderVertex = [[NSBundle mainBundle] pathForResource:@"shaderv" ofType:@"glsl"];
 NSString * shaderFragment = [[NSBundle mainBundle] pathForResource:@"shaderf" ofType:@"glsl"];
 // プログラムが存在するかどうかを判断し、そうでなければプログラムをクリアする。
 if (self.mProgram) {
 glDeleteProgram(self.mProgram);
 self.mProgram = 0;
 }
 // プログラムをプログラムにロードする
 self.mProgram = [self loadShaderV:shaderVertex frag:shaderFragment];
 // プロシージャーとシェーダーをリンクする
 glLinkProgram(self.mProgram);
 // リンクステータスを取得し、trueの場合glUseProgram
 GLint linkStatus;
 glGetProgramiv(self.mProgram, GL_LINK_STATUS, &linkStatus);
 if (linkStatus == GL_FALSE) {
 GLchar message;
 glGetProgramInfoLog(self.mProgram, sizeof(message), 0, &message[0]);
 NSString * messageInfo = [NSString stringWithUTF8String:message];
 NSLog(@"program link error:%@",messageInfo);
 return;
 }
 NSLog(@"program link success");
 glUseProgram(self.mProgram);
 
 //====頂点データを用意する & インデックス付き配列=====
 GLfloat attrArr[] =
 {
 -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, // 
 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, //右上1
 -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, //左下2
 
 0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, //右下3
 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 
 };
 
 //(2).インデックス付き配列
 GLuint indices[] =
 {
 0, 3, 2,
 0, 1, 3,
 0, 2, 4,
 0, 4, 1,
 2, 3, 4,
 1, 4, 3,
 };
 
 // 頂点バッファが空かどうかを決定し、それが空の場合は、バッファ識別子を要求する
 if (self.mVertices == 0) {
 glGenBuffers(1, &_mVertices);
 }
 
 // =====頂点データを扱う======
 //   _mVertices GLへのバインディング_ARRAY_BUFFER
 glBindBuffer(GL_ARRAY_BUFFER, _mVertices);
 // CPUメモリコピーからGPUメモリへの頂点データの処理
 glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
 // で myPrograme を介して頂点シェーダーの位置に頂点データを渡す。
 GLuint position = glGetAttribLocation(self.mProgram, "position");
 
 // チャンネルを開く
 glEnableVertexAttribArray(position);
 // readメソッドを設定する
 glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, NULL);
 
 // =====頂点カラー値を扱う=====
 GLuint positionColor = glGetAttribLocation(self.mProgram, "positionColor");
 glEnableVertexAttribArray(positionColor);
 glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (float *)NULL + 3);
 
 // プログラムに従ってprojectionMatrix、modelViewMatrixを見つける
 GLuint projectionMatrix = glGetUniformLocation(self.mProgram, "projectionMatrix");
 GLuint modelViewMatrix = glGetUniformLocation(self.mProgram, "modelViewMatrix");
 
 
 
 float width = self.frame.size.width;
 float height = self.frame.size.height;
 
 // 4バイトのデータを作成する * 4 投影行列
 KSMatrix4 _projectionMatrix;
 ksMatrixLoadIdentity(&_projectionMatrix);
 //  
 float aspect = width/height;
 
 // パースペクティブ行列の取得
 /*
 パラメータ1:行列
 パラメータ2:画角、単位:度
 パラメータ3:アスペクト比
 パラメータ4:ニアプレーン距離
 パラメータ5:遠平面距離
 */
 ksPerspective(&_projectionMatrix, 30.0, aspect, 5.0f, 20.0f);
 //投影行列を頂点シェーダに渡す
 /*
 location:変更される一様変数の場所を指す
 count:行列の数を変更する
 transpose:行列を転置し,それを一様変数の値として使用するかどうか.GL でなければならない._FALSE
 value:カウントする要素へのポインタは、指定された均一変数を更新するために使用される。
 */
 glUniformMatrix4fv(projectionMatrix, 1, GL_FALSE, (CGFloat *)&_projectionMatrix.m[0][0]);
 
 // MODビュー行列を作成する
 KSMatrix4 _modelViewMatrix;
 // セル行列の取得
 ksMatrixLoadIdentity(&_modelViewMatrix);
 // z軸変換-10
 ksTranslate(&_modelViewMatrix,0.0,0.0,-10.0);
 // 4バイトのデータを作成する * 4 行列,回転行列
 KSMatrix4 _rotationMatrix;
 // ユニタリー行列への初期化
 ksMatrixLoadIdentity(&_rotationMatrix);
 
 //  
 //  
 ksRotate(&_rotationMatrix, xDegree, 1.0, 0.0, 0.0);
 //  
 ksRotate(&_rotationMatrix, yDegree, 0.0, 1.0, 0.0);
 //  
 ksRotate(&_rotationMatrix, zDegree, 0.0, 0.0, 1.0);
 // 変換行列に _modelViewMatrix 行列 vs. _rotationMatrix 行列の乗算、モデルビューに組み合わせる
 ksMatrixMultiply(&_modelViewMatrix, &_rotationMatrix, &_modelViewMatrix);
 //(7)モデルビュー行列を頂点シェーダに渡す
 /*
 location:変更される一様変数の場所を指す
 count:行列の数を変更する
 transpose:行列を転置し,それを一様変数の値として使用するかどうか.GL でなければならない._FALSE
 value:カウントする要素へのポインタは、指定された均一変数を更新するために使用される。
 */
 glUniformMatrix4fv(modelViewMatrix, 1, GL_FALSE, (GLfloat *)&_modelViewMatrix.m[0][0]);
 
 // バックサイドカリングをオンにする
 glEnable(GL_CULL_FACE);
 
 // インデックスを使って描画する
 /*
 void glDrawElements(GLenum mode,GLsizei count,GLenum type,const GLvoid * indices);
 パラメータリスト:
 mode:レンダリングされる図面のモデル
 GL_POINTS
 GL_LINES
 GL_LINE_LOOP
 GL_LINE_STRIP
 GL_TRIANGLES
 GL_TRIANGLE_STRIP
 GL_TRIANGLE_FAN
 count:図面数
 type: 
 GL_BYTE
 GL_UNSIGNED_BYTE
 GL_SHORT
 GL_UNSIGNED_SHORT
 GL_INT
 GL_UNSIGNED_INT
 indicesインデックス付き配列の描画
 */
 
 glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(indices[0]), GL_UNSIGNED_INT, indices);
 // ローカルビューポート表示レンダーバッファ
 [self.mContext presentRenderbuffer:GL_RENDERBUFFER];
 // タイマーをオンにする
 [self gcdTimer];
}
- (void)gcdTimer
{
 double seconds = 0.1;
 timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
 dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC, 0.0);
 dispatch_source_set_event_handler(timer, ^{
 
 [self refreshAngle];
 });
 dispatch_resume(timer);
}
- (IBAction)X_Clicked:(id)sender {
 // タイマーをオンにする
 boolX = !boolX;
 
}
- (IBAction)Y_Clicked:(id)sender {
 boolY = !boolY;
}
- (IBAction)Z_Clicked:(id)sender {
 boolZ = !boolZ;
}
- (void)refreshAngle
{
 // 次数を更新する
 xDegree += boolX * 5;
 yDegree += boolY * 5;
 zDegree += boolZ * 5;
 // 再描画
 [self renderDisplay];
 
}

マイナーバグ

renderDisplay メソッドの set read メソッドで stride の連続する頂点属性間のオフセットを設定する際、CGFloat 修飾子を使用するとバグが発生します。

// readメソッドを設定する glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(CGFloat) * 6, NULL);

三角形でないことがわかります。また、カラー値を設定する際、頂点のカラー値が正常でないこともわかります。

 glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, (float *)NULL + 3);

typedef float GLfloat; CoreGraphicsライブラリ CGBase.hGLfloatはOpenGL ESライブラリで次のように定義されています。

/* Definition of `CGFLOAT_TYPE', `CGFLOAT_IS_DOUBLE', `CGFLOAT_MIN', and `CGFLOAT_MAX'. */ #if defined(__LP64__) && __LP64__ # define CGFLOAT_TYPE double # define CGFLOAT_IS_DOUBLE 1 # define CGFLOAT_MIN DBL_MIN # define CGFLOAT_MAX DBL_MAX #else # define CGFLOAT_TYPE float # define CGFLOAT_IS_DOUBLE 0 # define CGFLOAT_MIN FLT_MIN # define CGFLOAT_MAX FLT_MAX #endif /* Definition of the `CGFloat' type and `CGFLOAT_DEFINED'. */ typedef CGFLOAT_TYPE CGFloat;

GLfloat修飾子の使用は、通常の表示では、2つの定義によってsizeof値の使用時に知ることができる、2つは同じではありませんが、CGFloatの8、GLfloatの4;とストライドがtypedef int32_t GLsizeiで変更され、データの4バイトの32ビットの長さを受け入れ、CGFloatを使用する場合、このバグが発生します。CGFloatを使用すると、このバグが発生します。

Read next

非同期/待機原理分析

一文の要約:ジェネレータ関数の構文は砂糖です。 jsの非同期問題に対処するための方法です。 関数宣言でキーワードasync場合、関数は非同期関数です。非同期関数はPromiseオブジェクトを返し、関数が実行されると、Pr...

Aug 28, 2020 · 8 min read