§ Теория
Сегодня рассмотрю как найти преломленный луч. Преломление это когда луч проходит из плотности одной среды в другую не под тем же самым углом.Рассмотрим картинку. Есть луч, который падает под углом по отношению к нормали. Луч может как отражаться, так и преломляться. Когда луч преломляется, часть света уходит далее, при этом преломленный луч отклоняется относительно нормали и приобретает угол .
Существует формула закона Снеллиуса:
Здесь n1 и n2 это коэффициент преломления среды. Угол падения и угол преломления . Например воздух имеет меньший коэффициент, чем вода.
- Для воды — 1.333
- Воздух — 1.000
- Алмаз — 2.419
§ Вывод формулы
Задача найти вектор A'L'. Первое, что нужно найти это вектор OA. Это делается путем поиска через нормаль .
Теперь, чтобы найти вектор LA, потребуется вычесть A из L, то есть .
Теперь важный момент. Известно, что:
В итоге, получается, что найти A'L' можно так:
Но так можно найти только вектор A'L', но не OA'. Чтобы найти вектор OA', потребуется вычислить:
Итоговый вектор будет равен:
§ Второй вывод
Если OL равен 1 и нормаль тоже равна 1, то тогда , а также .Поскольку знаем, чему равен
То можно узнать, чему равен , а именно
Из следующей формулы можно вывести, чему будет равен :
После преобразования
Теперь надо найти, чему будет равен :
Итоговый вектор будет равен:
Я бы не сказал, что это оптимальное решение, но что делать.
Причем, важный момент — это работать будет только если подкоренное выражение не отрицательное. Если оно отрицательное, то происходит полное внутреннее отражение.
Бывает такой случай, когда . Это значит, что произошло то, что произошло — при преломлении луч не преломился, а отразился.
§ Код
Этот код для того, чтобы проиллюстрировать отражение и преломление одновременно.float length(vec3 n) { return sqrt(n.x*n.x + n.y*n.y + n.z*n.z); } // Нормализация вектора vec3 normalize(vec3 n) { float l = length(n); l = l ? 1/l : 1; return {l*n.x, l*n.y, l*n.z}; } // q - преломление n2/n1 void render(vec3 L, vec3 N, float n1, float n2) { float S = 90; float q = n1 / n2; N = normalize(N); L = normalize(L); // Нормаль и подложка под нее app->line(160 - N.y*S, 100 - N.x*S, 160, 100, 0x606060); // Подложка app->line(160 + N.y*S, 100 + N.x*S, 160, 100, 0x606060); // Подложка app->line(160 + N.x*S, 100 - N.y*S, 160, 100, 0xc0c000); // +Нормаль app->line(160 - N.x*S, 100 + N.y*S, 160, 100, 0x808000); // -Нормаль app->line(160 + L.x*S, 100 - L.y*S, 160, 100, 0xf0f0f0); // Луч // Вычисление <N,L> float cosa = N.x*L.x + N.y*L.y + N.z*L.z; float refl = 1 - q*q + q*q*cosa*cosa; float cosb = refl >= 0 ? sqrt(refl) : 0; vec3 K = {N.x*cosa - L.x, N.y*cosa - L.y, N.z*cosa - L.z}; vec3 R = {K.x + N.x*cosa, K.y + N.y*cosa, K.z + N.z*cosa}; vec3 F = {K.x*q - N.x*cosb, K.y*q - N.y*cosb, K.z*q - N.z*cosb}; // Построение луча отражения app->line(160 + R.x*S, 100 - R.y*S, 160, 100, 0x00f000); // Преломление. Если преломление превышает 90 градусов, то полное отражение if (refl >= 0) app->line(160 + F.x*S, 100 - F.y*S, 160, 100, 0x80f080); }