如何从入射向量和表面法线找到折射向量?

4

我正在用Java编写光线追踪器,尝试实现折射,但是我对此主题的信息感到困惑。如果我有一个用于入射光线的3D向量,以及一个给定为3D向量的表面法向量和两个介质的折射率,那么我需要应用哪些操作才能得到透射光线的向量?


http://en.wikipedia.org/wiki/Snell%27s_law#Vector_form - Benjy Kessler
3个回答

7

Bram de Greve写了一篇关于光线追踪中反射和折射的好文章。你可以在这里找到它。

他的实现(C++)如下所示:

Vector refract(const Vector& normal, const Vector& incident, 
               double n1, double n2) 
{
    const double n = n1 / n2;
    const double cosI = -dot(normal, incident);
    const double sinT2 = n * n * (1.0 - cosI * cosI);
    if(sinT2 > 1.0) return Vector::invalid; // TIR
    const double cosT = sqrt(1.0 - sinT2);
    return n * incident + (n * cosI - cosT) * normal;
}

这里是PBRTv3的实现(根据BSD-2-Clause许可证):
inline bool Refract(const Vector3f &wi, const Normal3f &n, Float eta,
                    Vector3f *wt) {
    // Compute $\cos \theta_\roman{t}$ using Snell's law
    Float cosThetaI = Dot(n, wi);
    Float sin2ThetaI = std::max(Float(0), Float(1 - cosThetaI * cosThetaI));
    Float sin2ThetaT = eta * eta * sin2ThetaI;

    // Handle total internal reflection for transmission
    if (sin2ThetaT >= 1) return false;
    Float cosThetaT = std::sqrt(1 - sin2ThetaT);
    *wt = eta * -wi + (eta * cosThetaI - cosThetaT) * Vector3f(n);
    return true;
}

请看我的博客,了解如何通过测试驱动的方式来实现它。


2
尽管我不得不写成cosI = abs(dot(normal, incident))才能使它正确工作,但这是一个非常有帮助的答案。 - noName

2

V_incedence成为标准化的入射向量。让n1n2成为两个表面的折射率。您需要计算V_refraction。让n成为标准化的法向量。

V_refraction = r*V_incedence + (rc - sqrt(1-Math.pow(r,2)(1-Math.pow(c,2))))n
where r = n1/n2 and c = -n dot V_incedence.

谢谢回复。第一行的意思是将V_incedence.multiply(r)的向量与n.multiply(rc - sqrt(1-r^2(1-c^2)))的向量相加吗? - user2320239
这是一个近似值吗?看起来有一些小数位的偏差。 - Josh C

1

我在我的Java光线追踪器中实现了这个,看一下https://github.com/bradforj287/brads-java-raytracer

private static double clamp(final double val, final double min, final double max) {
    return Math.max(min, Math.min(max, val));
}

private Vector3d getRefractionVector(final Vector3d I, final Vector3d N, final double ior) {
    double cosi = clamp(-1, 1, I.dot(N));
    double etai = 1, etat = ior;
    Vector3d n = N;
    if (cosi < 0) {
        cosi = -cosi;
    } else {
        double temp = etai;
        etai = etat;
        etat = temp;
        n = N.multiply(-1);
    }
    double eta = etai / etat;
    double k = 1 - (eta * eta) * (1 - (cosi * cosi));
    if (k <= 0) {
        return Vector3d.ZERO;
    } else {
        return I.multiply(eta).add(n.multiply(((eta * cosi) - Math.sqrt(k))));
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接