Raytracer светоотражающая ошибка - PullRequest
1 голос
/ 23 февраля 2020

Я пытаюсь портировать smallpt: Global Illumination в 99 строк C ++ на C#, и я получаю эту странную ошибку, когда свет отражается от рассеянной поверхности. У кого-нибудь есть идея, откуда может возникнуть проблема?

Это то, что я получаю с 40 сэмплами

Это то, что должно выглядеть как

Это мой код для диффузных поверхностей:

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));}

Ответы [ 2 ]

1 голос
/ 24 февраля 2020

Я отказался от 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); }
}
0 голосов
/ 23 февраля 2020

Эффект, который вы получаете, напоминает мне немного о прыщах в тени. Обычно это более круглая схема, поэтому я не уверен. Теневые прыщи случаются из-за неточностей поплавка. Когда вы рассеиваете или отражаете, вы делаете новый луч из определенного источника на поверхности в направлении. Начало координат иногда смещается под / над поверхностью в зависимости от погрешности поплавка. Вот почему вы часто смещаете луч небольшим числом EPSILON в направлении нормали. Итак, ваше новое происхождение становится: пересечение + пересечение. Нормальное * EPSILON. Вы должны проверить различные значения для эпсилон, но обычно это около 0,01 до 0,02 или что-то около этого. В вашем коде вы все еще используете точку пересечения, которая, как я полагаю, не имеет смещения. Я не уверен, сработает ли это, так как ваш результат выглядит несколько иначе, чем у теневых угрей, к которым я привык, но стоит попробовать, верно?

...