-(void) awakeFromNib
{
[self loadShaders];
}
-(BOOL) loadShaders
{
//プログラムの生成
GLuint program = glCreateProgram();
//シェーダの生成
GLuint vertShader, fragShader;
[self compileShader:&vertShader type:GL_VERTEX_SHADER file:@"Shader.vsh"];
[self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:@"Shader.fsh"];
//シェーダをアタッチする
glAttachShader(program, vertShader);
glAttachShader(program, fragShader);
//プログラムをリンクする
[self linkProgram:program];
//シェーダを解放
glDeleteShader(vertShader);
glDeleteShader(fragShader);
}
-(BOOL) compileShader:(GLuint*)shader type:(GLenum)type file:(NSString*)file
{
//シェーダソースを取得
const GLchar *source = (GLchar *)[[NSString stringWithContentsOfFile:file] UTF8String];
//シェーダ生成
*shader = glCreateShader(type);
//シェーダソースを渡す
glShaderSource(*shader, 1, &source, NULL);
//引数:シェーダハンドル、ソース数、ソース文字列へのポインタ配列、
//ソース文字数の配列(NULLの場合、文字列はすべてNULLで終わると仮定する)
//コンパイル
glCompileShader(*shader);
}
-(BOOL) linkProgram:(GLuint)prog
{
glLinkProgram(prog);
}
-(void) drawGame
{
[(EAGLView*)self.view setFramebuffer];
glClearColor(0.5f, 0.5f, 1.0f, 1.0f); //わざと黒でない中間色にしておく
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(program);
//ゲームの描画処理
draw();
[(EAGLView*)self.view presentFramebuffer];
}
GLDraw
ゲーム側。
シェーダの中身を記述する。
//シェーダ関連変数の置き場
struct UserData{
//プログラム
GLuint programObject;
//アトリビュート
GLint positionLoc;
GLint colorLoc;
GLint texCoordLoc;
//サンプラー
GLint samplerLoc;
//テクスチャ
GLuint textureId[16];
};
//シェーダ変数の取得と、テクスチャ関係の初期化
void initProgram(GLuint program)
{
userData = new UserData;
userData->programObject = program;
userData->positionLoc = glGetAttribLocation( program, "position");
userData->colorLoc = glGetAttribLocation( program, "color");
userData->texCoordLoc = glGetAttribLocation( program, "texCoord");
userData->samplerLoc = glGetUniformLocation( program, "s_texture");
//テクスチャのアライメント指定. グローバルな指定なので、ここで行う
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//テクスチャ生成
glGenTextures(16, &userData->textureId[0]);
}
//テクスチャの登録
bool registerTexture(NSString *file, GLuint *hTexture)
{
GLuint textureId = userData->textureId[0];
//テクスチャをターゲットにバインドして有効化
glBindTexture(GL_TEXUTRE_2D, textureId);
//テクスチャデータを取得
GLubyte *bits;
GLfloat width, height;
loadTextureFromFile(file, &bits, &width, &height);
//テクスチャデータをロード
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bits);
//フィルタリングモードをセット
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//後始末
glBindTexture(GL_TEXTURE_2D, 0);
*hTexture = textureId;
return true;
}
//テクスチャ描画
void drawTexture(GLuint textureId, GLfloat *vertices, GLfloat *texCoord)
{
//座標
glVertexAttribPointer(userData->positionLoc, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(userData->positionLoc); //頂点配列頂点属性を使用する
//色
glVertexAttribPointer(userData->texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(userData->texCoordLoc); //頂点配列頂点属性を使用する
//テクスチャをバインド
glActiveTexture(GL_TEXTURE0); //カレントのテクスチャユニットを設定
glBindTexture(GL_TEXTURE_2D, texutreId); //カレントのテクスチャユニットにテクスチャをバインド
//サンプリング対象にするテクスチャユニットのインデックスを指定
glUniform1i(userData->samplerLoc, 0);
//glActiveTexture(GL_TEXTURE**)と同じ値にする
//インデックスバッファ
GLushort indices[] = { 0,1,2, 0,2,3};
//描画
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
}
//プリミティブの描画
void drawRectFull(GLfloat *vertices, Color color)
{
//座標
glEnableVertexAttribArray(userData->positionLoc);
glVertexAttribPointer(userData->positionLoc, 2, GL_FLOAT, GL_FALSE, 0, vertices);
//色指定
glEnableVertexAttribArray(userData->colorLoc);
glVertexAttrib4f(userData->colorLoc, color.r, color.g, color.b, color.a);
//描画
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
テクスチャを使用してもしなくても描画できるように一応してある。
その場合、シェーダの中でどちらの色を使うか(指定した頂点カラーそのままなのか、テクスチャを使うのか)を見分ける必要がある。
そのためにアトリビュートをもう一つ使うようなのはきれいじゃない。
カラー減算する仕組みを乗せておき、常時真っ白いテクスチャを用意しておくようにするのがスマートではないかと思う。
PR