苛性水のリアルタイムレンダリング

この記事では、WebGLとThreeJSを使用してリアルタイムの苛性計算を一般化する試みを紹介します。すべての場合に機能し、60 fpsを提供するソリューションを見つけることは、不可能ではないにしても難しいため、これが試みであるという事実は重要です。しかし、私の方法の助けを借りて、かなりまともな結果を達成できることがわかります。



苛性とは何ですか?



苛性アルカリは、光が表面、この場合は水と空気の境界で屈折および反射したときに発生する光のパターンです。



反射と屈折は水の波で発生するため、ここでは水が動的レンズとして機能し、これらの光のパターンを作成します。





この投稿では、通常水中で発生する光の屈折によって引き起こされる苛性に焦点を当てます。



安定した60fpsを達成するには、グラフィックカード(GPU)で計算する必要があるため、GLSLで記述されたシェーダーを使用して苛性を計算するだけです。



それを計算するには、次のものが必要です。



  • 水面で屈折した光線を計算します(GLSLでは、この機能組み込まれているため、これは簡単です)。
  • 交差アルゴリズムを使用して、これらの光線が環境と衝突するポイントを計算します
  • 光線の収束点をチェックして苛性輝度を計算します




WebGLでよく知られている水のデモ



私は常に、WebGLで視覚的にリアルな水の苛性を示すこのEvan Wallaceデモに驚いています:madebyevan.com/webgl-water





ライトフロントメッシュとGLSL部分微分関数を使用してリアルタイムで苛性アルカリを計算する方法を説明している 彼のMediumの記事を読むことをお勧めします。その実装は非常に高速で見栄えがしますが、いくつかの欠点がありますキューブプールと球形プールボールでのみ機能します。サメを水中に置くと、デモは機能しません。シェーダーでは、水中に球形のボールがあることがハードコードされています。



屈折した光線と球の交点を計算するのは非常に単純な数学を使用する簡単な作業であるため、彼は球を水中に配置しました。



これはすべてデモには適していますが、より一般的なソリューションを作成したかったのです。 サメなどの構造化されていないメッシュをプールに含めることができるように、苛性を計算します。





それでは、私のテクニックに移りましょう。この記事では、ラスター化を使用した3Dレンダリングの基本を既に理解しており、頂点シェーダーフラグメントシェーダーが連携してプリミティブ(三角形)を画面にレンダリングする方法に精通していることを前提としています。



GLSL制約の操作



GLSL(OpenGL Shading Language)で記述されたシェーダーでは、シーンに関する限られた量の情報にしかアクセスできません。次に例を示します。



  • 現在描画されている頂点の属性(位置:3Dベクトル、法線:3Dベクトルなど)。GPU属性を渡すことはできますが、それらは組み込みのGLSLタイプである必要があります。
  • 均一、つまり、現在のフレームで現在レンダリングされているメッシュ全体の定数。これらは、テクスチャ、カメラの投影マトリックス、照明の方向などです。それらには組み込みタイプが必要です:int、float、テクスチャ用のsampler2D、vec2、vec3、vec4、mat3、mat4。


ただし、シーンに存在するメッシュにアクセスする方法はありません



これが、webgl-waterデモが単純な3Dシーンでのみ実行できる理由です。屈折した光線と、均一を使用して表すことができる非常に単純な形状との交点を計算する方が簡単です。球の場合、位置(3Dベクトル)と半径(フロート)で指定できるため、この情報を均一を使用してシェーダーに渡すことができます。交差の計算には、シェーダーで簡単かつ迅速に実行できる非常に単純な計算が必要です。



シェーダーで実行される一部のレイトレース手法は、テクスチャでメッシュをレンダリングしますが、2020年には、このソリューションはWebGLでのリアルタイムレンダリングには適用できません。適切な結果を得るには、多くの光線を使用して1秒あたり60枚の画像を計算する必要があることを覚えておく必要があります。256x256 = 65536光線を使用して苛性アルカリを計算する場合、毎秒、かなりの量の交差計算を実行する必要があります(これはシーン内のメッシュの数にも依存します)。



水中環境を均一に表現し、十分な速度を維持しながら交差点を計算する方法を見つける必要があります。



環境マップの作成



動的シャドウの計算が必要な場合、シャドウマッピングはよく知られている手法です。ビデオゲームでよく使用され、見栄えがよく、実行が高速です。



シャドウマッピングは2パスの手法です。



  • まず、3Dシーンが光源の観点からレンダリングされます。このテクスチャには、フラグメントの色は含まれていませんが、フラグメントの深さ(光源とフラグメントの間の距離)が含まれています。このテクスチャはシャドウマップと呼ばれます。
  • シャドウマップは、3Dシーンをレンダリングするときに使用されます。画面にフラグメントを描画すると、光源と現在のフラグメントの間に別のフラグメントがあるかどうかがわかります。もしそうなら、現在のフラグメントが影になっていることがわかり、少し暗くする必要があります。


この優れたOpenGLチュートリアルでシャドウマッピングの詳細を読むことができます:www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping



ThreeJSでインタラクティブな例を見ることができます(Tを押して左下隅にシャドウマップを表示します):threejs.org/examples/?q = shadowm#webgl_shadowmap



ほとんどの場合、この手法はうまく機能します。シーン内の構造化されていないメッシュで機能します。



最初は、水質腐食にも同様のアプローチを使用できると思いました。つまり、最初に水中環境をテクスチャにレンダリングし、次にこのテクスチャを使用して光線と環境の交差を計算します。..。フラグメントの深さだけをレンダリングする代わりに、環境マップ内のフラグメントの位置もレンダリングします。



環境マップを作成した結果は次のとおりです。





環境マップ:XYZ位置はRGBチャネルに保存され、深度はアルファチャネルに保存されます



光線と周囲の交差を計算する方法



水中環境のマップができたので、屈折光線と環境の交差を計算する必要があります。



アルゴリズムは次のように機能します。



  • ステージ1:光線と水面の交点から開始
  • ステージ2:屈折関数を使用し屈折を計算する
  • ステージ3:現在の位置から屈折光線の方向に移動します。環境マップテクスチャの1ピクセルです。
  • ステージ4:登録されたアンビエンス深度(アンビエンステクスチャの現在のピクセルに格納されている)を現在の深度と比較します。環境の深さが現在の深さよりも大きい場合は、次に進む必要があるため、手順3を再度適用します環境の深さが現在の深さよりも浅い場合は、環境テクスチャから読み取った位置で光線が環境に衝突し、環境との交差が見つかったことを意味します。




現在の深さは環境の深さよりも浅いです:先に進む必要があります





現在の深さは周囲の深さよりも大きい:交差点が見つかりました



苛性テクスチャー



交点を見つけたら、Evan Wallaceが彼の記事で説明した手法を使用して、苛性輝度(および苛性輝度テクスチャ)を計算できます結果のテクスチャは次のようになります。





苛性ルミナンステクスチャ(サメは水面に近く、光線の収束が少ないため、苛性効果はそれほど重要ではないことに注意してください)



このテクスチャには、3D空間の各ポイントの光強度に関する情報が含まれています。完成したシーンをレンダリングすると、苛性テクスチャからこの光の強度を読み取り、次の結果を得ることができます。







この手法の実装は、Githubリポジトリ(github.com/martinRenou/threejs-caustics)にあります。あなたがそれを好きなら彼女に星を与えてください!苛性



計算の結果を確認したい場合は、次のデモを実行できます:martinrenou.github.io/threejs-caustics



この交差アルゴリズムについて



この決定は、環境テクスチャの解像度に大きく依存します。テクスチャが大きいほど、アルゴリズムの精度は高くなりますが、解決策を見つけるのに時間がかかります(解決策を見つける前に、より多くのピクセルを数えて比較する必要があります)。



また、シェーダーでテクスチャを読み取ることは、何度も行わない限り許容されます。ここでは、テクスチャから新しいピクセルを読み取り続けるループを作成しますが、これはお勧めしません。



さらに、whileループはWebGLでは許可さていません。(そして正当な理由で)したがって、コンパイラーによって拡張できるforループにアルゴリズムを実装する必要があります。これは、コンパイル時に既知のループ終了条件(通常は「最大反復」値)が必要であることを意味します。これにより、最大試行回数内に解決策が見つからない場合、解決策の検索を停止します。屈折があまりにも重要である場合、この制限は不正確な苛性結果につながります。



私たちの手法は、Evan Wallaceが提案した単純化されたアプローチほど高速ではありませんが、本格的なレイトレース手法よりもはるかに柔軟性があり、リアルタイムレンダリングにも使用できます。ただし、速度は、光の方向、屈折の明るさ、周囲のテクスチャの解像度など、いくつかの条件に依存します。



デモレビューを閉じる



この記事では、水の苛性を計算する方法について説明しましたが、デモでは他の手法を使用しました。



水面をレンダリングするときは、スカイボックステクスチャとキューブマップを使用して反射を取得しました。また、スクリーンスペースでの単純な屈折を使用して水面に屈折を適用しました(スクリーンスペースでの反射と屈折に関するこの記事参照)。この手法は物理的に正しくありませんが、視覚的に説得力があり、高速です。また、よりリアルな色収差を追加しました。



方法論をさらに改善するためのアイデアがさらにあります。



  • 苛性アルカリの色収差:現在、水面に色収差を適用していますが、この影響は水中苛性アルカリでも見られるはずです。
  • 大量の水に光が散乱する。
  • MartinGerardとAlanWolfがTwitterアドバイスしたよう、階層環境マップ(交差点を見つけるためのクワッドツリーとして使用されます)を使用してパフォーマンスを向上させることができます。彼らはまた、屈折光線の観点から環境マップをレンダリングすることをアドバイスしました(完全にフラットであると仮定)。これにより、パフォーマンスは照明の入射角に依存しなくなります。


謝辞



現実的なリアルタイムの水の視覚化に関するこの作業は、QuantStackで実施されERDCによって資金提供されました



All Articles