С преломлениями в трассировке лучей все немного сложнее, чем с отражениями. Во-первых, нужно учитывать эффект так называемого "полного внутреннего отражения", который говорит о том, что при больших углах преломления не происходит и свет полностью отражается. Во-вторых, необходимо получить коэффициенты преломления двух сред: той, из которой луч выходит той, в которую он входит. Как это сделать имея информацию только о поверхности, о которую ударился луч? И в третиьх, нужно учитывать, что луч прошел определенное растояние внутри преломляющего объекта, который вообще говоря может быть мутным. То есть нужно учитывать затухание (Закон Бугера — Ламберта — Бера). И наконец, неплохо бы учесть волновую природу света. Для разных длинн волн свет будет преломляться на разные углы и затухание у каждой длинны волны вообще говоря свое (коэффициенты преломления и затухания задаются для каждой длинны волны).
Разберемся сначала с первыми двумя проблемами. Если произошло полное внутреннее отражение, то преломляющий луч вообще не нужно траверсить (то есть его просто нет). Далее, как нам получить коэффициенты преломления? В общем случае это непросто. Если преломляющие объекты пересекаются друг с другом, то имея лишь информацию о том, каким материалом обладает поверхность, о которую ударился луч, невозможно сказать, в какую среду луч попадет при выходе из объекта. Не будем усложнять себе жизнь. Предположим, что все преломляющие объекты не пересекаются друг с другом а преломлющий показатель воздуха равен 1. Тогда нетрудно заметить, что если угол между нормалью к поверхности и направлением луча тупой, то луч переходит из воздуха в среду с показателем преломления материала, какорым обладает поверхность. Если же угол острый, то наоборот - луч вылетает из среды в воздух.
bool refract(float3& ray_dir, float3 a_normal, float a_matIOR)
{
float eta = 1.0f/a_matIOR; // eta = in_IOR/out_IOR
float cos_theta = -dot(a_normal, ray_dir);
if(cos_theta < 0)
{
cos_theta *= -1.0f;
a_normal *= -1.0f;
eta = 1.0f/eta;
}
float k = 1.0f - eta*eta*(1.0-cos_theta*cos_theta);
if(k >= 0.0f)
ray_dir = normalize( eta*ray_dir + (eta*cos_theta - sqrt(k))*a_normal);
return (k > 0);
}
Математика того, как рассчитывается преломление описана тут http://users.skynet.be/bdegreve/writings/reflection_transmission.pdf. Однако, код из статьи обладает тем недостатком, что он не различает случаи входа в среду и выхода из нее и никак не говорит о том, как же получить коэффициенты преломления сред. Плюс в ней видимо считается, что нормаль к поверхности всегда смотрит в ту сторону, откуда прилетел луч.
Для того чтобы использовать код из ссылки, приведенной выше, по крайней мере необходимо перед его применением инвертировать нормаль к поверхности и показатель преломления если косинус угла между номалью и направлением луча меньше нуля (угол тупой), иначе получится ерунда.
Результат вроде похоже на правду. Преломляющая стеклянная сфера расположена слева. Изображение переворачивется, как и должно быть.
|