2012年7月4日水曜日

画像でステンシルバッファを操作する in OpenGL

画像を使って画素単位でステンシルバッファの値を定め、一部分が欠けたオブジェクトを描画する方法.

  • アルファチャンネルを持つテクスチャを準備する
  • GL_ALPHA_TESTを有効化
  • glDrawBuffer(GL_NONE)にしてテクスチャマッピング
  • glDrawBuffer(GL_BACK)にして描画したいものを描く

コード例

#include <gl/glut.h>
 
void reshape(int w, int h)
{
    glViewport(0, 0, w, h);
 
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
 
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}
 
void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 
    glEnable( GL_STENCIL_TEST );
 
    glDrawBuffer(GL_NONE);
    glStencilFunc(GL_ALWAYS, 1, 1);
    glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
 
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_ALPHA_TEST);
    glAlphaFunc( GL_NOTEQUAL, 0.0 );
 
    glColor4d(1.0, 1.0, 1.0, 0.5);
    glBegin( GL_QUADS );
    glTexCoord2d(0, 0); glVertex2d(-0.8, -0.8);
    glTexCoord2d(1, 0); glVertex2d(+0.8, -0.8);
    glTexCoord2d(1, 1); glVertex2d(+0.8, +0.8);
    glTexCoord2d(0, 1); glVertex2d(-0.8, +0.8);
    glEnd();
 
    glStencilFunc(GL_NOTEQUAL, 1, 1);
    glDrawBuffer(GL_BACK);
 
    glDisable(GL_TEXTURE_2D);
    glColor4d(1.0, 0.0, 1.0, 1.0);
    glBegin( GL_QUADS );
    glVertex2d(+0.0, -0.4);
    glVertex2d(+0.9, -0.4);
    glVertex2d(+0.9, +0.4);
    glVertex2d(-0.0, +0.4);
    glEnd();
 
    glutSwapBuffers();
}
 
void main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
 
    //ウィンドウ(camera)の作成
    glutInitWindowPosition(256, 256);
    glutInitWindowSize(256, 256);
    glutCreateWindow("write texture in stencil buf"); 
    glutReshapeFunc(reshape);
    glutDisplayFunc(display);
    glClearColor(0.0, 0.5, 0.0, 1.0);
 
    //ATI-RADEONでは指定すると正しく動作しない
    //glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
 
    const int size = 256*256;
    unsigned char image[size];
    for(int i=0;  i<size/2; i++)
    {
        image[i] = 0;
    }
    for(int i=size/2; i<size; i++)
    {
        image[i] = 255;
    }
 
    //テクスチャが確保できているかどうかチェックする
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA, 256, 256, 0, GL_ALPHA,  GL_UNSIGNED_BYTE, image );
 
    glutMainLoop();
}