«FBO を使ってデプスバッファを表示する 最新 屈折マッピング »

床井研究室

※このブログは遅くとも 2027 年 3 月に管理者の定年退職により閉鎖します (移転先は管理者本人共々模索中)

■ 2008年12月08日 [OpenGL][GLSL] デプスバッファの輪郭線抽出

2008年12月09日 10:45更新

シェーダで小細工

シェーダが使えるようになって,本当にいろんなことができるようになりました.バーテックスシェーダーにスキニングや変形アニメーションを実装するのも楽しいですし,フラグメントシェーダで色を重ねたりテクスチャのサンプリングに凝ったりするのも楽しくて仕方ありません.でも,やってみたいことはいろいろあるんですけど,自分にとってはどうも「遊び」の域を出ることができず,仕事に結び付けられずにいます.アイデア出てこい,どんどこどん(←わかるかなぁ!!,わっかんねぇだろうなぁ!!).

あ,これは松鶴家千とせという芸人さんのギャグです,って説明しないと通じないかも.しかし,この人の Web ページがあるのも驚いたんですけど,この人のブログを見て更に驚きました.「DEC さんの Alpha が使用されていました」ってセリフが,どうしてもこの芸人さんと結び付きません.

輪郭線抽出

前にやったトゥーンシェーディングでは,テクスチャを使って輪郭線の抽出を行っていました.この方法は簡単なんですけど,結果がどうもきれいじゃありません.かなり以前に見た ATI の 9700 NPR Hatching というデモでは,デプスバッファとかさまざまな情報を使って輪郭線の抽出を行っていたので,前回デプスバッファをサンプリングするプログラムを作ったことですし,これを使ってちょっと遊んでみることにします.

デプスバッファに Sobel オペレータをかけてみる

輪郭線の抽出には様々な方法がありますが,ここでは Sobel オペレータ物理のかぎしっぽさん)を使ってみます.前回作ったプログラム,

のフラグメントシェーダのソースプログラム showdepth.frag を,以下の内容(sobel.frag)に書き換えてみてください.

// sobel.frag
 
uniform sampler2D depth;
 
const float dx = 0.001953125;
const float dy = 0.001953125;
 
float peek(const in float x, const in float y)
{
  return texture2D(depth, vec2(x, y)).r;
}
 
void main (void)
{
  float x = gl_TexCoord[0].x;
  float y = gl_TexCoord[0].y;
  mat3 m = mat3(
    peek(x - dx, y - dy), peek(x, y - dy), peek(x + dx, y - dy),
    peek(x - dx, y     ), peek(x, y     ), peek(x + dx, y     ),
    peek(x - dx, y + dy), peek(x, y + dy), peek(x + dx, y + dy)
  );
  vec2 h = vec2(
    m[0][0] - m[0][2] + (m[1][0] - m[1][2]) * 2.0 + m[2][0] - m[2][2],
    m[0][0] - m[2][0] + (m[0][1] - m[2][1]) * 2.0 + m[0][2] - m[2][2]
  );
  float d = 1.0 - length(h);
  gl_FragColor = vec4(vec3(d), 1.0);
}

なんか,泥臭いプログラムですね.もっとうまく書けそうな気がするのですが,とりあえずこの辺で許してください.定数の 0.001953125 というのは 1 / 512,すなわちテクスチャ座標における画素の間隔です.この値を変えると線の太さが変わります.これは,こういう結果になります.

Sobel オペレータ

この最後の部分を次のように書き換えると,この結果を2値化できます.

  ・・・
  float d = step(0.9, 1.0 - length(h));
  gl_FragColor = vec4(vec3(d), 1.0);
}

こんな感じになります.

Sobel オペレータの結果を2値化

微分してみる

フラグメントシェーダを次のように書き換えて,微分もしてみました.

// differential.frag
 
uniform sampler2D texture;
 
const float dx = 0.001953125;
const float dy = 0.001953125;
 
float peek(const in float x, const in float y)
{
  return texture2D(texture, vec2(x, y)).r;
}
 
void main (void)
{
  float x = gl_TexCoord[0].x;
  float y = gl_TexCoord[0].y;
  float b = peek(x, y);
  float d = abs(b - peek(x + dx, y))
          + abs(b - peek(x, y + dy));
  gl_FragColor = vec4(vec3(1.0 - d), 1.0);
}

これは,こういう結果になります.

微分オペレータ

Roberts もやってみる

ここまできたら,ついでに Roberts オペレータもやってみました.

// roberts.frag
 
uniform sampler2D texture;
 
const float dx = 0.001953125;
const float dy = 0.001953125;
 
float peek(const in float x, const in float y)
{
  return texture2D(texture, vec2(x, y)).r;
}
 
void main (void)
{
  float x = gl_TexCoord[0].x;
  float y = gl_TexCoord[0].y;
  float d = abs(peek(x, y) - peek(x + dx, y + dy))
          + abs(peek(x + dx, y) - peek(x, y + dy));
  gl_FragColor = vec4(vec3(1.0 - d), 1.0);
}

こういう結果が得られました.

Roberts オペレータ

いろいろやってみたい

今回はデプスバッファしか使ってませんけど,カラーバッファや法線ベクトルの補間値などと組み合わせると,更にいろんなことができそうです.オレンジブックにも,もっと高度な例がたくさん載っているので,負けないくらい面白いものを考えてみたいですね.アイデア出てこい,どんどこどん.


編集 «FBO を使ってデプスバッファを表示する 最新 屈折マッピング »