Edited at

RaymarchingとGeometryを同一シーンにレンダーする

More than 1 year has passed since last update.

 やることは非常に単純で、シェーダー側(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とか)も今まで通り使えるはず。