やることは非常に単純で、シェーダー側(gl_FragDepth)でデプスを更新することである。

ModernOpenGL-AdventCal2017/21-render_raymarching_and_geometory_at_the_same_time

Feature

  • GBuffer生成し、1パス目でGeometryをレンダーし、2パス目でRaymarchingをレンダーする
  • 2パス目のRaymarchingで、1パス目と同じカメラ情報で、rayを定義する
  • rayの衝突判定からシーンの深度を構築し、その値でgl_FragDepthを更新する

GBuffer生成

GBufferは、特に変な設定はしない。
位置、法線、色(アルベド)をカラーバッファ、デプスバッファをレンダーバッファとして生成する。(ofFboではデフォルトで、レンダーバッファがデプスバッファになる)

    vector<GLint> formats = { GL_RGBA16F, GL_RGBA16F, GL_RGBA16F };
    ofFbo::Settings settings;
    settings.width = WIDTH;
    settings.height = HEIGHT;
    settings.textureTarget = GL_TEXTURE_2D;
    settings.wrapModeHorizontal = GL_CLAMP_TO_EDGE;
    settings.wrapModeVertical = GL_CLAMP_TO_EDGE;
    settings.minFilter = GL_NEAREST;
    settings.maxFilter = GL_NEAREST;
    settings.numColorbuffers = 3;
    settings.colorFormats = formats;
    settings.numSamples = 4;
    settings.useDepth = true;
    settings.useStencil = true;

    g_buffer.allocate(settings);
    g_buffer.checkStatus();

1パス目でジオメトリを普通にMRT

 1パス目のポリゴンのGBufferへのレンダリング。ここでも、特別なことは何にもしていない。
source

2パス目でRaymarchingのシーンを書き込む

 ここで重要なのは2つ
* 1パス目と同じカメラ情報で、rayを定義すること
* rayの衝突判定からシーンの深度を構築し、その値でgl_FragDepthを更新すること

 深度を更新しないと、適切な前後関係で、シーンがレンダーされない
しかし、RayMarchingは、ただの板ポリにfragment shaderで描画しているだけなので、深度値はつねに1である。
そのため自前で、Rayの衝突位置から深度値を計算する必要がある。
source

この深度値で、gl_FragDepthを更新する。

 2パス目ではglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);をせず、GBufferにレンダーする。
source

これで、RayMarchingとofBoxPrimitiveが共存するGBufferができる。

8月-24-2017 14-40-35.gif
GBufferの中身。正しい前後関係でレンダリングされていることが確認できる。

後はライティングを行う

 ここまでくると、後は一般的な遅延シェーディング処理を行うだけ。
8月-24-2017 13-50-24.gif

最終レンダリング結果 | 緑のSphereは、Raymarching。赤のCubeは、ofBoxPrimitive。
ライティングが一貫しているのが確認できる。

まとめ

 このテクニックを利用すれば、キャラクターアニメーションの後ろで、
フラクタルやトンネルがバキバキ動いているという複雑なシーンをレンダーすることができる。
 GBufferが生成できたので、スクリーンスペース系のテクニック(SSAOとか, SSRとか)も今まで通り使えるはず。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.