工場裏のアーカイブス

素人によるiPhoneアプリ開発の学習記

OpenGL ES2.0でポイントスプライト(2)

OpenGLでポイントスプライト(1)の続きとなります。

サンプルプログラムの要点

ここでは前回の記事に掲載した、OpenGL ES2.0でポイントスプライトを利用するサンプルプログラムの要点についてメモしていきます。


GLKitを用いたテクスチャの読み込み

ポイントスプライトを利用するためには、当然ポイントに貼付けるためのテクスチャを用意する必要があります。しかし素のOpenGL ES2.0には、汎用的な画像ファイル(JPEGとかPNGとか)をテクスチャとして読み込む仕組みは用意されていません。そのため、本来なら自前でメソッドを実装したりしないといけないのですが、幸いなことにGLKitには画像ファイルのパスを指定するだけで、自動的にテクスチャを生成してくれる GLKTextureLoader が用意されています。以下に使い方のサンプルコードを示します(前回のサンプルプログラムより抜粋)。

  //GLKBaseEffectを使用する準備。初期化のとき(setupGL メソッドの冒頭など)で行っておけば良い。
    self.effect = [[GLKBaseEffect alloc] init];
    
    //テクスチャのロード。以下の手続きで画像をテクスチャとして読み込み、テクスチャユニット0にバインドすることが出来る。
    //「particle.png」というPNG画像を読み込む例
    NSString* filePath = [[NSBundle mainBundle] pathForResource:@"particle" ofType:@"png"];
    GLKTextureInfo *texInfo =
    [GLKTextureLoader textureWithContentsOfFile:filePath options:nil error:nil];
    if (texInfo) {
        //確認用。このログ表示は削除しても構わない。
        NSLog(@"Texture loaded successfully. name = %d size = (%d x %d)",
                                                   texInfo.name, texInfo.width, texInfo.height);
    }
    self.effect.texture2d0.name = texInfo.name;



頂点シェーダにおけるポイントサイズの設定

サンプルプログラムにおける頂点シェーダの全コードを以下に再掲します。

//Shader.vsh

attribute vec4 position;
attribute vec4 color;
varying vec4 vcolor;

uniform mat4 modelViewProjectionMatrix;

void main()
{
    vcolor = color;
    gl_Position = modelViewProjectionMatrix * position;
    
    //ポイントのサイズをここで設定する
    gl_PointSize = 20.0;
}

頂点シェーダのコードは非常にシンプルです。attribute変数 color に入力された各頂点の色は、そのまま(varying変数を介して)フラグメントシェーダに渡しています。attribute変数 position に入力された各頂点の位置は、渡された変換行列(モデルビュー行列と投影行列を乗算したもの)によって座標変換されます。

そして、組み込みの特殊変数である gl_PointSize に値を設定することにより、ポイントサイズをピクセル単位で設定することが出来ます。昔のOpenGLではシェーダを用いずとも "glPointSize(20.0f);" といった関数によりポイントサイズを変更出来たようですが、OpenGL ES2.0ではこのような関数は(私が試した限りでは)使えないようです。

またgl_PointSize に定数の値を設定すると、当然全てのポイントは同じサイズで表示されますが、場合によってはポイントに遠近感を付けて表示したい場合もあります。すなわち、視点に近いポイントほど大きく、遠いポイントほど小さく描画されるようにしたいわけです。これは以下のように、ある定数を gl_Position の w 要素で割った値を gl_PointSize に設定することにより容易に実現出来ます。

//gl_Positionには、既に(座標変換された)ポイントの位置が格納されているとする
gl_PointSize = 200.0 / gl_Position.w;

詳しい原理については(私自身が理解しきれていないので)省きますが、視点の位置に対して、各ポイントの位置が離れていればいるほど w 要素の値は大きくなります。すなわち上記の方法によって、視点からの距離に反比例してポイントサイズの大きさが変わるようになり、以下の画像のように遠近感を付けることが出来ます。

f:id:fleron:20130816205324p:plain


フラグメントシェーダによるアルファテスト

サンプルプログラムにおけるフラグメントシェーダの全コードを以下に再掲します。

//Shader.fsh

precision mediump float;

uniform sampler2D s_texture;
varying vec4 vcolor;

void main()
{
    //サンプラで取り込んだテクスチャを、変数"baseColor"に格納
    vec4 baseColor = texture2D(s_texture, gl_PointCoord);
    
    //アルファ値が0.5未満である場合はフラグメントを破棄(アルファテスト)
    if(baseColor.a < 0.5){
        discard;
    }
    else{
        //元々のテクスチャの色に、各ポイントに設定された色を付けて出力する
        gl_FragColor = baseColor * vec4(vcolor);
    }
}

フラグメントシェーダのコードも非常にシンプルであり、基本的にはサンプラによって取り込んだテクスチャの色に、頂点シェーダから(varying変数を介して)受け取った各頂点の色を付けて出力しているだけです。

そして、テクスチャを格納する変数 baseColor の a 要素には、テクスチャ画像のアルファ値が入っています。この値が0.5未満である場合には discard キーワードを呼び出しています。discard を呼び出すと、そのフラグメントは破棄されるため、アルファ値が0.5未満の領域を切り抜く(あるいは透明にする)ことが出来ます。

昔のOpenGLではシェーダを用いずとも、glAlphaFunc(GL_GREATER, 0.5); や glEnable(GL_ALPHA_TEST); といった関数によってアルファテストの機能を利用することが出来たのですが、例によってOpenGL ES2.0ではこれらはサポートされなくなったようです。