← На главную

Как симулировать несжимаемую жидкость на C: диффузия, проекция, адвекция

30.05.2026 16:46 · hackernews

В 2005 году автор магистерской диссертации по быстрой 3D-симуляции жидкости решил объяснить метод простыми словами. В основе лежат уравнения Навье-Стокса, но он их почти не трогает — слишком сложно. Вместо этого он разбивает жидкость на ячейки-кубы. У каждой ячейки есть скорость и плотность. Симуляция работает только с несжимаемой жидкостью (как вода), а под плотностью везде подразумевается плотность красителя, а не самой жидкости — это ключевой момент, который автор сам понял через полгода.

Для данных используется 1D-массив и макрос IX(x, y, z), который превращает три координаты в индекс. Всё хранится в структуре FluidCube: размер size, шаг по времени dt, коэффициенты диффузии и вязкости, массивы для плотности, трёх компонент скорости и их старых копий. Для создания, удаления и добавления плотности или скорости есть функции FluidCubeCreate, FluidCubeFree, FluidCubeAddDensity и FluidCubeAddVelocity.

Главный шаг симуляции — FluidCubeStep. Он выполняет три операции. Сначала diffuse — краситель и скорость растекаются. Затем project — компенсирует сжатие, чтобы жидкость оставалась несжимаемой (расходимость равна нулю). Потом advect — всё двигается вместе с потоком: берётся скорость в ячейке, трассируется назад по времени, и новое значение интерполируется из соседних ячеек. Для скорости эти три шага повторяются дважды, для плотности — один раз.

Вспомогательные функции: set_bnd — отражает граничные ячейки, чтобы жидкость не вытекала за пределы куба. Параметр b указывает, с каким типом данных работаем (0 — плотность, 1–3 — компоненты скорости). Для x-скорости граничная ячейка берётся с обратным знаком, для остальных — такой же. lin_solve — загадочная функция, которая решает систему линейных уравнений итерациями (в примере — четыре итерации). Именно её используют diffuse и project.

Готовую симуляцию сложно отобразить: 3D-сетка не поддерживается обычным графическим API. Автор предлагает три выхода: переписать симуляцию в 2D и рисовать как картинку, показывать только один 2D-срез куба, или сделать сетку настолько мелкой, чтобы рисовать по одному полигону на ячейку без потери производительности. Код полностью доступен на сайте.

Читать оригинал →