Reputation: 23
I'm trying to port the smallpt: Global Illumination in 99 lines of C++ to C# and I'm getting this weird bug when the light reflects of a diffuse surface. Does anyone have an idea where the problem might be coming from?
This what I'm getting with 40 samples
This is what it's supposed to look like
This my code for diffuse surfaces:
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;
}
And this is the original:
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));}
Upvotes: 1
Views: 118
Reputation: 23
I gave up on Vector3 and used a custom class that works with doubles and not floats. And that fixed the problem. Thanks @AmberElferink for the help!
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); }
}
Upvotes: 1
Reputation: 152
The effect you are getting reminds me a bit of shadow acne. Usually this is a more circular pattern, which is why I'm not sure. Shadow acne happens due to float inaccuracies. When you scatter or reflect, you make a new ray from a certain origin on a surface in a direction. The origin will sometimes shift under/above the surface depending on the float inaccuracy. That's why you often offset the ray with a small number EPSILON in the direction of the normal. So your new origin becomes: intersection + intersection.normal * EPSILON. You should test different values for epsilon, but usually it's around 0.01 to 0.02 or something around that. In your code you are still using intersectionpoint, which I assume does not have the offset. I'm not sure if that will work, since your result looks a bit different from the shadow acne I'm used to, but it's worth a try right?
Upvotes: 0