Godot ゲームエンジンによるナヴィエ-ストークスの流体シミュレーション解説

2026/05/27 7:15

Godot ゲームエンジンによるナヴィエ-ストークスの流体シミュレーション解説

RSS: https://news.ycombinator.com/rss

要約

日本語訳:

このリポジトリは、主に流体力学のロジックを学ぶための教育用ツールとして設計された、Godot エンジン内での専用の CPU 専用流体シミュレーションデモを提供します。即座にゲームへの展開向けのパフォーマンス最適化が目的ではないことに留意してください。2026 年 5 月 19 日に rskupnik によって開発され、このプロジェクトは AI で生成されたコードを使用することを厳格に避け、手動でのテキスト/コード作成のみを用いています(AI は研究目的のみに利用されます)。また、基礎となる作品として Jos Stam の「Real-Time Fluid Dynamics for Games」と Mike Ash の「Fluid Simulation for Dummies」を引用しています。実装は、ベクトル速度場によって輸送されるスカラー密度場上の Navier-Stokes 方程式を解き、16×16 のグリッド(ボーダーを含む)とカスタムインデックスを持つ一次元配列、拡散および圧力投影にガウス・ザイデル緩和を用いること、シミュレーション段階に応じて調整される特定のリテンション率を使用することで、これらの概念を実現しています。これらは、クリック操作で密度を追加し、ドラッグ操作で密度/速度を注入するユーザーとの相互作用によって実行されるロケット火炎のエフェクトのシミュレーションを通じて示されます。現在の実装は最適化が施されておらず、完全に CPU で動作しますが、物理学の数式とデータ構造(水平・垂直速度用の別々の配列を含む)に関する深い理解を容易にするためであり、将来の計画としてこのロジックを GPU 計算シェーダへ移植して高速化するまでの明確な出発点となります。

本文

Godot で実装したリアルタイム流体シミュレーション解説

2026 年 5 月 19 日の投稿に基づく、ゲーム開発向けの流体力シミュレーション実装の日本語訳と整理です。


💡 導入と前提条件

本プロジェクトは学習および理解を最優先に設計されたものです。パフォーマンスの最適化(GPU 計算や極限までの変数削減)よりも、コードの可読性と仕組みの明確さを重視しています。

重要なお知らせ

  • 著者プロフィール: 私は数学者ではありません。説明に誤りがある場合は Bluesky で DM を送るか、メールしてください。
  • 使用ツール: Godot エンジンを使用(GDScript)。
  • コードベース: 全リポジトリは github.com/rskupnik/godot-fluid-simulation-demo で確認可能。コミット履歴と各章のコードスナップショットが用意されています。
  • AI 利用状況: 記事全文・図・動画・コードはすべて人手で作成。AI は研究支援のみ使用しました。

推奨学習資料

  1. Jos Stam: Real-Time Fluid Dynamic for Games (論文)
  2. Mike Ash: Fluid Simulation for Dummies (チュートリアル系)

🧱 基礎:グリッドとデータ構造

流体シミュレーションの根幹は、ナビエ・ストークス方程式に基づいた物理モデルです。ここでは、計算精度よりも速度を犠牲にして「ゲームで使える」程度の品質を目指します。

シミュレーションのコスト削減手法

  1. グリッドサイズ: コーストを抑えるため、大きなセルを持つ比較的小さなグリッドを使用。
  2. 時間ステップ: シミュレーションを進化させる時間を任意に設定(
    delta
    )し、高精度計算をしない。
  3. 近似手法: 高ス・セidel 緩和法など、完全解より近似で「十分」な結果を得る手法を採用。

データ構造とグリッド定義

コードは単一のスクリプト

FluidGrid
に含まれます。配列は二次元ではなく、アクセスしやすくするために**単一次元の配列(1D Array)**を使用しています。

@export var N := 16              # グリッドのセル数 (Nx)
@export var cell_size := 32      # セルごとのピクセルサイズ
var size := 0                    # 全セル数 (後で計算)

データ配列の詳細

境界(ボーダー)処理のため、有効領域だけでなく周囲も用意し、実質

(N+2) x (N+2)
のサイズにします。

変数名格納内容備考
density
物質の量(密度)0.0 (空) 〜 1.0 (満ち)
u
水平方向速度 ($x$ 軸)
v
垂直方向速度 ($y$ 軸)Godot では Y が下向き増加
_prev
前フレームの値計算中のデータ改ざんを防ぐため(スナップショット)

ヘルパー関数:インデックス変換

二次元座標

(i, j)
を単一配列へのインデックスに変換する関数です。

func IX(i: int, j: int) -> int:
  return i + (N + 2) * j

グリッドの描画 (
_draw
)

各セルを

Rect2
として描画し、境界色と内部色の区別を行います。


💧 「fluid」シミュレーションの実装ステップ

以下の順序で機能を追加していくことで、視覚的な流体が動き出します。

1. マウスでの密度注入

マウスクリック(ドラッグ)時に特定のセルに密度

+1.0
を加えます。

func _input(event):
  if event is InputEventMouseButton and event.pressed:
    var cell := cell_from_mouse(to_local(event.position))
    # ボーダー内側のみ対象 (1 から N)
    if cell.x >= 1 and cell.x <= N and cell.y >= 1 and cell.y <= N:
      density[IX(cell.x, cell.y)] += 1.0

2. 密度のフェードアウト (
fade_density
)

グリッドが白く染まるのを防ぐため、時間経過とともに密度を減衰させます。 注意:

delta
(フレーム間隔)を掛けることで FPS に依存しない速度を実現します。

func fade_density(delta: float) -> void:
  for i in range(1, N + 1):
    for j in range(1, N + 1):
      density[IX(i, j)] = max(0.0, density[IX(i, j)] - density_fade_rate * delta)

3. 速度矢印の表示 (
_draw_velocity_arrows
)

現在

u
,
v
はすべて 0 ですが、視覚化のために矢印を描画する関数を追加します。

  • velocity_draw_scale
    : 矢印の描画スケール(シミュレーション挙動には影響しない)。

4. マウスドラッグによる速度注入

クリックだけでなく、マウスを動かした分だけ速度ベクトルを追加できます。これにより、密度を持たない空気の流れ(風のよう)も表現できます。

# ドラッグ時の相対移動量に基づき速度を追加
var delta_velocity := event.relative * velocity_add_scale
u[idx] += delta_velocity.x
v[idx] += delta_velocity.y

5. 速度のフェードアウト (
fade_velocity
)

密度と同様に、速度も時間をかけて減衰させます。

move_toward
を使用して滑らかに消えるようにします。


🌊 魔法を起こさせるメカニズム:流体運動の導入

ここからがシミュレーションの核心です。「密度を静止状態」から「流れる状態」に変化させる処理を加えます。

🔀 密度の対流 (
advect_density
)

原理: 現在のセルの密度は、「過去(時間を遡った先)のある場所」にある値を引き継いでいます。

  1. 現在の速度ベクトルを「逆転させて時間を遡る」。
  2. その位置を特定し、その点を取り囲む 4 つの隣接セルから双線形補間を行い密度を取得。

このアプローチを採用する理由:

  • 速度場の連続性: 速度が瞬間的に方向を変えることは物理的に起きないため、単純な前方追跡よりも逆算の方が安定する。
  • 計算精度とコストのバランス: 近似方程式を使用し、計算負荷を下げつつ十分な品質を得る。
# スナップショットを取得(計算中の改ざんを防ぐ)
func copy_density_to_prev() -> void:
  for idx in range(size): density_prev[idx] = density[idx]

# 対流処理実行
func advect_density(delta: float) -> void:
  # 時間を遡ってソース座標を計算
  var x := i - dt0 * u[idx]
  var y := j - dt0 * v[idx]
  
  # その位置を取り囲む 4 つのセルを見つける
  # 双線形補間を行い、密度値を取得して現在のセルに設定

🎨 密度拡散 (
diffuse_density
)

原理: 墨を水に垂らしたような現象。各セルから近傍セルへ均等に関係なく値が広がる。

  • 手法: Gauss-Seidel 緩和法を使用(推測を繰り返して値を滑らかにする)。
  • 効果: 濃淡の境界を滑らかにし、自然な雲のような動きを作る。
# 繰り返し反復 (Iterations) を行うことで拡散効果を強める
func diffuse_density(delta: float) -> void:
  for k in range(20): # 20 回繰り返す
    # 各セルの値を、近傍セルの平均に近づける計算を行う

🧱 境界の設定 (
set_bnd
)

シミュレーション領域の壁際(ボーダー)で流体が外へ流出しないように処理します。

  • 壁としての振る舞い: 壁面に向かってくる速度成分を反転させる(反射)。
  • 密度の保持: 壁際のセルは、内部のセルの値をコピーまたは平均化して設定。

⚡ 速度そのものを流す:速度対流と圧力投影

密度だけでなく、「風」そのものも動き回らせる必要があります。また、流体が圧縮されたり膨張したりしないよう(非圧縮流体)する処理を加えます。

💨 速度拡散 (
diffuse_velocity
)

密度と同様に、速度場も時間とともに緩やかに変化・広がる特性を与えます。これにより、角ばった流れを滑らかにし、粘性効果を実装します。

🌪️ 速度対流 (
advect_velocity
)

原理: 「空気の流れ」そのものを追跡する。現在ある位置に空気が存在しているのは、「過去のある位置から移動して来たから」。

  • 注意点: この処理では実際の速度配列
    u
    ,
    v
    を書き換えるため、計算を安定させるために
    _prev
    スナップショットを使用します(密度対流とは異なります)。

⚖️ 圧力投影 (
project_velocity
):非圧縮性の担保

流体が「膨らむ・潰れる」のを防ぐための最も重要なステップです。

  1. 発散計算: 各セルから流れ出る量と流入する量の差を計算(密度の増減につながる)。
  2. 圧力解像 (Poisson Solver): 発散が 0 になるような圧力場を計算するために、Gauss-Seidel 緩和法を用いて反復計算を行う。
  3. 速度修正: 算出された圧力勾配に基づき、各セルの速度ベクトルを微調整し、「膨張/収縮」させない方向へ修正する。
func _process(delta: float) -> void:
  # --- 速度処理 (先頭と真ん中に必ず project_velocity を挿入) ---
  copy_velocity_to_prev()
  diffuse_velocity(delta)
  advect_velocity(delta)
  project_velocity() # ★ 圧力による修正
  
  # --- 密度処理 ---
  copy_density_to_prev()
  diffuse_density(delta)
  advect_density(delta)
  
  # --- フェードアウト & 再描画 ---
  fade_density(delta)
  queue_redraw()

🚀 ゲーム開発での応用と展望

実装が完成した状態

この時点で、マウスで塗った「墨(密度)」が風(速度)を引いて広がり、壁に衝突して跳ねる、粘度を持つ流体的な動きを実現しました。

次のステップ:シェーダーとの連携

現在のシミュレーションは CPU 上で計算されており、複雑な図形を描くには限界があります。今後の方向性として以下のアプローチが考えられます。

  1. Compute Shader への移植:
    • GPU を活用することで、より高分解能のグリッドや更大規模のシミュレーションを実現可能になる。
    • GPU で計算したデータを CPU から取得し、それをシェーダーに渡し、美しい視覚効果を合成する。
  2. コンテキストとしての使用:
    • ロケット排気: エンジンの炎を流体として吐き出させる。
    • 魔法・特殊効果: 重力の歪みやエネルギー波など、物理挙動を持つエフェクト生成。

まとめ

  • 密度と速度を別々に扱い、互いに影響し合うシミュレーションを行う。
  • 対流で移動させ、拡散で滑らかにする。
  • 圧力投影によって非圧縮性を確保し、壁面で適切な反射を実現する。

この手法は、数式を深く理解せずとも、「物理的な挙動」をゲーム内に組み込むための強力なツールとなります。

同じ日のほかのニュース

一覧に戻る →

2026/05/31 8:26

マイクロソフトが永続ライセンス付きのオフライン製品の機能制限を実施

## Japanese Translation: 2026 年 7 月 13 日、Microsoft は macOS および iOS 向けに永続ライセンス付きの Office アプリに対して重要な制限を施行し、Word、Excel、PowerPoint、Outlook、OneDrive のライセンス証明書が期限切れになった時点でユーザーを閲覧専用モードにロックします。これにより、これらの特定のデバイスでの完全な編集機能は事実上終了し、2023 年のサポート終了日以降もデータ安全性が恒久的に維持されるとの以前のアシuranse と大きな決別を示します。Windows や Android バージョンはこの証明書問題の影響を受けない一方、この機能劣化は Apple ハードウェア上の古い永続ライセンスを特定して対象としています。ユーザーは現在のソフトウェアを再インストールしても問題を修復することはできず、代わりに無料の Web アプリへ移行するか、新しいサブスクリプションベースのライセンスを購入する(または Office Home 2024 永続ライセンスの可能性もある)、あるいは LibreOffice、OnlyOffice、Pages のような代替スイートへ切り替える必要があります。Microsoft は Office 2021 ユーザーに対して限られた解決策を提供しており、バージョン 16.83 以降へのアップグレードが可能で、これは 2026 年 10 月までの新たな証明書を含んでいます。しかし、サポート終了済み Office 2019 を実行している場合に必要な閾値未満のハードキャップのため、有効なアップデートパスは存在しません。Microsoft は 2026 年 5 月中旬から影響を受ける顧客へ電子メールを送信し、代替策として無料の Microsoft 365 Personal トライアルを提供しています。この動きは、業界全体の広範な傾向を浮き彫りにしており、永続ライセンスは継続的な有料サポートや特定の技術パッチなしに長期的な機能維持のためにはますます信頼性が低いという事実を示しています。

2026/05/31 5:40

専門分野の知見こそが、本物の護城河であった。

## Japanese Translation: ソフトウェア開発における核心的な変化は、システム構築と検証を分離する「エージェント型 AI」の台頭であり、これにより深いドメイン知識が生のコーディングスキルよりも新たな最重要資産となっています。従来のエンジニアは、失敗を観察したり専門家の影を追うことによりドメインを習得しましたが、この育成型の道筋は現在、多くの場合に封殺されています。今日のエージェントは、物流スケジュールや給与計算規則といった複雑な業界データを調和させることで得られる暗黙的理解を再現するのが困難です。そのため、特定のバックグラウンドを持たない一般向けエンジニアは、AI が生成したコードを効果的に検証することができず、コーディングを行わない者もこれらの強力なツールを効果的に活用できません。未来の景観では、機械的なコーディング能力と深い業界専門知識を併せ持ち、「二重判断」を行う専門家—すなわちコードの健全性と事実の正確性の両方を保証する人材—が優位に立つでしょう。AI による抽象モデルの機械的翻訳がもはや独占的ではなくなる中、現実世界の法規制の実証済みモデルこそが決定的な資産となります。この移行は物流配車オペレーターなどの役割を再定義し、清らかなコードの生成が主たる制約ではなくなった時代において、ドメインの複雑性に対する人間の洞察が不可欠であることを証明しています。 ## Text to translate: No significant improvements are needed as the original summary is concise, accurate, and comprehensive. ## Summary: The central shift in software development is the emergence of Agentic AI, which separates building systems from verifying them, making deep domain knowledge the new most valuable asset rather than raw coding skills. Unlike traditional engineers who learned domains by observing failures and shadowing experts—a developmental path now blocked for many—today's agents struggle to replicate the tacit understanding gained from reconciling complex industry data like logistics schedules or payroll rules. Consequently, generalist engineers cannot effectively verify AI-generated code without this specific background, while non-coders remain unable to leverage these powerful tools effectively. The future landscape favors professionals who combine mechanical coding proficiency with profound industry expertise to perform "double judgment," ensuring both code soundness and factual accuracy. As mechanical translation of abstract models becomes less exclusive due to AI, verified models of real-world regulations become the critical asset. This transition elevates roles like logistics dispatchers, proving that human insight into domain complexities is irreplaceable in an age where generating clean code is no longer the primary constraint.

2026/05/29 11:49

砂漠の真ん中に貝殻を見つけた

## Japanese Translation: サウジアラビアのアルガット砂漠の崖基部で発見された目立つ岩石は、海岸線近くにはなく、地質学的証拠によるとジュラ紀(約 1500 万年前)には海洋の底だった場所で、貝殻のように見える。地域のパレオントロジー専門家がいなかったため、著者は DIY データ分析を用いてその系統を同定した。ほぼ 8,000 の貝殻種を含むデータセット(Zhang et al.)において、著者は各輪郭を 256x2 マトリクスとして表現し、輪郭間の二乗ユークリッド距離を計算し、主成分分析(PCA)を適用した。得られた 2 次元潜在空間では、負の PC1 値は丸みを、正の値は尖り具合を示し、PC2 は対称性または質量分布を捕捉していた。化石は最も近似的に*Sphincterochila candidissima*に類似しており、これは約 3800 万年前にのみ出現した種であり、ジュラ紀にはいなかった。PCA に基づくと形状はほぼ同一だが、時間的ギャップにより直接的な祖先関係は否定され、代わりに収斂進化が示唆される:無縁の生物が同様の環境圧力の影響を受けて類似の形態を発達させた。このプロジェクトでは、また shell.hawzen.me というインタラクティブなツールを提供しており、これは遠隔地の非専門家でも専門的な科学ツールにアクセスできるようにし、研究の民主化と地球の歴史に関する深遠な事実の解明を実現することを示している。

Godot ゲームエンジンによるナヴィエ-ストークスの流体シミュレーション解説 | そっか~ニュース