レッスンへ移動
ae↗独立系
デザインスタジオ
← レッスン一覧へ

数学とアート / レッスン01

水の
つくり方。

海は、一本の線から始まる。
リズムを与え、ほかの波を重ね、
最後に光を当ててみよう。

ひとつの波から始める 読む時間:6分 · 動かして学ぶ実験

手の中に、小さな海。

値を変えて、違いを見よう。
重なり合う波でできた青い水面 04 / 光を加える大きな世界の、小さな研究
観察のポイント

形はメッシュ表示と同じです。細かな波紋が輝きを散らし、深さが色を変えます。波を止めてから太陽を動かしてみましょう。

色 = 透過光 + 空の反射 + 太陽の光

考え方 / 単純なルールから生まれる美しさ

一滴ずつ
すべてを 描かなくても。

ゲームの水は、平らな面からつくれます。関数で各点の高さを決め、時間で模様を動かし、光で濡れた印象を与える。小さな判断を重ねながら、その仕組みをつくりましょう。

これは見た目を設計した波のモデルです。池や海の水面づくりの出発点になりますが、液体の流れ、水しぶき、砕ける波をシミュレーションするものではありません。

01 / リズム

サイン波から始める。

サイン波は−1から1の間を滑らかに繰り返します。 A を掛けて高さを決めます。空間方向の広がりは波長 λで決め、時間を引くと波の山が移動します。

h(x, t) = A sin(2πx / λ − ωt)
A · 振幅
基準の水面からの高さ。山から谷までは2Aです。
λ · 波長
隣り合う山の間の距離。
ω · 角周波数
位相が変化する速さ。単位はラジアン毎秒です。

波の形が進んでも、水がそのまま前へ流れるわけではありません。点を見てください。この高さ場のモデルでは、横方向に固定された場所が、山の通過に合わせて上下するだけです。

02 / 変化を重ねる

波を出会わせる。

ひとつの波だけでは、少し整いすぎて見えます。間隔、方向、速度の違う小さな波を重ねましょう。山どうしは強め合い、山と谷は打ち消し合います。少数の単純なルールから、複雑な形が生まれます。

H(x, z, t) = h₁ + h₂ + h₃ + h₄

この例は4つの波を使います。どれも同じサインのルールに従い、それぞれの進行方向の距離を使います: dxx + dzz。2つの方向成分は単位ベクトルです。このデモでは、小さな波の振幅を主な波の45%、22%、10%にしています。

03 / 形

線を面にする。

小さな三角形でできた平らなシートを想像してください。各頂点の横方向の座標から、 Hを計算し、その値を縦方向の位置に使います。頂点は横に動かず、高さだけを変えます。

position = (x, H(x, z, t), z)

メッシュ表示で構造が見えます。長い波は輪郭を、短い波は細部を決めます。細かな波を滑らかに見せるには、十分な頂点数が必要です。この研究では128 × 128のセルを使い、メッシュでは4本ごとの線を表示して読みやすくしています。

04 / 水らしさ

光が、水に見せる。

青く塗るだけでは、青いシートです。重要な手がかりは、 面の法線です。それぞれの小さな面が向く方向を表します。波の傾きから計算するので、形をつくる数学と光の反応が一致します。

n = normalise(−∂H/∂x, 1, −∂H/∂z)

光の計算に使う法線に細かな波紋を加え、空の反射、鋭い太陽の輝き、水を通る光を組み合わせます。大きな4つの波が形を決めます。フレネル近似により、浅い角度で見るほど反射が強くなります。波を止めても、太陽を回すと輝きが動きます。

屈折によって、砂と小石を手続き的に描いた底の見え方がずれます。深さは青よりも暖色の光を速く吸収します。動く明るい模様は、水面が集める光であるコースティクスを表します。「水の仕上げ」で透明度、波紋、深さを比べましょう。透明度は、側面を通る光も含めた屈折経路の吸収を変えます。側面は説明用の断面で、コースティクスも手続き的な近似です。岸の泡、砕ける波、物体との相互作用は、より本格的な描画で加える別の要素です。

自分で試す / ひとつの式、ふたつの雰囲気

池をつくる。
次に、 天気を変える。

低い振幅と広い間隔で、静かな池をつくりましょう。振幅を上げ、山を近づけると、にぎやかな水面になります。両方を止めて、傾きの違いを見てください。

次に、波の高さをゼロにします。面は平らになっても、空を反射できます。水らしさのどこまでが形で、どこからが光でしょうか?

自分の海へ戻る ↑
持ち帰れる、小さなコード +

これは高さを計算する中心部分です。描画では、光のためにその微分も計算します。デモは任意のシーン単位を使い、動きのスライダーは時間の速さを変えます。

function wave(x, z, time, A, wavelength, omega, direction) {
  const distance = direction.x * x + direction.z * z;
  const phase = 2 * Math.PI * distance / wavelength - omega * time;
  return A * Math.sin(phase);
}

// Add waves with different amplitudes, wavelengths and directions.
const height = waves.reduce((sum, w) =>
  sum + wave(x, z, time, w.A, w.wavelength, w.omega, w.direction), 0);
// Move the vertex to (x, height, z).

物理的な深水波では、角周波数と波長はω² = gkで結ばれ、k = 2π/λです。ここでは見やすい研究になるよう周波数を選んでいます。スライダーは表現のための操作です。