やることは非常に単純で、シェーダー側(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]
(https://github.com/yumataesu/ModernOpenGL-AdventCal2017/blob/7ece3a72d6d2e501221ae0417b851578946eedd0/21-render_raymarching_and_geometry_at_the_same_time/src/ofApp.cpp#L75-L94)
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ができる。
GBufferの中身。正しい前後関係でレンダリングされていることが確認できる。
後はライティングを行う
ここまでくると、後は一般的な遅延シェーディング処理を行うだけ。
最終レンダリング結果 | 緑のSphereは、Raymarching。赤のCubeは、ofBoxPrimitive。
ライティングが一貫しているのが確認できる。
まとめ
このテクニックを利用すれば、キャラクターアニメーションの後ろで、
フラクタルやトンネルがバキバキ動いているという複雑なシーンをレンダーすることができる。
GBufferが生成できたので、スクリーンスペース系のテクニック(SSAOとか, SSRとか)も今まで通り使えるはず。