【问题标题】:Ray tracing sphere reflection bug光线追踪球体反射错误
【发布时间】:2018-05-31 05:05:20
【问题描述】:

我正在尝试实现光线追踪算法,但在计算球形物体的反射光线时遇到了一些问题。看来 对于某些特定的光线,反射光线刚好穿过并与追踪光线共线。 波纹管是我如何记录射线 - 球体相交:

bool Sphere::intersectLocal(const ray & r, isect & i) const {
    Vec3d P = r.getPosition();
    Vec3d D = r.getDirection();
    //D.normalize();
    double a = dot(D, D);
    double b = 2 * dot(P, D);
    double c = dot(P, P) - 1;
    double delta = b * b - 4 * a * c;
    if (delta < 0)
        return false;
    if (delta == 0) {
        double t = -b / 2 * a;
        Vec3d Q = P + t * D;
        Vec3d N = Q;
        N.normalize();
        i.setT(t);
        i.setN(N);
        i.setObject(this);
        return true;
    }
    if (delta > 0) {
        double t1 = (-b - sqrt(delta)) / 2 * a;
        double t2 = (-b + sqrt(delta)) / 2 * a;
        double t;
        if (t1 > 0) t = t1;
        else if (t2 > 0) t = t2;
        else return false;
        Vec3d N = P + t * D;
        N.normalize();
        i.setT(t);
        i.setN(N);
        i.setObject(this);
        return true;
    }
    return false;
}

这就是我计算每个交叉点的反射光线的方法:

isect i;
if (scene - > intersect(r, i)) {
    // An intersection occured!  
    const Material & m = i.getMaterial();
    double t = i.t;
    Vec3d N = i.N;
    Vec3d I = m.shade(scene, r, i); //local illumination
    if (!m.kr(i).iszero() && depth >= 0) {
        // compute reflection direction
        Vec3d raydir = r.getDirection();
        Vec3d refldir = 2 * dot(-raydir, i.N) * i.N + raydir;
        refldir.normalize();
        ray reflectionRay = ray(r.at(i.t), refldir, ray::RayType::REFLECTION);
        Vec3d reflection = traceRay(reflectionRay, thresh, depth - 1);
        Vec3d R = reflection;
        I += R;
    }
    return I;
} else {
    // No intersection.  This ray travels to infinity, so we color
    // it according to the background color, which in this (simple) case
    // is just black.
    return Vec3d(0.0, 0.0, 0.0);
}

上面的代码似乎对于球体上光线相交的大部分点都可以正常工作,但对于其他点来说,它并没有像我预期的那样反射

【问题讨论】:

  • 图像看起来好像你在某处遇到了一些准确性问题。您是否尝试使用双精度数而不是浮点数?这里有几个比较和帮助的链接:Reflection and refraction impossible without recursive ray tracing?ray and ellipsoid intersection accuracy improvement 也使用相机相对坐标通常可以提高准确性。
  • 你说得对,问题与准确性有关。我不确定浮点数和双精度数在相等运算方面是否有任何区别,但我发现如果我沿反射方向向交点添加一个小偏移量,问题就会消失。在这里查看结果:imgur.com/a/as5wn。我的猜测是,如果我将反射光线的起点作为交点,光线有时会立即与球体相交,从而将光线反射到球体内。
  • 是的,您应该在每次相交后向新的光线位置添加一小段距离,以避免多个自身相交。在调试我链接的光线追踪器时遇到了类似的问题。好吧,射线圆相交需要二次热,并且使用 GPU 浮点数不是很精确......尤其是如果向量的大小很大......双倍会减慢速度,但会产生更好和稳定的结果
  • 你猜对了。这是光线追踪器中非常常见的问题,在实现阴影/反射时可能会发生。更常被称为“阴影-痤疮”(黑点) 原因正如你和 Spektre 想通的那样,自我交集。因此,通常建议添加一个小的浮点常量,例如 epsilon = 0.01f
  • @wandering-warrior +1 嘿嘿不知道它有英文版。我总是把它称为噪音或伪影:-)

标签: c++ opengl raytracing


【解决方案1】:

如果我没看错,这会使法线面与射线的方向相同。所以ray==normal==reflected_ray 没有任何反应。

Vec3d Q = P + t * D;
Vec3d N = Q;

【讨论】:

    【解决方案2】:

    关于浮点运算中的错误以及如何处理:

    What Every Computer Scientist Should Know About Floating-Point Arithmetic

    在这里您可以找到如何比较浮点数。搜索relative absolute compare floating,您可能会找到更多信息。

    https://floating-point-gui.de/errors/comparison/

    这是我的 C# 代码的摘录。几乎从不使用绝对比较。

    public static bool IsAlmostRelativeEquals(this double d1, double d2, double epsilon)
    {
        double absDiff = Math.Abs(d1 - d2);
    
        if (double.IsPositiveInfinity(absDiff))
            return false;
    
        if (absDiff < epsilon)
            return true;
    
        double absMax = Math.Max(Math.Abs(d1), Math.Abs(d2));
    
        return Math.Abs(d1 - d2) <= epsilon * absMax;
    }
    
    public static bool IsAlmostZero(this double d, double epsilon)
    {
        double abs = Math.Abs(d);
    
        if (double.IsPositiveInfinity(abs))
            return false;
    
        return abs < epsilon;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-26
      • 1970-01-01
      • 2019-11-29
      • 1970-01-01
      • 1970-01-01
      • 2019-11-29
      相关资源
      最近更新 更多