GPU-технології, використані у цій хвилі
Нові симуляції
Mandelbulb GLSL Raymarching
Raymarching 3D-фрактала GLSL-фрагментним шейдером. Триплексна степінь n, оцінювач відстані de=0.5·log(r)·r/dr. 96 кроків + нормалі скінченних різниць + м'які тіні + AO. Повзунок степені 2–12. 4 палітри.
Губка Менгера GLSL SDF
Ітеративне SDF-згортання: згортання до одиничного куба, віднімання хрестоподібної порожнини, масштабування ×3, повтор. 3 режими: суцільний/каркас/рентген. Розмірність Хаусдорфа log20/log3 ≈ 2,7268. Площа поверхні → 0 при ітераціях → ∞.
Кротовина 3D GLSL-шейдер
Кротовина Морріса-Торна, яку можна перетнути. Параболоїд Флемма r(ℓ)=√(b₀²+ℓ²). GLSL-змішування неба двох всесвітів. 3 режими: вкладення/від першої особи/поперечний переріз. Анімація пульсації горловини.
Суперформа Three.js Shader
Суперформула Гіліса r(φ)=(|cos(mφ/4)/a|^n2+|sin(mφ/4)/b|^n3)^(−1/n1) як сферичний добуток. Жива BufferGeometry 300×300. 8 пресетів. Матовий/металевий, каркас, збереження PNG.
Частинки GPGPU WebGL2 GPGPU
Ping-pong GPGPU: 2×2 WebGLRenderTargets (RGBA FloatType) для позицій і швидкостей. Обчислювальний фрагментний шейдер. 4 поведінки: шум завихрення/N-тіла/boids/Лоренц. До 250 000 частинок.
Mandelbulb: raymarching 3D-фрактала
Mandelbulb — найуспішніший 3D-аналог множини Мандельброта. Він використовує "триплексну" алгебру — спосіб множення 3D-векторів через піднесення сферичних координат до степеня — щоб узагальнити двовимірну ітерацію z → z^n + c на три виміри.
Триплексна степінь та оцінювач відстані
Ітерація для степеня n відображає точку (r, θ, φ) у сферичних координатах у:
r_new = r^n
theta_new = n * theta
phi_new = n * phi
x_new = r_new * sin(theta_new) * cos(phi_new) + cx
y_new = r_new * cos(theta_new) + cy
z_new = r_new * sin(theta_new) * sin(phi_new) + cz
Оцінювач відстані використовує величину похідної dr, що накопичується разом з r під час ітерації: dr = n·r^(n-1)·dr + 1. Нижня межа відстані до множини — de = 0.5 · |log(r)| · r / dr. Цей безпечний розмір кроку дозволяє алгоритму raymarching швидко просуватись крізь порожній простір і сповільнюватись поблизу поверхні — sphere tracing, а не марш фіксованими кроками.
Затінення: нормалі, тіні та ambient occlusion
Нормалі оцінюються скінченними різницями: обчислюємо SDE у шести сусідніх точках (±ε по кожній осі) і беремо градієнт. М'які тіні наближаються, просуваючи додатковий промінь до джерела світла й відслідковуючи мінімальне співвідношення розміру кроку до відстані по дорозі. Ambient occlusion вибирає п'ять відстаней вздовж нормалі поверхні й обчислює, наскільки поверхня загинається сама на себе — вгнуті щілини темніші.
- Повзунок степені n від 2 (класичний Mandelbulb) до 12 (все гостріший)
- 96 кроків raymarching на піксель; ранній вихід, коли відстань < 0,001
- 4 колірні палітри: Inferno (вогняна), Viridis (наукова), Mono (чиста), Iris (фіолетово-блакитна)
- Перетягування мишею для обертання; прокрутка для наближення до деталей поверхні
Губка Менгера: ітеративне SDF-згортання
Губка Менгера визначається через рекурсивне віднімання: беремо куб, ділимо його на 3×3×3 = 27 підкубів, видаляємо 7 центральних хрестоподібних частин (центри граней і центр тіла) і повторюємо на кожному з підкубів, що залишились. Після нескінченної кількості ітерацій результат має розмірність Хаусдорфа log(20)/log(3) ≈ 2,7268 — менше за 3D, але більше за 2D.
GLSL SDF через ітеративне згортання
Функція знакової відстані для губки обчислюється повністю всередині фрагментного шейдера елегантною ітеративною технікою згортання:
for (int i = 0; i < iterations; i++) {
p = abs(p); // згортання у позитивний октант
if (p.x < p.y) p.xy = p.yx; // сортування осей
if (p.x < p.z) p.xz = p.zx;
if (p.y < p.z) p.yz = p.zy;
p = p * 3.0 - 2.0; // масштабування та зміщення
if (p.z < -1.0) p.z += 2.0; // згортання назад
}
float d = (length(max(abs(p) - 1.0, 0.0))) / pow(3.0, float(iterations));
Кожна зовнішня ітерація подвоює ефективну глибину ітерацій. Отриманий SDF можна trace-ити sphere-tracing'ом з інтерактивною частотою кадрів для до 6 ітерацій губки.
Три режими рендерингу та математичні властивості
- Суцільний: стандартний raymarching з затіненням за Фонгом та ambient occlusion — розкриває вигадливу текстуру поверхні
- Каркас: рендер країв ізоповерхні SDF з невеликим зміщенням — виявляє кубічну самоподібність
- Рентген: накопичення густини вздовж кожного променя пропорційно відстані "мало не влучив" — показує внутрішню структуру, як КТ-скан
Панель математичних властивостей показує живі значення: загальну площу поверхні (6·(5/9)^n·20^n → 0 при n → ∞ — поверхня зменшується на кожному кроці) та формулу розмірності Хаусдорфа log(N_залишених)/log(коефіцієнт_масштабу) = log(20)/log(3).
Кротовина 3D: шейдер вкладення простору-часу
Кротовина Морріса-Торна, яку можна перетнути, — гіпотетичний розв'язок рівнянь поля Ейнштейна, що з'єднує дві віддалені області простору-часу через "горловину". Її неможливо створити без екзотичної матерії (від'ємної густини енергії), але вона дає ефектну візуалізацію та справжній урок диференціальної геометрії.
Вкладення параболоїда Флемма
Просторова геометрія кротовини в певний момент часу описується параболоїдом Флемма. У екваторіальній площині власна радіальна відстань ℓ відображається у евклідів радіус вкладення r(ℓ) = √(b₀² + ℓ²) та висоту z(ℓ) = b₀ · arcsinh(ℓ/b₀), де b₀ — радіус горловини. Це дає дві розтрублені вирви, з'єднані при r = b₀.
GLSL-шейдер: небо двох всесвітів та горловина
Фрагментний шейдер визначає, з якого боку горловини опиниться промінь, інтегруючи геодезичний шлях крізь вкладення. "Небо" кожного всесвіту — дискретна множина кольорових зірок; два всесвіти мають різні кольорові температури. При зменшенні b₀ (вужча горловина) крізь отвір видно більше неба кожного всесвіту. Градієнт доплерівського синього зсуву поблизу горловини дає візуальне відчуття гравітаційної глибини. Три режими:
- Вкладення: вигляд від третьої особи на всю поверхню параболоїда, що обертається у просторі
- Від першої особи: політ крізь горловину — спостерігайте, як один всесвіт поступається місцем іншому
- Поперечний переріз: зріз крізь екваторіальну площину, що показує два з'єднані листи
- Повзунок b₀ (радіус горловини): від вузького отвору до широкого тунелю
- Анімація пульсації горловини: періодичні коливання b₀
- Густина зірок та кольорова температура незалежні для кожного всесвіту
Суперформа: геометрія суперформули Гіліса
Суперформула Йохана Гіліса 2003 року — це єдине параметричне рівняння, що генерує надзвичайну різноманітність форм — від платонових тіл до квітів та інопланетних геометрій — варіюючи шість параметрів. Застосована як сферичний добуток (один раз для широти θ, один раз для довготи φ), вона створює 3D-поверхні надзвичайної багатоманітності.
Суперформула та сферичний добуток
Полярний радіус у 2D:
r(φ) = (|cos(m·φ/4) / a|^n2 + |sin(m·φ/4) / b|^n3)^(-1/n1)
Для 3D сферичного добутку обчислюємо r₁(θ) та r₂(φ) незалежно. Точка 3D-поверхні:
x = r1(θ) · cos(θ) · r2(φ) · cos(φ)
y = r1(θ) · sin(θ) · r2(φ) · cos(φ)
z = r2(φ) · sin(φ)
Сітка — це решітка UV-точок 300×300, кожна обчислюється незалежно й завантажується в Three.js BufferGeometry. Зміна параметрів перебудовує геометрію за один прохід JavaScript приблизно за 20 мс.
Вісім пресетів та система матеріалів
- Сфера: m=0, n1=n2=n3=1, a=b=1 — базова форма
- Зірка: m=5, n1=1, n2=n3=2 — п'ятикутна гостра форма
- Скручений бублик: асиметричні параметри n з нецілим m — самоперетинний варіант тора
- Корона: високе m, низьке n1 — зазубрена екваторіальна смуга
- Подушка: низьке m, високе n — надутий квадратний переріз
- Діамант: m=4, усі n рівні — октаедричне наближення
- Квітка: m=7, низьке n1, високе n2/n3 — форма з розходящимися пелюстками
- Гриб: асиметричні параметри θ/φ — форма з капелюшком і ніжкою
- Матовий та металевий режими PBR-матеріалу
- Перемикач накладання каркасу
- Кнопка збереження PNG захоплює поточний вигляд у повній роздільності канвасу
- Усі шість параметрів суперформули доступні як живі повзунки
Частинки GPGPU: 250 000 частинок на GPU
General-Purpose GPU computing (GPGPU) перепрофільовує графічний конвеєр у масово паралельний обчислювальний двигун. Кожен "піксель" текстури рендеру з плаваючою комою зберігає стан однієї частинки. Фрагментний шейдер, що "малює" цю текстуру, насправді обчислює новий стан частинки — і, читаючи з однієї текстури під час запису в іншу (ping-pong), ми можемо симулювати сотні тисяч частинок з інтерактивною частотою кадрів повністю на GPU.
Архітектура ping-pong
Симуляція підтримує дві пари WebGLRenderTargets типу RGBA FloatType: одну для позицій (x, y, z, вік) і одну для швидкостей (vx, vy, vz, маса). Щокадру:
- Прив'язується "обчислювальний" шейдер із поточними текстурами позицій/швидкостей як uniforms
- Рендериться повноекранний квад у наступні текстури позицій/швидкостей
- Поточна й наступна текстури міняються місцями (ping ↔ pong)
- Частинки рендеряться як GL_POINTS, вибірка позиції з поточної текстури відбувається у вершинному шейдері
Дані ніколи не покидають GPU між кадрами — CPU лише задає початкові умови й підкручує uniforms.
Чотири поведінки частинок
- Шум завихрення: поле швидкостей, виведене з ротора градієнта 3D Simplex-шуму — бездивергентне, тож частинки ніколи не збиваються в купу й не розсіюються; утворює плавні звивисті потоки
- N-тільні атрактори: 4 фіксовані точки-атрактори з
F = m/r²·n̂плюс квадратичний опір; частинки обертаються хаотичними вісімками - Наближення boids: правила розділення, вирівнювання та згуртованості, закодовані як зважені відстанню функції поля локальної густини, отримані з текстури позицій
- Атрактор Лоренца: кожна частинка незалежно інтегрує ОДУ Лоренца
(ẋ=σ(y-x), ẏ=rx-y-xz, ż=xy-bz)із трохи іншою початковою умовою — атрактор-метелик, візуалізований густиною частинок
- Повзунок N: від 1000 до 250 000 частинок (текстура 512×512)
- Перевірка підтримки WebGL2: коректне запасне повідомлення на непідтримуваних пристроях
- Розмір точки й прозорість реагують на вік частинки для затухання при народженні/загибелі
- Лічильник FPS показує обчислювальну вартість GPU на різних рівнях N
Інженерні нотатки: WebGL2 та текстури з плаваючою комою
Щоб змусити 250 000 частинок працювати коректно, довелось розв'язати кілька специфічних для WebGL2 проблем:
Точність текстур з плаваючою комою
WebGL2 підтримує OES_texture_float (32-бітні RGBA-текстури з плаваючою комою) як базову можливість, усуваючи танці з розширеннями, потрібні у WebGL1. Проте лінійна фільтрація текстур з плаваючою комою досі вимагає OES_texture_float_linear — GPGPU-симуляція частинок використовує вибірку найближчого сусіда, оскільки кожна частинка відповідає рівно одному тексел.
MRT та одночасне оновлення позиції/швидкості
Одночасне оновлення позиції та швидкості без використання швидкості з попереднього кадру вимагало б читання й запису в ту саму текстуру — неможливо у WebGL. Рішення — окремі текстури позицій і швидкостей, кожна у своїй парі ping-pong. Обчислювальний шейдер читає обидві з "поточного" набору й записує обидві у "наступний", зберігаючи весь стан узгодженим.
Індексація частинок
UV-координата кожної частинки в текстурі обчислюється з її індексу: uv = vec2(float(index % WIDTH), float(index / WIDTH)) / float(WIDTH). Вершинний шейдер читає цю UV з текстури позицій і використовує результат як gl_Position. При 250 000 частинок у текстурі 500×500 кожен тексел — це частинка.
Що далі
Це була хвиля 73 — усі 5 симуляцій, усі нативні для GPU. Маючи вже 638 живих симуляцій у всіх категоріях, ми робимо паузу для консолідації: виправляємо мобільні макети, додаємо відсутні клавіатурні скорочення й розширюємо пошуковий індекс перед стартом хвилі 74. Перегляньте архів блогу, щоб побачити всі попередні девлоги.