はじめに
前回作った白い地面に、夜空と水面、光るキューブを加えました。今回は、空と水の境目、水面の波、キューブの光を順に調整した記録です。完成イメージの月と大きなフレームは、まだありません。

遠くからの一枚絵だけで判断せず、歩いて水面を見下ろし、キューブにも近づいて確かめました。まずは現在の実画面から。

今回の採用版(試行11)。月と大きなフレームは未実装です。
空の色と水平線をつなぐ
最初は水平線の紫が強く、空と水の境目が帯のように見えました。そこで参考画像の空を高さごとに採色し、水平線近くは明るい青紫、上へ行くほど暗い青になるよう、複数のRGB値を記録しました。画面の各点が水平線からどれだけ上にあるかを求め、その角度に合う色をシェーダーで滑らかにつないでいます。 参考画像を背景として貼っているわけではありません。
大気のように見えるのは、水平線へ向かう緩やかな明るさの変化に、左右のわずかな明暗と淡い色むらを重ねているためです。実際の大気による光の散乱を計算しているわけではありません。さらに、遠くの水面の色を空の水平線付近の色へ近づけることで、境目をなじませています。試行3から4では、この紫の強さと水面の見え方を調整しました。
星も画像ではなく、空のシェーダーがまばらな小さな点として描きます。位置は固定し、水平線の近くでは薄くしています。星自体は周囲を照らす光源ではありません。

試行3・4の実画面と、空の色を水面の遠景へつなぐ方法。キューブを置く前の状態です。
水面の波を、形と反射で比べる
水面は二通り試しました。試行5では網目を細かく分け、頂点を上下させて波の形を作りました。輪郭まで動かせますが、この試行では水面に24,448枚の三角形を使いました。
今回ほしかったのは、大きく盛り上がる波よりも、静かに揺れる反射です。試行6では水面を三角形2枚の平面に戻し、ノーマルマップで光を受ける向きを変えました。これは細かな傾きの情報を記録した画像で、水面の形を変えずに反射を揺らせます。現在は起動時に作る512×512の画像を、向きや速さを変えて4層に重ねています。
ノーマルマップはシェーダーの代わりではありません。今回の水面シェーダーは色と反射を計算し、ノーマルマップは傾きを伝える画像です。 両方の試行でシェーダーを使っています。

試行5と6の実画面。空を含むシーン全体の三角形は24,449→3、Draw Callsは2のままです。形の数は減りましたが、ノーマルマップの読み込みと画素ごとの計算は残るため、この数値だけで高速化したとは言えません。
歩くと足元に波紋が出て、着地時には少し強く広がります。
光は、近くからも確かめる
次に約3mのキューブを置き、光と水面への映り込みを加えました。輪郭を強く光らせ、Bloomという処理で周囲に柔らかくにじませています。水面に見える光は、キューブの位置と反射の向きを使って計算した表現です。
遠くからはまとまって見えますが、近づくと発光する縦線が途切れ、白い斑も見えました。PCの全画面では処理する画素数が増え、動きも重く感じます。そこで、遠景だけで判断せず、近くと大きな画面の両方で確認しました。
画面が大きくなっても描く画素が増えすぎないよう上限を設け、発光線と光のにじみを調整しました。一度は軽量画質を初期設定にしましたが、光の輪郭が粗くなったため標準に戻しています。軽量画質は必要なときに選べる設定として残しました。見た目を良くする調整と、重くしないための調整を行き来した部分です。

現在の接近画面。修正前では発光線の途切れや白い斑が目立ちました。
今回の到達点
白い地面だけだった空間が、夜空と水面のある場所に変わりました。空と水の色を水平線でつなぎ、水面は細かい形より反射の揺れを選んでいます。キューブの光も、近くで見たときの輪郭を直しました。
一方で、遠くからの見た目だけでは、近づいたときの乱れや全画面での負荷に気づけません。次にモデルを置くときも、実際に歩いて、距離や画面サイズを変えながら確かめます。
作り方と測定値をもう少し見る
空の色は、参考画像の物体や文字を避けて高さごとに採りました。上部RGB(18, 33, 61)、中ほど(42, 66, 112)、水平線付近(142, 145, 202)など、複数の採色点と仰角を対応させています。シェーダーは各画素の視線方向から仰角を求め、間の色を補間します。左右方向の明るさとごく弱いノイズも重ねるため、採色したRGBがそのまま最終画面の値になるわけではありません。
星は視線方向を細かな区画に分け、疑似乱数で一部の区画だけに小さな点を描きます。配置は静的で、水平線に近づくほど消えます。星の点は光源ではなく、水面の空の反射計算にも含めていません。水面の遠景は、カメラから離れるほど同じ空の水平線色へ混ぜています。
水面は試行5で形を細かく分割し、試行6では平らな面に戻しました。現在は起動時に作る512×512のノーマルマップ1枚を、向き・大きさ・流れる速さの異なる4層で使っています。形は三角形2枚のままですが、テクスチャや画素の処理は残るため、三角形の数だけで速さは決まりません。
キューブの発光は明るい輪郭とBloom処理を組み合わせ、水面への映り込みはシェーダーで近似しています。光の輪郭と画素数の確認を、SellaとNeroの図にまとめました。

現在の試行11では標準画質が初期値です。標準は最大DPR 1.5・約360万画素、任意の軽量画質は最大DPR 1・約200万画素に制限します。大きな画面では、上限を守るため実際のDPRが下がります。
Braveの本番ビルドで、CSS 2284×1371 pxを再現した画面を30秒ずつ測定しました。待機中は平均118.96 FPS、歩行中は119.47 FPS。フレーム間隔のp95はいずれも11.30msでした。p95は、測った間隔の95%がその値以下だったことを示します。待機中のDraw Callsは17、Trianglesは29です。画面左上の短時間のFPS表示とは別の測定値になります。
試行9は実ウィンドウ、試行11は指定した画面サイズの再現で、条件が完全には一致しません。FPSの小さな差から個々の変更の効果は断定できず、スマートフォン実機の測定もまだ行っていません。水面までの試行記録と全画面での測定記録に、詳しい条件と数値を残しています。
SHARE
この記事を共有
AUTHOR

SHIZUYA
Creative Developer / Designer / Musician
フロントエンド開発、3D表現、AI活用、音楽制作などを横断しながら、美しく機能する体験を追求しています。



