Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

CAEAGLLayerを同じ絵で埋め尽くす

タグ: iPhone OpenGL Objective-C

以前CGContextでやった、任意の領域を特定の画像で埋め尽くす処理を、OpenGL ESでもやってみる。

まずは、任意の領域の指定に「深度バッファ」を使ってみよう。
このような用途には「ステンシルバッファ」の方が適しているのだろうが、iPhoneのOpenGL ESにはステンシルバッファはない。

  1. iGLUの試用に使ったプロジェクトに画像(ここでは「64x64.png」)を追加する。
  2. EAGLViewに以下のインスタンス変数を追加する。
    GLuint tileTexture;
    CGSize tileSize;
  3. #define USE_DEPTH_BUFFER 0#define USE_DEPTH_BUFFER 1 に書き換える。
  4. const int vertex_color_size = 4;const int vertex_color_size = 0; に書き換える。
  5. initWithCorder: の後ろの方でテクスチャーを作る。
    CGImageRef tileImage = [UIImage imageNamed:@"64x64.png"].CGImage;
    size_t width = CGImageGetWidth(tileImage);
    size_t height = CGImageGetHeight(tileImage);
    
    GLubyte* tileData = (GLubyte*)malloc(width * height * 4);
    CGContextRef tileContext = CGBitmapContextCreate(tileData, width, height, 8, width * 4, CGImageGetColorSpace(tileImage), kCGImageAlphaPremultipliedLast);
    CGContextDrawImage(tileContext, CGRectMake(0.0, 0.0, width, height), tileImage);
    CGContextRelease(tileContext);
    	
    glGenTextures(1, &tileTexture);
    glBindTexture(GL_TEXTURE_2D, tileTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, tileData);
    free(tileData);
    
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    tileSize = CGSizeMake(width, height);
    
    glEnable(GL_TEXTURE_2D);
    
    // ついでに深度テストも有効に
    glEnable(GL_DEPTH_TEST);
  6. drawView:glClear から下を書き換える。
    // 深度バッファをクリア
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    glEnableClientState(GL_VERTEX_ARRAY);
    
    // ☆を描く
    glDepthFunc(GL_ALWAYS);
    std::for_each(parts.begin(), parts.end(), &draw_part);
    
    const GLshort vertices[] = {
    	-1, -1,
    	1, -1,
    	-1, 1,
    	1, 1,
    };
    
    const GLfloat w = CGRectGetWidth(self.bounds) / tileSize.width;
    const GLfloat tileTexcoords[] = {
    	0.0f, w,
    	w, w,
    	0.0f, 0.0f,
    	w, 0.0f,
    };
    
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    
    // ☆が描かれたピクセル(=深度が同じピクセル)にのみ画像を貼り付ける
    glDepthFunc(GL_EQUAL);
    glVertexPointer(2, GL_SHORT, 0, vertices);
    glTexCoordPointer(2, GL_FLOAT, 0, tileTexcoords);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
  7. dealloc でテクスチャを削除する。

以上。

これで「☆形の領域にのみ画像を並べて貼り付ける」ことができる。
ただ、この方法は「描く」か「描かない」の二択でしかないため、境界のジャギを抑えられないのが難。

depth_star.png

次に、「アルファ値」を使う方法を試す。
アルファ値の計算は深度テストよりいささか手間だが、その分「半透明で描く」という指示も可能だ。

  1. 先のプロジェクトにマスク画像(ここでは「mask.png」)を追加する。
  2. EAGLViewに以下のインスタンス変数を追加する。
    GLuint maskTexture;
  3. #define USE_DEPTH_BUFFER 1#define USE_DEPTH_BUFFER 0 に戻す。
  4. initWithCorder: の後ろの方でさらにテクスチャーを作る。
    // glEnable(GL_DEPTH_TEST); 深度テストは有効にしない
    
    CGImageRef maskImage = [UIImage imageNamed:@"mask.png"].CGImage;
    width = CGImageGetWidth(maskImage);
    height = CGImageGetHeight(maskImage);
    
    GLubyte* maskData = (GLubyte*)malloc(width * height);
    CGContextRef maskContext = CGBitmapContextCreate(maskData, width, height, 8, width, NULL, kCGImageAlphaOnly);
    CGContextDrawImage(maskContext, CGRectMake(0.0, 0.0, width, height), maskImage);
    CGContextRelease(maskContext);
    
    glGenTextures(1, &maskTexture);
    glBindTexture(GL_TEXTURE_2D, maskTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, maskData);
    free(maskData);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  5. drawView:glClear から下を書き換える。
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    const GLshort maskTexcoords[] = {
    	0, 1,
    	1, 1,
    	0, 0,
    	1, 0,
    };
    
    glClear(GL_COLOR_BUFFER_BIT);
    
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    
    glVertexPointer(2, GL_SHORT, 0, vertices);
    
    // 描画する領域の色を、アルファ値含めて落とす
    glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
    glBindTexture(GL_TEXTURE_2D, maskTexture);
    glTexCoordPointer(2, GL_SHORT, 0, maskTexcoords);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    // アルファ値が1になるように色を混ぜ合わせる
    glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
    glBindTexture(GL_TEXTURE_2D, tileTexture);
    glTexCoordPointer(2, GL_FLOAT, 0, tileTexcoords);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
  6. dealloc でテクスチャを削除する。

以上。

境界を拡大してみると、マスクのアルファ値に合わせて、ちゃんとアンチエイリアスっぽくなっているのが分かる。

alpha_mask.png

まあ、これはこれで、元画像のアルファ値を完全に無視しているので、完璧ではないが。

スポンサーサイト

コメント

コメントの投稿

コメントの投稿
管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://idlysphere.blog66.fc2.com/tb.php/166-8a91c0f7
この記事にトラックバックする(FC2ブログユーザー)

Appendix

タグ

Blog内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。