【问题标题】:Raytracer light reflection bugRaytracer 光反射错误
【发布时间】:2020-02-24 21:08:17
【问题描述】:

我正在尝试将 smallpt: Global Illumination in 99 lines of C++ 移植到 C#,当光线反射漫反射表面时,我遇到了这个奇怪的错误。有谁知道问题可能来自哪里?

This what I'm getting with 40 samples

This is what it's supposed to look like

这是我的漫反射表面代码:

if(sphere.Reflection == Sphere.ReflectionType.DIFFUSE)
        {
            double angleRand = random.NextDouble(seed) *2f*Math.PI;
            double distanceRand = random.NextDouble(seed);
            double distanceRandSqtr = Math.Sqrt(distanceRand);

            Vector3 w = surfaceNormal;
            Vector3 u = Vector3.Normalize(Vector3.Cross(Math.Abs(w.X) > .1 ? new Vector3(0f, 1f, 0f) : new Vector3(1f, 0f, 0f), w));
            Vector3 v = Vector3.Cross(w, u);

            Vector3 ref1 = Vector3.Multiply(u, (float)Math.Cos(angleRand));
            ref1 = Vector3.Multiply(ref1, (float)distanceRandSqtr);
            Vector3 ref2 = Vector3.Multiply(v, (float)Math.Sin(angleRand));
            ref2 = Vector3.Multiply(ref2, (float)distanceRandSqtr);
            Vector3 ref3 = Vector3.Multiply(w, (float)Math.Sqrt(1 - distanceRand));
            Vector3 ref4 = Vector3.Add(ref1, ref2);
            ref4 = Vector3.Add(ref4, ref3);

            Vector3 reflectionRayRand = Vector3.Normalize(ref4);

            Vector3 nextRadiance = ComputeRadiance(new Ray(intersectionPoint, reflectionRayRand), depth, seed);

            Vector3 result = Vector3.Multiply(color, nextRadiance);
            result = Vector3.Add(sphere.Emission, result);

            if (float.IsNaN(result.X) || float.IsNaN(result.Y) || float.IsNaN(result.Z))
            {
                throw new Exception();
            } 

            return result;
        }

这是原文:

if (obj.refl == DIFF){                  // Ideal DIFFUSE reflection
double r1=2*M_PI*erand48(Xi), r2=erand48(Xi), r2s=sqrt(r2);
Vec w=nl, u=((fabs(w.x)>.1?Vec(0,1):Vec(1))%w).norm(), v=w%u;
Vec d = (u*cos(r1)*r2s + v*sin(r1)*r2s + w*sqrt(1-r2)).norm();
return obj.e + f.mult(radiance(Ray(x,d),depth,Xi));}

【问题讨论】:

    标签: c# c++ raytracing


    【解决方案1】:

    我放弃了 Vector3 并使用了一个自定义类,该类适用于双精度数而不是浮点数。这解决了问题。感谢@AmberElferink 的帮助!

    class Vec
    {
        public double X { get; set; }
        public double Y { get; set; }
    
        public double Z { get; set; }
    
        public Vec(double x=0, double y=0, double z=0)
        {
            X = x;
            Y = y;
            Z = z;
        }
    
        public static Vec Normalize(Vec vec) { return vec.GetNormal(); }
    
        public static Vec Cross(Vec right, Vec left) { return right.CrossWith(left); }
    
        public static double Dot(Vec right, Vec left) { return right.DotWith(left); }
    
        public static Vec Multiply(Vec right, Vec left) { return right * left; }
    
        public static Vec Multiply(Vec right, double left) { return right * left; }
    
        public static Vec Add(Vec right, Vec left) { return right + left; }
    
        public static Vec Subtract(Vec right, Vec left) { return right - left; }
    
        public static Vec operator+(Vec right, Vec left) { return new Vec(right.X + left.X, right.Y + left.Y, right.Z + left.Z); }
        public static Vec operator-(Vec right, Vec left) { return new Vec(right.X - left.X, right.Y - left.Y, right.Z - left.Z); }
    
        public static Vec operator *(Vec right, Vec left) { return new Vec(right.X * left.X, right.Y * left.Y, right.Z * left.Z); }
    
        public static Vec operator *(Vec right, double left) { return new Vec(right.X * left, right.Y * left, right.Z * left); }
    
        public Vec GetNormal() { return this * (1 / Math.Sqrt(X * X + Y * Y + Z * Z)); }
    
        public double DotWith(Vec b) { return X * b.X + Y * b.Y + Z * b.Z; }
    
        public Vec CrossWith(Vec b) { return new Vec(Y * b.Z - Z * b.Y, Z * b.X - X * b.Z, X * b.Y - Y * b.X); }
    }
    

    【讨论】:

    • 很高兴听到你修复了它@Pavel。请考虑光线追踪器不使用双打的原因是双打的性能要差得多,特别是因为光线追踪器的速度通常受内存限制(您的缓存一直都是满的,这会导致从内存中检索内容的延迟很大) .使用双打可能会降低性能,但只要它明显变慢对你来说并不重要,你应该没问题:)
    【解决方案2】:

    你得到的效果让我想起了阴影粉刺。通常这是一个更圆形的模式,这就是我不确定的原因。 阴影粉刺的发生是由于浮动不准确。当你散射或反射时,你会在一个方向上从表面上的某个原点发出一条新射线。根据浮动误差,原点有时会在表面下方/上方移动。这就是为什么您经常在法线方向上用少量 EPSILON 偏移光线的原因。 所以你的新原点变成:intersection + intersection.normal * EPSILON。您应该为 epsilon 测试不同的值,但通常它在 0.01 到 0.02 左右或附近。 在您的代码中,您仍在使用交点,我认为它没有偏移量。 我不确定这是否有效,因为您的结果看起来与我习惯的阴影粉刺有点不同,但值得一试吧?

    【讨论】:

    • 这就是我计算交点的方式 'Vector3 intersectionPoint = ray.Origin + Vector3.Multiply(ray.Direction, (float)distanceToIntersection); intersectionPoint = intersectionPoint + Vector3.Multiply(Vector3.Normalize(intersectionPoint), 0.000000001f);'我只是添加了第二部分,结果是一样的
    • @PavelPavlov,0.00000001f 太小了。尝试大约 0.01 的值进行测试。甚至可能更大一些。浮点数不准确发生在小数点后 3 位左右。所以这太精确了。
    • 我尝试使用 0.1:link 0.01:link 0.001:link
    猜你喜欢
    • 1970-01-01
    • 2018-05-31
    • 2015-02-05
    • 2015-06-19
    • 2013-01-04
    • 1970-01-01
    • 2011-08-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多