转载自: 羊羊2035
原文:几何向量:计算光线折射refract向量
光线折射的产生,其实是因为光线通过不同介质所产生的“偏折”现象。
ps:一般情况下,我们认为真空中(或者说空气中)折射率n = 1,毕竟我们起码要选取一个标准阀值,来推导其他介质的折射率,这里直接规定真空(空气)为标准1,那么其他介质的折射率都比真空要大,也就是>1。
我们通过建立单位圆辅助计算,通过标量推导出向量的模长,然后通过向量平行和同异方向推导出向量,推出OB = OD + DB = -|cosβ|OE + |sinβ/sinθ|(AO + |cosθ|* OE),推导到这里,我们就把OB的公式用cos sin以及入射光向量AO和介质法向量N来计算了,这时候我们就思考如何计算cosβ cosθ,让这两个值使用已知的n1,n2,AO,OE来替换,如下图:
我们通过斯涅尔定律:n1 * sinθ = n2 * sinβ得到sinβ = n1/n2 * sinθ
点积的概念推出cosθ
cosβ的推导可以先试用sinβ替换,然后替换斯涅尔定律中计算的sinβ得到cosθ的表示,
这个时候我们所有的未知量就全部通过已知值(n1,n2,AO,OE)来表示了,如下图
代码实现:
// Snell's law
// https://blog.csdn.net/yinhun2012/article/details/79472364
Vec3f refract(const Vec3f& I, const Vec3f& N,float &refractive_index) {
float cosi = -std::max(-1.0f, std::min(1.f, I*N));
float etai = 1, etat = refractive_index;
Vec3f n = N;
if (cosi < 0) {
// if the ray is inside the object,
// swap the indices and invert the normal to get the correct result
cosi = -cosi;
std::swap(etai, etat); n = -N;
}
float eta = etai / etat;
float k = 1 - eta*eta*(1 - cosi*cosi);
return k < 0 ? Vec3f(0, 0, 0) : I*eta + n*(eta * cosi - sqrtf(k));
}