§ Теория

Сегодня рассмотрю как найти преломленный луч. Преломление это когда луч проходит из плотности одной среды в другую не под тем же самым углом.

Рассмотрим картинку. Есть луч, который падает под углом \alpha по отношению к нормали. Луч может как отражаться, так и преломляться. Когда луч преломляется, часть света уходит далее, при этом преломленный луч отклоняется относительно нормали и приобретает угол \beta .
Существует формула закона Снеллиуса:
n_1 sin \alpha = n_2 sin \beta
Здесь n1 и n2 это коэффициент преломления среды. Угол падения \alpha и угол преломления \beta . Например воздух имеет меньший коэффициент, чем вода.
  • Для воды — 1.333
  • Воздух — 1.000
  • Алмаз — 2.419
Это просто список для примера. Еще стоит отметить, что помимо преломления существует и дисперсия, когда луч отклоняется тем больше, чем короче длина волны.

§ Вывод формулы


Задача найти вектор A'L'. Первое, что нужно найти это вектор OA. Это делается путем поиска через нормаль \vec{N}\langle \vec{N},\vec{L}\rangle .
Теперь, чтобы найти вектор LA, потребуется вычесть A из L, то есть \vec{LA} = A - L = \vec{N}\langle \vec{N},\vec{L}\rangle - L .
Теперь важный момент. Известно, что:
  • sin \alpha = \frac{AL}{OL} = AL
  • sin \beta = \frac{A'L'}{OL'} = A'L'
Поскольку полагаем, что OL и OL' равны 1, то переписываем так:
\frac{sin \alpha}{sin \beta} = \frac{AL}{A'L'} = \frac{n_2}{n_1}
В итоге, получается, что найти A'L' можно так:
A'L' = \frac{n_1}{n_2} LA = \frac{n_1}{n_2} ( \vec{N}\langle \vec{N},\vec{L}\rangle - L )
Но так можно найти только вектор A'L', но не OA'. Чтобы найти вектор OA', потребуется вычислить:
OA' = - \vec{N} \sqrt{1 - |AL'|^2}
Итоговый вектор будет равен: \vec{OL'} = \vec{A'L} + \vec{OA'}

§ Второй вывод

Если OL равен 1 и нормаль тоже равна 1, то тогда sin \alpha = AL , а также cos\beta = OA' .
Поскольку знаем, чему равен cos \alpha = \langle \vec{N},\vec{L}\rangle
То можно узнать, чему равен sin^2 \alpha + cos^2 \alpha = 1 , а именно
sin\alpha = \sqrt{1 - {\langle \vec{N},\vec{L}\rangle}^2}
Из следующей формулы можно вывести, чему будет равен sin \beta :
\frac{sin \alpha}{sin \beta} = \frac{n_2}{n_1} = \frac{1}{q}
После преобразования
sin \beta = \frac{n_1}{n_2} sin \alpha = q \sqrt{1 - {\langle \vec{N},\vec{L}\rangle}^2}
Теперь надо найти, чему будет равен cos \beta :
cos \beta = \sqrt{1 - sin^2 \beta} = \sqrt{1 - q^2 (1 - {\langle \vec{N},\vec{L}\rangle}^2 ) }) = \sqrt{1 - q^2 + q^2 {\langle \vec{N},\vec{L}\rangle}^2 }
Итоговый вектор будет равен:
OL' = (\vec{N} {\langle \vec{N},\vec{L}\rangle} - L) q - \vec{N} cos \beta
Я бы не сказал, что это оптимальное решение, но что делать.
Причем, важный момент — это работать будет только если подкоренное выражение не отрицательное. Если оно отрицательное, то происходит полное внутреннее отражение.
Бывает такой случай, когда sin \beta > 1 . Это значит, что произошло то, что произошло — при преломлении луч не преломился, а отразился.

§ Код

Этот код для того, чтобы проиллюстрировать отражение и преломление одновременно.
1float length(vec3 n) {
2    return sqrt(n.x*n.x + n.y*n.y + n.z*n.z);
3}
4
5// Нормализация вектора
6vec3 normalize(vec3 n) {
7
8    float l = length(n);
9          l = l ? 1/l : 1;
10
11    return {l*n.x, l*n.y, l*n.z};
12}
13
14// q - преломление n2/n1
15void render(vec3 L, vec3 N, float n1, float n2) {
16
17    float S = 90;
18    float q = n1 / n2;
19
20    N = normalize(N);
21    L = normalize(L);
22
23    // Нормаль и подложка под нее
24    app->line(160 - N.y*S, 100 - N.x*S, 160, 100, 0x606060); // Подложка
25    app->line(160 + N.y*S, 100 + N.x*S, 160, 100, 0x606060); // Подложка
26    app->line(160 + N.x*S, 100 - N.y*S, 160, 100, 0xc0c000); // +Нормаль
27    app->line(160 - N.x*S, 100 + N.y*S, 160, 100, 0x808000); // -Нормаль
28    app->line(160 + L.x*S, 100 - L.y*S, 160, 100, 0xf0f0f0); // Луч
29
30    // Вычисление <N,L>
31    float cosa = N.x*L.x + N.y*L.y + N.z*L.z;
32    float refl = 1 - q*q + q*q*cosa*cosa;
33    float cosb = refl >= 0 ? sqrt(refl) : 0;
34
35    vec3 K = {N.x*cosa - L.x,      N.y*cosa - L.y,      N.z*cosa - L.z};
36    vec3 R = {K.x      + N.x*cosa, K.y      + N.y*cosa, K.z      + N.z*cosa};
37    vec3 F = {K.x*q    - N.x*cosb, K.y*q    - N.y*cosb, K.z*q    - N.z*cosb};
38
39    // Построение луча отражения
40    app->line(160 + R.x*S, 100 - R.y*S, 160, 100, 0x00f000);
41
42    // Преломление. Если преломление превышает 90 градусов, то полное отражение
43    if (refl >= 0) app->line(160 + F.x*S, 100 - F.y*S, 160, 100, 0x80f080);
44}