Render Monkey 1.6 でお絵かきツールを作ろう

添付写真のような絵が描けます。rfxファイル(XML)を以下からダウンロードできます。名前を付けて保存して、Render Monkey に食わせてください。

http://cvs.sourceforge.jp/cgi-bin/viewcvs.cgi/*checkout*/gslib/GSLib/SandBox/RenderMonkey/Paint.rfx?rev=HEAD&content-type=text/plain

操作方法は

  • 左ドラッグ:ペイント
  • 右クリック:クリア

※Render Monkey のツールバーにある Mouse Input をクリックしないと正しく動作しません

このページを見ている人のほとんどは、シェーダーなんて当然使えるはずなので、ディフューズやスペキュラーの計算は他所に譲りまして、ここでは、Render Monkey のトリッキーな使い方を紹介させていただきます。こいつを通じて、さくっとRender Moneky に慣れてもらえればと思います。いや、やっぱり入門にはきついかも。
というわけで、2D Paint を作ってみようと思います。マウス入力を受け取り、塗り塗りします。恐るべきことに、マウスの位置やマウスボタンの押下情報がシェーダーコンスタントとして入力可能です(FX Composerでもできるようです)。

まず、レンダーターゲットテクスチャーを作ります。

  1. Screen-Aligned Quad を作ったら、Single Pass を Draw と名前を変えてください。名前を変えるには、クリックして F2 です。
  2. Screen-Aligned Quad を右クリック
  3. Add Texture -> Add Renderable Texture
  4. renderTextureをダブルクリック
  5. サイズを512×512に
  6. Auto-Generate Mip Map を off
  7. Format を A8R8G8B8 に

つぎに、各種変数を定義します。変数は、シェーダーコンスタントとして、すぐさま利用可能です。

  1. Drawを右クリック
  2. Add Variable -> Float -> Predefined の中から、以下のものを選択
    1. fMouseButtonStateLeft
    2. fMouseButtonStateRight
    3. fMouseCoordsXY_NDC
    4. fViewportDimensionsInverse

Drawパスのレンダリングターゲットを変更します。

  1. Drawを右クリック
  2. Add Render Target -> renderTexture
  3. Draw 以下の renderTexture をダブルクリック(2重丸みたいなアイコン)
  4. Enable color clear を off

renderTexture にはペイントされたデータが入ることになります(Enable color clear が on だと、毎回ターゲットがクリアされるので、画像が残りません)。次に、レンダリングステートを設定します。描画はちょっと変わった加算ブレンドを使います。

  1. Drawを右クリック
  2. Add Render State Block
  3. Render State をダブルクリックし、以下のように変更
    1. GL_AlphaEnable TRUE
    2. GL_BlendEnable TRUE
    3. GL_BlendEquation ADD
    4. GL_BlendSourceRGB ONE
    5. GL_BlendSourceAlpha ONE
    6. GL_BlendDestRGB SRC_ALPHA
    7. GL_BlendDestAlpha ONE

最後に、シェーダーを以下のように変更してください。

// vertex shader
varying vec2 texCoord;
uniform vec2 fViewportDimensionsInverse;

void main(void)
{
   gl_Position = vec4( gl_Vertex.xy, 0.0, 1.0 );
   gl_Position = sign( gl_Position );

   // Texture coordinate for screen aligned (in correct range):
   texCoord = (gl_Position.xy + vec2( 1.0 ) ) * 0.5 * ( vec2( 512.0 ) * fViewportDimensionsInverse );
}

僕のカードではレンダリングターゲットは2乗サイズが必須なので、サイズを512にし、fViewportDimensionsInverseで実際の画面の逆数をもらい、値を補正することにしました。

// fragment shader
uniform sampler2D Texture0;
uniform vec2 fMouseCoordsXY_NDC;
uniform float fMouseButtonStateRight;
uniform float fMouseButtonStateLeft;

varying vec2 texCoord;

void main(void)
{
   vec2 invYMouseXY = vec2( fMouseCoordsXY_NDC.x, 1.0 - fMouseCoordsXY_NDC.y );
   if ( fMouseButtonStateRight > 0.0 ) {
      //   clear canvas
      gl_FragColor = vec4( 0.0 );
   } else {
      float intensity = ( 1.0 - length( invYMouseXY - texCoord ) * 20.0 ) * fMouseButtonStateLeft;
      gl_FragColor =
         vec4(
            intensity, intensity, intensity, 1.0
         );
   }
}

fragment program の if ( fMouseButtonStateRight > 0.0 ) { は右クリックでキャンバスをクリアするためのモノです。Render State で GL_BlendDestRGB を SRC_ALPHA に指定したのはこのためです。
fMouseCoordsXY_NDC にはマウスの座標が [ 0.0 - 1.0 ] で入ってきます。テクスチャーと上下関係が逆になるので、invYMouseXY を作っています。後は、マウスカーソルの位置と現在打ち込もうとしている座標が近いほど高いインテンシティーを設定します。最後に、fMouseButtonStateLeft とかけ算することで、左クリック時のみ色を塗ることが可能になります。

あとは、これをディスプレーに表示します。最初のやり方で、Screen-Aligned Quad をもう一度作ります。そして、描画しているパスをコピーして貼り付け、名前を Display に変えてください。

Textureを追加します。

  1. Display を右クリック
  2. Add Texture Object
  3. Texture0 の中の baseMap を右クリック
  4. Reference Node -> renderTexture

後は、シェーダーを以下のように変更してください。

// vertex shader
varying vec2  texCoord;
uniform vec2 fViewportDimensions;

void main(void)
{
   gl_Position = vec4( gl_Vertex.xy, 0.0, 1.0 );
   gl_Position = sign( gl_Position );
    
   // Texture coordinate for screen aligned (in correct range):
   texCoord = (gl_Position.xy + vec2( 1.0 ) ) / vec2( 2.0 ) * ( fViewportDimensions / vec2( 512.0 ) );
}
// fragment shader
uniform sampler2D Texture0;

varying vec2 texCoord;

void main(void)
{
    gl_FragColor = texture2D( Texture0, texCoord );
}