Каустики та фокусування хвиль
Каустики — ті яскраві хвилясті узори на дні басейну або під склянкою з водою. Вони виникають, коли промені світла фокусуються на поверхні після проходження крізь неоднорідне середовище. За ними стоїть повна картина оптики — від геометричного трасування до хвильової дифракції.
1. Геометрична vs хвильова оптика
Оптика розглядає поширення електромагнітного випромінювання. Залежно від масштабу явища використовуються два апарати:
Геометрична оптика
Λ ≪ характерний розмір. Світло описується променями — прямолінійними траєкторіями, що змінюють напрямок на межах розділу. Дифракція знехтувана.
Хвильова оптика
Λ ~ розміру деталей. Важливі інтерференція та дифракція. Описується рівнянням Гельмгольца: ∇²E + k²E = 0, де k = 2π/λ.
Зв'язок між ними
Геометрична оптика — граничний випадок хвильової при λ → 0. Каустики — місце, де геометрична оптика "ламається" через нескінченну густину променів, а хвильова дає скінченну інтенсивність.
Оптичний шлях
Принцип Ферма: промінь іде шляхом, що мінімізує оптичний шлях OPL = ∫n ds. З нього випливають закони відбиття та заломлення.
Для каустик у воді геометрична оптика чудово описує яскраві плями — хвильові ефекти дають тонкі корекції на краях. Ми почнемо з геометричного підходу.
2. Закон Снеля та показник заломлення
Коли промінь перетинає межу між середовищами з показниками заломлення n₁ і n₂, кут заломлення θ₂ визначається законом Снеля:
n₁ sin θ₁ = n₂ sin θ₂
Для води: n_вода ≈ 1.333, n_повітря = 1.000
При θ₁ = 45°: sin θ₂ = sin 45° / 1.333 ≈ 0.530 → θ₂ ≈ 32°
Векторна форма закону Снеля
Для тривимірного трасування зручно векторне формулювання. Нехай d̂ — одиничний вектор падаючого променя, n̂ — нормаль поверхні (назовні), η = n₁/n₂:
cosθ₁ = −d̂ · n̂
cosθ₂ = √(1 − η²(1 − cos²θ₁))
d_refr = η·d̂ + (η·cosθ₁ − cosθ₂)·n̂
Якщо підкорінний вираз від'ємний — повне внутрішнє відбиття: промінь не виходить з середовища. Критичний кут: sin θ_c = n₂/n₁.
3. Тонка лінза та формула лінзи
Ідеальна тонка лінза фокусує паралельний пучок у точку. Її оптична сила P = 1/f (діоптрії), де f — фокусна відстань. Зв'язок між відстанями предмета та зображення задається формулою тонкої лінзи:
1/f = 1/d_o + 1/d_i
Збільшення: M = −d_i / d_o
За формулою лінзмейкерів: 1/f = (n−1)(1/R₁ − 1/R₂)
Знаки та конвенції
| Величина | Позитивне значення | Негативне значення |
|---|---|---|
| f | Збираюча лінза | Розсіювальна лінза |
| d_o | Реальний предмет (зліва) | Віртуальний предмет |
| d_i | Реальне зображення (справа) | Віртуальне зображення (зліва) |
| M | Пряме зображення | Перевернуте зображення |
Аберації заважають ідеальному фокусу
Реальна лінза страждає від аберацій: сферичної (крайові промені фокусуються ближче до лінзи), хроматичної (різна фокусна відстань для λ), дисторсії. Саме сферична аберація породжує одну з найчастіших форм каустик — зосереджений "хвіст" яскравості вздовж осі.
4. Каустики: поверхня фокусування
Каустика (від грец. καυστικός — палюча) — це огинаюча сімейства заломлених або відбитих променів. Промені не перетинаються в одній точці через неідеальність поверхні — вони утворюють caustic surface у просторі, а перетин цієї поверхні з екраном дає яскравий візерунок.
Математика: огинаюча сімейства ліній
Сімейство ліній Φ(x, y, t) = 0, де t — параметр (наприклад, координата точки падіння на поверхню). Огинаюча задається системою:
Φ(x, y, t) = 0
∂Φ/∂t = 0
Розв'язок (x(t), y(t)) — і є каустика
Яскравість каустики
Інтенсивність I у точці на екрані пропорційна густині потоку фотонів. Якщо d_ω — тілесний кут пучка, а dA — площа плями на екрані, то I ~ dΩ/dA. На каустиці dA → 0 (промені "стискуються"), тому I → ∞ у геометричній оптиці. Хвильова оптика дає скінченний пік — фігуру Ері з характерним масштабом Δx ~ (λ·R)^(2/3).
Класифікація за теорією катастроф
Fold caustic
Один параметр. Типовий вигляд — яскрава лінія. Найпоширеніша форма каустик у воді (зморшки поверхні).
Cusp caustic
Два параметри. Загострений хвіст. Виникає біля кута плавального басейну або на дні під кутом огляду.
Swallowtail
Три параметри. Рідша форма, але видна у фокальних областях при великих амплітудах хвилі поверхні.
Elliptic umbilic
Тривимірні каустики дзеркал. Видно в чашці кави (catacaustic) або на циліндричній поверхні.
5. Коефіцієнти Френеля: відбиття та пропускання
На межі розділу частина енергії відбивається, частина проходить далі. Для монохроматичної EM-хвилі рівняння Максвелла дають точний результат — формули Френеля для s- та p-поляризацій:
r_s = (n₁cosθ₁ − n₂cosθ₂) / (n₁cosθ₁ + n₂cosθ₂)
r_p = (n₂cosθ₁ − n₁cosθ₂) / (n₂cosθ₁ + n₁cosθ₂)
R_s = |r_s|², R_p = |r_p|²
T_s = 1 − R_s, T_p = 1 − R_p (за збереженням енергії)
Апроксимація Шліка
У рендерингу часто використовують дешеву апроксимацію (Schlick 1994) для спектрального відбиття при нормальному падінні F₀ = ((n₁−n₂)/(n₁+n₂))²:
F(θ) ≈ F₀ + (1 − F₀)(1 − cosθ)⁵
Для вода/повітря: F₀ = ((1 − 1.333)/(1 + 1.333))² ≈ 0.020
Вода при нормальному падінні відбиває лише 2% світла. При θ → 90° (косе падіння) F → 1 — дзеркальна вода на горизонті.
6. Каустики у воді: хвиляста поверхня
Поверхня басейну описується висотою h(x, y, t). Промінь від сонця спрямований вертикально вниз, нормаль до поверхні відхилена на кут, що залежить від градієнта h. Заломлений промінь потрапляє в іншу точку дна — це і є каустичний узор.
Зміщення точки попадання
Для малих нахилів (∂h/∂x ≪ 1) можна лінеаризувати. Нехай поверхня має нахил ∂h/∂x у напрямку x. Нормаль: n̂ ≈ (−∂h/∂x, −∂h/∂y, 1). Відхилення заломленого вертикального променя від вертикалі:
Δx ≈ d · (n_вода − 1) · ∂h/∂x
Δy ≈ d · (n_вода − 1) · ∂h/∂y
де d — глибина води
Якщо ∂Δx/∂x + ∂Δy/∂y < 0, промені сходяться — виникає яскрава пляма. Якщо > 0 — розходяться, пляма темна. Таким чином, інтенсивність на дні пропорційна оберненому значенню якобіана відображення поверхні на дно:
I ∝ 1 / |J|, де J = ∂(Δx, Δy)/∂(x, y)
J ≈ 1 + d·(n−1)·∇²h
Каустика: де J = 0 (тобто ∇²h = −1/(d(n−1)))
Лапласіан висоти ∇²h визначає кривизну поверхні. Каустика формується там, де гребінь хвилі має достатньо велику від'ємну кривизну на опуклих ділянках.
7. Рендеринг каустик: env-map + refraction map
Є два підходи до рендерингу каустик у WebGL у реальному часі.
Метод 1: Forward photon mapping
Стріляємо N фотонів від джерела (сонця), заломлюємо через поверхню, зберігаємо точки попадання на дно в текстуру-акумулятор. Розмивання через gaussian blur дає реалістичний результат. O(N) операцій за кадр.
Метод 2: Screen-space refraction map (рекомендований)
// Vertex shader — генерація caustic UV
precision highp float;
uniform sampler2D uHeightMap; // висота поверхні
uniform float uDepth; // глибина басейну
uniform float uEta; // n_air / n_water = 0.750
attribute vec2 aTexCoord;
varying vec2 vCausticUV;
vec3 refractRay(vec3 d, vec3 n, float eta) {
float cosi = dot(-d, n);
float sin2t = eta * eta * (1.0 - cosi * cosi);
if (sin2t > 1.0) return reflect(d, n); // TIR
return eta * d + (eta * cosi - sqrt(1.0 - sin2t)) * n;
}
void main() {
vec2 uv = aTexCoord;
float h = texture2D(uHeightMap, uv).r;
// Наближення нормалі через сусідні пікселі (texel size = 1/512)
float ts = 1.0 / 512.0;
float hx = texture2D(uHeightMap, uv + vec2(ts, 0.)).r;
float hy = texture2D(uHeightMap, uv + vec2(0., ts)).r;
vec3 norm = normalize(vec3(h - hx, h - hy, ts * 20.0));
vec3 sunDir = vec3(0.0, 0.0, -1.0); // вертикальне сонце
vec3 refracted = refractRay(sunDir, norm, uEta); // 0.750 = 1/1.333
// Проецюємо на дно (уздовж refracted напрямку)
float t = (h - uDepth) / refracted.z; // дно на z = -depth
vec2 hit = uv + refracted.xy * t;
vCausticUV = hit;
gl_Position = vec4(aTexCoord * 2.0 - 1.0, 0.0, 1.0);
}
// Fragment shader — нанесення фотонних плям
precision highp float;
varying vec2 vCausticUV;
void main() {
// Кожен тексель (фотон) пише у свій цільовий пікс
// Використовуємо адитивний blending (ONE, ONE)
gl_FragColor = vec4(1.0, 0.95, 0.85, 0.015); // теплий відтінок
}
JS-код: менеджер каустик
class CausticsRenderer {
constructor(gl, resolution = 512) {
this.gl = gl;
this.res = resolution;
this.causticsTexture = this.createFramebuffer(resolution);
this.prog = this.compileProgram(CAUSTIC_VERT, CAUSTIC_FRAG);
this.buildQuadMesh(resolution); // 512×512 = 262144 quad vertices
}
render(heightTexture, depth, eta = 0.75) {
const gl = this.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, this.causticsTexture.fbo);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
// Адитивний блендінг — фотони накопичуються
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE);
gl.useProgram(this.prog);
gl.uniform1f(this.loc('uDepth'), depth);
gl.uniform1f(this.loc('uEta'), eta);
gl.bindTexture(gl.TEXTURE_2D, heightTexture);
// Кожен quad = один "фотон", малюється в ціловий UV
gl.drawArrays(gl.TRIANGLES, 0, this.res * this.res * 6);
gl.disable(gl.BLEND);
}
getTexture() { return this.causticsTexture.texture; }
}
Підключення до матеріалу дна
Отриману текстуру каустик сумуємо з базовою текстурою дна у фінальному fragment shader. Додаємо Fresnel-blend між відбитим небом та рефракцією:
color = envColor × F(θ) + refractionColor × (1 − F(θ))
color += causticsTex × lightColor × shadowFactor
8. Довгофокусні каустики та фізична оптика
Чисто геометричний підхід має обмеження: нескінченна яскравість на каустиці. Хвильова оптика замінює її на апаратну функцію Ері:
I(x) ∝ |Ai((x−x_c)/l_c)|²
l_c = (λ·R/k̈)^(1/3) — характерний масштаб каустики
Ai — функція Ейрі (розв'язок d²y/dx² = xy)
Райдужні хмари та corona
Краплі дощу діють як мікролінзи — пояснюють веселку (λ-залежне заломлення) та corona (дифракційний кільця навколо сонця). У басейні аналогом є кольорові фринжі на краях каустики при монохроматичному підсвічуванні.
Photon mapping
Класичний алгоритм Йенсена (1996): стрільба фотонів від джерела, їх зберігання у kd-дереві, density estimation при рендерингу.
Bidirectional PT
Поєднує шляхи від джерела та від камери через MIS-вагування. Набагато ефективніше для каустик від точкових джерел.
Vertex Connection & Merging
VCM/UPM — сучасний SoA алгоритм, суміщає photon mapping + bidirectional PT, ефективний у виробничих рендерерах.
GPU-caustics (screen-space)
Метод, що розглянутий у цій статті: approx real-time за рахунок обмеженої коректності (плоске дно, паралельне світло).
Зв'язок з симуляціями
Каустичний рендеринг зазвичай пов'язаний з симуляцією поверхні рідини (Gerstner Waves або SPH). Дивіться пов'язані статті нижче.