3D/CG

NOCTE #01|夜の水面と光を作る

NOCTE制作ログ第1回。白い地面に夜空、水面、光るキューブを加えました。見え方を確かめながら一つずつ整えた過程を紹介します。
Published
Author
SHIZUYA
Updated
NOCTE #01|夜の水面と光を作る

SHARE

この記事を共有

XLINEBluesky

はじめに

前回: NOCTE #00|歩ける3D空間の土台を作る

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

SellaとNeroがモニターに表示したNOCTEの夜空と水面、光るキューブを確かめる構成イメージ

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

夜空と水面の中央に光るキューブを置いたNOCTEの現在の実画面

今回の採用版(試行11)。月と大きなフレームは未実装です。

空の色と水平線をつなぐ

最初は水平線の紫が強く、空と水の境目が帯のように見えました。そこで参考画像の空を高さごとに採色し、水平線近くは明るい青紫、上へ行くほど暗い青になるよう、複数のRGB値を記録しました。画面の各点が水平線からどれだけ上にあるかを求め、その角度に合う色をシェーダーで滑らかにつないでいます。 参考画像を背景として貼っているわけではありません。

大気のように見えるのは、水平線へ向かう緩やかな明るさの変化に、左右のわずかな明暗と淡い色むらを重ねているためです。実際の大気による光の散乱を計算しているわけではありません。さらに、遠くの水面の色を空の水平線付近の色へ近づけることで、境目をなじませています。試行3から4では、この紫の強さと水面の見え方を調整しました。

星も画像ではなく、空のシェーダーがまばらな小さな点として描きます。位置は固定し、水平線の近くでは薄くしています。星自体は周囲を照らす光源ではありません。

試行3と4の実画面を比較。上空から水平線へ空の色をつなぎ、遠くの水面を空の色になじませる仕組み

試行3・4の実画面と、空の色を水面の遠景へつなぐ方法。キューブを置く前の状態です。

水面の波を、形と反射で比べる

水面は二通り試しました。試行5では網目を細かく分け、頂点を上下させて波の形を作りました。輪郭まで動かせますが、この試行では水面に24,448枚の三角形を使いました。

今回ほしかったのは、大きく盛り上がる波よりも、静かに揺れる反射です。試行6では水面を三角形2枚の平面に戻し、ノーマルマップで光を受ける向きを変えました。これは細かな傾きの情報を記録した画像で、水面の形を変えずに反射を揺らせます。現在は起動時に作る512×512の画像を、向きや速さを変えて4層に重ねています。

ノーマルマップはシェーダーの代わりではありません。今回の水面シェーダーは色と反射を計算し、ノーマルマップは傾きを伝える画像です。 両方の試行でシェーダーを使っています。

試行5と6の実画面、細分化して波の形を動かす断面図、平面のままノーマルマップで反射の向きを変える図を比較。水面の三角形は24,448枚から2枚へ

試行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の図にまとめました。

SellaとNeroが発光線の見え方を確認する図。標準画質と軽量画質の画素数上限、30秒測定のフレーム間隔も示す

現在の試行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

この記事を共有

XLINEBluesky

AUTHOR

SHIZUYA

Creative Developer / Designer / Musician

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