■ 2008年12月08日 [OpenGL][GLSL] デプスバッファの輪郭線抽出
シェーダで小細工
シェーダが使えるようになって,本当にいろんなことができるようになりました.バーテックスシェーダーにスキニングや変形アニメーションを実装するのも楽しいですし,フラグメントシェーダで色を重ねたりテクスチャのサンプリングに凝ったりするのも楽しくて仕方ありません.でも,やってみたいことはいろいろあるんですけど,自分にとってはどうも「遊び」の域を出ることができず,仕事に結び付けられずにいます.アイデア出てこい,どんどこどん(←わかるかなぁ!!,わっかんねぇだろうなぁ!!).
あ,これは松鶴家千とせという芸人さんのギャグです,って説明しないと通じないかも.しかし,この人の 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,すなわちテクスチャ座標における画素の間隔です.この値を変えると線の太さが変わります.これは,こういう結果になります.
この最後の部分を次のように書き換えると,この結果を2値化できます.
・・・ float d = step(0.9, 1.0 - length(h)); gl_FragColor = vec4(vec3(d), 1.0); }
こんな感じになります.
微分してみる
フラグメントシェーダを次のように書き換えて,微分もしてみました.
// 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); }
こういう結果が得られました.
いろいろやってみたい
今回はデプスバッファしか使ってませんけど,カラーバッファや法線ベクトルの補間値などと組み合わせると,更にいろんなことができそうです.オレンジブックにも,もっと高度な例がたくさん載っているので,負けないくらい面白いものを考えてみたいですね.アイデア出てこい,どんどこどん.