イントロニュートンプロトコル:4キロバイトに収まるもの

画像






私は最近、PC 4kイントロカテゴリのRevision 2019デモシーンに参加し、私のイントロが1位になりました。 私はコーディングとグラフィックスを行い、ディキサンは音楽を作成しました。 競争の基本的なルールは、サイズがわずか4096バイトの実行可能ファイルまたはWebサイトを作成することです。 これは、数学とアルゴリズムを使用してすべてを生成する必要があることを意味します。 他の方法では、画像、ビデオ、音声をこのような小さなメモリに詰め込むことはできません。 この記事では、Newtonイントロイントロのレンダリングパイプラインについて説明します。 以下で完成した結果を見ることができます。または、 ここをクリックしてリビジョンでどのように表示されるかを確認するか、コンテストに参加したイントロをコメントしてダウンロードするためにpouetアクセスしてください 。 競合他社の作業と修正については、 こちらをご覧ください





レイマーチング距離フィールド手法は、わずか数行のコードで複雑なフォームを指定できるため、4kイントロの分野では非常に人気があります。 ただし、このアプローチの欠点は実行速度です。 シーンをレンダリングするには、光線とシーンの交点を見つけ、まずカメラからの光線などを確認し、次にオブジェクトから光源への光線を決定して照明を計算する必要があります。 レイマーチングを使用する場合、これらの交点は1ステップで見つけることができません。ビームに沿って多くの小さなステップを実行し、各ポイントですべてのオブジェクトを評価する必要があります。 一方、レイトレーシングを使用する場合、各オブジェクトを1回だけチェックすることで正確な交差点を見つけることができますが、使用できる形状のセットは非常に限られています:レイとの交差点を計算するには、各タイプの式が必要です。



このイントロでは、非常に正確な照明をシミュレートしたかった。 このために、シーン内で数百万の光線を反射する必要があったため、光線追跡はこの効果を達成するための論理的な選択肢であると思われました。 光線と球の交点は非常に簡単に計算されるため、私は自分を1つの図、つまり球に限定しました。 イントロの壁でさえ、実際には非常に大きな球体です。 さらに、物理学のシミュレーションを簡素化しました。 球体間の衝突のみを考慮すれば十分でした。



4096バイトに収まるコードの量を説明するため、完成したイントロの完全なソースコードを以下に示しました。 末尾のHTMLを除くすべての部分は、PNG画像としてエンコードされ、より小さなサイズに圧縮されます。 この圧縮なしでは、コードはほぼ8900バイトを占有します。 Synthと呼ばれる部分は、 SoundBoxの簡易バージョンです。 Google Closure CompilerShader Minifierを使用して、この最小化形式でコードをパッケージ化しました。 最終的に、ほとんどすべてがJsExeを使用してPNGに圧縮されます。 完全なコンパイルパイプラインは、以前の4kイントロCore Criticalのソースコードで見ることができます。これは、ここで紹介したものと完全に一致するためです。









音楽とシンセサイザーは、Javascriptで完全に実装されています。 WebGLの部分は2つの部分に分かれています(コード内で緑色で強調表示されています)。 彼女はレンダリングパイプラインを設定します。 物理要素とレイトレーサー要素はGLSLシェーダーです。 残りのコードはPNG画像にエンコードされ、HTMLは変更されていない結果の画像の最後に追加されます。 ブラウザは画像データを無視し、HTMLコードのみを実行します。HTMLコードは、PNGをデコードしてjavascriptに戻し、実行します。



レンダリングパイプライン



以下の画像は、レンダリングパイプラインを示しています。 2つの部分で構成されています。 パイプラインの最初の部分は物理シミュレーターです。 イントロシーンには、部屋内で互いに衝突する50個の球が含まれています。 部屋自体は6つの球体で構成され、その一部は他の球体よりも小さく、より湾曲した壁を作成します。 コーナーの2つの垂直光源も球体です。つまり、シーン内の合計58個の球体です。 パイプラインの2番目の部分は、シーンをレンダリングするレイトレーサーです。 次の図は、時間tでの1フレームのレンダリングを示しています。 物理シミュレーションは、前のフレーム(t-1)を取り、現在の状態をシミュレートします。 レイトレーサーは、現在の位置と前のフレーム(速度チャネル用)の位置を取得して、シーンをレンダリングします。 その後、前の5つのフレームと現在のフレームを後処理で組み合わせて、歪みとノイズを減らし、完成した結果を作成します。









時間tでのフレームのレンダリング。



物理的な部分は非常に簡単です。インターネットでは、球体のプリミティブシミュレーションの作成に関する多くのチュートリアルを見つけることができます。 位置、半径、速度、質量は1 x 58の解像度で2つのテクスチャに保存されます。複数のレンダーターゲットへのレンダリングを可能にするWebgl 2機能を使用して、2つのテクスチャのデータを同時に記録します。 同じ機能がレイトレーサーによって使用され、3つのテクスチャが作成されます。 WebglはNVidia RTXまたはDirectX Raytracing(DXR)レイトレーシングAPIへのアクセスを提供しないため、すべてがゼロから行われます。



レイトレーサー



レイトレーシング自体はかなり原始的な手法です。 レイをシーンに放出し、4回反射します。光源に到達すると、反射の色が蓄積されます。 そうしないと、黒になります。 4096バイト(音楽、シンセサイザー、物理学、レンダリングを含む)では、複雑な加速レイトレーシング構造を作成する余地がありません。 したがって、ブルートフォース法を使用します。つまり、球体の一部を除外する最適化を行うことなく、各光線の57個の球体すべてをチェックします(前壁は除外されます)。 これは、1080pの解像度で1秒あたり60フレームを提供するために、ピクセルあたり2〜6本の光線またはサンプルのみを放出できることを意味します。 これは、滑らかな照明を作成するのに十分近い距離です。









ピクセルごとに1サンプル。









ピクセルあたり6サンプル。



これに対処する方法は? 最初に光線追跡アルゴリズムを調査しましたが、すでに完全に単純化されています。 光線が球体の内側から始まる場合を排除することで、パフォーマンスをわずかに向上させることができました。そのような場合は、透明効果が存在する場合にのみ適用可能であり、シーンには不透明なオブジェクトしかありませんでした。 その後、不要な分岐を回避するために、各if条件を個別のステートメントに結合しました。「不要な」計算にもかかわらず、このアプローチは条件ステートメントの束よりも高速です。 サンプリングパターンを改善することもできます。ランダムに光線を放出する代わりに、より均一なパターンでシーン全体に光線を分散させることができます。 残念ながら、これは役に立たず、私が試したすべてのアルゴリズムで波状のアーチファクトを引き起こしました。 ただし、このアプローチは静止画像に良い結果をもたらしました。 その結果、完全にランダムな分布を使用することに戻りました。



隣接するピクセルの照明は非常に似ているはずなので、それらを使用して単一のピクセルの照明を計算してみませんか? テクスチャをぼかすのではなく、ライティングのみを使用するため、別々のチャネルでレンダリングする必要があります。 また、オブジェクトをぼかしたくないので、どのピクセルが簡単にぼかしられるかを知るために、オブジェクトの識別子を考慮する必要があります。 光を反射するオブジェクトがあり、明確な反射が必要なため、ビームが衝突する最初のオブジェクトのIDを見つけるだけでは不十分です。 純粋な反射素材の特殊なケースを使用して、オブジェクト識別子チャンネルの反射に表示される最初と2番目のオブジェクトのIDも含めました。 この場合、ぼかしにより、オブジェクトの境界を維持しながら、反射するオブジェクトの照明を滑らかにすることができます。









テクスチャチャンネル、ぼかす必要はありません。









ここで、赤のチャネルには最初のオブジェクトのIDが含まれ、緑で-2番目、青で-3番目です。 実際には、それらはすべて浮動小数点形式の単一の値にエンコードされ、整数部にはオブジェクトの識別子が格納され、小数部は粗さを示します:332211.RR。



シーンには粗さの異なるオブジェクトが存在するため(一部の領域は粗く、光は他の領域に散乱し、3番目には鏡面反射があります)、粗さを保存してぼかし半径を制御します。 シーンには細かいディテールがありません。そのため、ぼかしには逆正方形の形でぼかしのある大きな50x50コアを使用しました。 ワールド空間を考慮していません(より正確な結果を得るためにこれを実現できます)。ある方向に角度を付けて配置されたサーフェスでは、より大きなエリアを侵食するためです。 このようなぼかしはかなり滑らかな画像を作成しますが、アーチファクトは、特に動きの中ではっきりと見えます。









ぼかしとまだ顕著なアーティファクトを持つ照明チャンネル。 この画像では、後壁にぼやけたドットが表示されています。これは、2番目に反射したオブジェクトの識別子に関する小さなバグが原因です(光線はシーンから出ます)。 完成した画像では、テクスチャチャネルから明確な反射が取得されるため、これはあまり目立ちません。 光源もぼやけますが、私はこの効果が好きで、そのままにしました。 必要に応じて、素材に応じてオブジェクトの識別子を変更することでこれを防ぐことができます。



オブジェクトがシーン内にあり、シーンを撮影するカメラがゆっくりと移動する場合、各フレームの照明は一定のままでなければなりません。 したがって、画面のXY座標だけでなくぼかしも実行できます。 時間内にぼやけることがあります。 照明が100ミリ秒であまり変化しないと仮定すると、6フレームで平均化できます。 しかし、この時間枠の間、オブジェクトとカメラはまだある程度の距離を移動するため、6フレームの平均を単純に計算すると、非常にぼやけた画像が作成されます。 ただし、すべてのオブジェクトとカメラが前のマップのどこにあるかはわかっているため、画面空間で速度ベクトルを計算できます。 これは一時的な再投影と呼ばれます。 時間tにピクセルがある場合、このピクセルの速度を取得して、時間t-1の位置を計算し、時間t-1のピクセルが時間t-2の位置にあることを計算できます。 5フレーム戻ります。 画面スペースのぼかしとは異なり、ここでは各フレームに同じ重みを使用しました。 一時的な「ぼかし」のために、すべてのフレーム間の色を平均しただけです。









オブジェクトとカメラの動きに基づいて、ピクセルが最後のフレームのどこにあったかを報告するピクセル速度チャンネル。









オブジェクトのジョイントブラーを回避するために、オブジェクト識別子のチャネルを再び使用します。 この場合、ビームが衝突した最初のオブジェクトのみを考慮します。 これにより、オブジェクト内でアンチエイリアスが提供されます。 反射で。



もちろん、前のフレームではピクセルが表示されない場合があります。 別のオブジェクトに隠れているか、カメラの視野から外れている可能性があります。 そのような場合、以前の情報を使用することはできません。 このチェックはフレームごとに個別に実行されるため、ピクセルあたり1〜6個のサンプルまたはフレームを取得し、可能なものを使用します。 下の図は、遅いオブジェクトの場合、これはそれほど深刻な問題ではないことを示しています。









オブジェクトが移動してシーンの新しい部分を開くとき、これらの部分で平均化するための6フレームの情報はありません。 この画像は、6フレーム(白)の領域と、欠落している領域(徐々に暗くなる)を示しています。 輪郭の外観は、各フレームのピクセルのサンプリング位置のランダム化と、最初のサンプルからオブジェクトの識別子を取得するという事実によって引き起こされます。









ぼやけた照明は6フレームにわたって平均化されます。 アーティファクトはほとんど目に見えず、結果は時間とともに安定します。これは、各フレームで、照明が考慮される6つの変更のうち1つのフレームのみであるためです。



これらすべてを組み合わせて、完成した画像を取得します。 照明は隣接するピクセルにぼやけていますが、テクスチャと反射は鮮明なままです。 その後、これらすべてが6つのフレーム間で平均化され、時間とともにさらに滑らかで安定した画像が作成されます。









完成した画像。



ピクセルごとに複数のサンプルを平均化したため、減衰アーティファクトは依然として顕著です。ただし、最初の交差点のオブジェクト識別子と速度のチャネルを使用しました。 これを修正して、反射を滑らかにし、最初のサンプルと一致しない場合、または少なくとも最初の衝突が順番に一致しない場合はサンプルを破棄することができます。 実際には、トレースはほとんど見えないので、それらを削除することはありません。 速度のチャネルとオブジェクト識別子を平滑化できないため、オブジェクトの境界も歪んでいます。 画像全体を2160p解像度でレンダリングし、さらに1080pにスケールを縮小する可能性を検討しましたが、私のNVidia GTX 980tiはこのような解像度を60fpsの周波数で処理できないため、このアイデアを放棄することにしました。



一般に、私はイントロがどのようになったかに非常に満足しています。 私は自分が考えていたすべてをそこに詰め込むことができ、小さなバグにもかかわらず、最終結果は非常に高品質でした。 将来的には、バグを修正し、アンチエイリアスを改善することができます。 また、透明度、モーションブラー、さまざまな形状、オブジェクト変換などの機能を試してみる価値があります。










All Articles