# Problem with specular BRDF (Monte Carlo & Path Tracing)  Above on the left is my attempt and on the right is what I'm aiming for.As you can see my sphere with the specular brdf has a weird dark ring

In my radiance function I am recursing through the intersected objects with the next intersection from the ray that's being traced. I'm using russian roulette with a depth of 5 to avoid infinite recursion. After this I find a point of intersection Vec y and calculate the normal there. After this I sample the brdf, which in this case is specular, to get the incoming direction vector, stored in i. Finally I call the function using a new ray with the point of intersection and -i.

I have a feeling the my error is with my normal vector n being sent to brdf.eval or brdf.sample. I am not sure if the normal is supposed to be the normal at the origin of the ray, r.o, or at the intersection point y. When I try changing it the specular sphere ends up completely black, or my diffuse brdf for the rest of the scene becomes incorrect.

Vec receivedRadiance(const Ray &r, int depth, bool flag) {
double t, p;                                   // Distance to intersection/pdf
int id = 0;                                 // id of intersected sphere

if (!intersect(r, t, id)) return Vec();   // if miss, return black
const Sphere &obj = spheres[id];            // the hit object
const BRDF &brdf = obj.brdf;                // Surface BRDF at x
Vec rad = obj.e;                            // Emitted radiance

Vec o = (Vec() - r.d).normalize();          // The outgoing direction (= -r.d)
Vec x = r.o + r.d*t;                        // The intersection point

Vec n = (x - obj.p).normalize();            // The normal direction
if (n.dot(r.d) < 0) n = n*-1.0;

if (depth <= 5)
p = 1.0;
else
p = .9;

if (rng() < p)
{
Vec i, b;
brdf.sample(n, o, i, p);
b = brdf.eval(n, o, i);
Ray y = Ray(x, i * -1);
rad = rad + (receivedRadiance(y, depth + 1, true) * (PI)).mult(b) * (1 / p);
}


Here is my specular BRDF struct:

struct SpecularBRDF : public BRDF {
SpecularBRDF(Vec ks_) : ks(ks_) {}

Vec eval(const Vec &n, const Vec &o, const Vec &i) const {
Vec m;
mirroredDir(n, o, m);
if (abs(i.x - m.x) < 1e-5 && abs(i.y - m.y) < 1e-5 && abs(i.z - m.z) < 1e-5) //i == m
return ks * (1 / n.dot(i));
else
return Vec(0.0, 0.0, 0.0);
}

void sample(const Vec &n, const Vec &o, Vec &i, double &pdf) const {
mirroredDir(n, o, i);
pdf = 1.0;
}

Vec ks;}


And my mirrorDir function:

inline void mirroredDir(const Vec &n, const Vec &o, Vec &m){
m = n * 2.0 * (n.dot(o)) - o;}

• Usually the normal in a BRDF calculation is the normal of the surface at the intersection point. the 'if (n.dot(r.d) < 0) n = n*-1.0;' bit looks strange, what is that for ? – PaulHK May 10 '17 at 9:05
• Thanks for the input, and that part was given to me by my professor, but as far as I know that ensures that the normal points out from the surface, or is properly oriented. – Aloof May 10 '17 at 9:30
• how about 1/n.dot(i) in your eval() function -- if n.dot(i) is in the 0..1 range your output is going to be 1 .. infinity – PaulHK May 11 '17 at 2:43
• hmm I tried removing the dot product and just sending back the ks color and nothing changed. Also I've updated my code a bit, I had a mistake in the first brdf function calls in which I was sending the r.d vector instead of -r.d (or o) vector. This has given me a new stranger error now though where the specular sphere has a black ring now. – Aloof May 11 '17 at 3:30
• I was finally able to solve this problem, I had a problem with the "rad = rad + (receivedRadiance(y, depth + 1, true) * (PI)).mult(b) * (1 / p);" statement. I changed it to this : "rad = rad + receivedRadiance(y, depth + 1, flag).mult(brdf.eval(n, o, i))*(n.dot(i)/(pdf*p));" I found this solution by just looking up similar projects and formulas online. In the diffuse BRDF I added "pdf = i.dot(n) * (1 / PI)" to account for this change, and cancel out the dot product. edit: oh I also needed my normal to be checked again o, not r.d. – Aloof May 12 '17 at 6:29