<p><img src="https://i.sstatic.net/D2uft.png" width="300" height="250">
<img src="https://i.sstatic.net/AtyZf.jpg" width="300" height="250"><br/>
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<br/></p>

<p>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 <code>Vec y</code> 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.<br/><br/>
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.</p>

<pre><code>Vec receivedRadiance(const Ray &amp;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 &amp;obj = spheres[id];            // the hit object
const BRDF &amp;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) &lt; 0) n = n*-1.0;

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

if (rng() &lt; 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);
}
return rad;}
</code></pre>

<p>Here is my specular BRDF struct:</p>

<pre><code>struct SpecularBRDF : public BRDF {
SpecularBRDF(Vec ks_) : ks(ks_) {}

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

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

Vec ks;}
</code></pre>

<p>And my mirrorDir function:</p>

<pre><code>inline void mirroredDir(const Vec &amp;n, const Vec &amp;o, Vec &amp;m){
m = n * 2.0 * (n.dot(o)) - o;}
</code></pre>

CC BY-SA 3.0
2017-05-10T09:05:34.723Z
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
CC BY-SA 3.0
2017-05-10T09:30:07.807Z
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
CC BY-SA 3.0
2017-05-11T02:43:50.617Z
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
CC BY-SA 3.0
2017-05-11T03:30:55.847Z
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
CC BY-SA 3.0
2017-05-11T21:25:38.607Z
I'm not sure if this gives a clue, but the reflective sphere also seems to be lacking any shadow on the plane beneath it.
trichoplax is on Codidact now
CC BY-SA 3.0
2017-05-12T02:28:51.547Z
From the shadowed wall area's point of view, it will see the glowing ringed ball so it acts as a light source instead of an occluder. Energy is also not being conserved due to this problem so the scene ends up with too much light.
PaulHK
CC BY-SA 3.0
2017-05-12T02:34:52.140Z
One thing I would do is make a debug mode in your path tracer. In this mode you stop recursing at the first intersection and instead output the outgoing sampling direction as a colour (colour = rayDir*0.5+0.5). From there you can visualise if the out bound ray direction is being computed properly, the walls can act as a reference.
PaulHK
CC BY-SA 3.0
2017-05-12T06:29:34.743Z
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
<raytracing><c++><pathtracing><specular><monte-carlo>
Problem with specular BRDF (Monte Carlo & Path Tracing)
Aloof
# Problem with specular BRDF (Monte Carlo & Path Tracing)

(Asked by: username_0 on 2017-05-10)

![](https://i.sstatic.net/D2uft.png)
![](https://i.sstatic.net/AtyZf.jpg)  
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);
}
return rad;}

```

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

```

**Tags:** raytracing, c++, pathtracing, specular, monte-carlo

> **Comments:**
> **username_1**: 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 ?  
> **username_0**: 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.  
> **username_1**: 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  
> **username_0**: 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.  
> **username_2**: I'm not sure if this gives a clue, but the reflective sphere also seems to be lacking any shadow on the plane beneath it.  
> **username_1**: From the shadowed wall area's point of view, it will see the glowing ringed ball so it acts as a light source instead of an occluder. Energy is also not being conserved due to this problem so the scene ends up with too much light.  
> **username_1**: One thing I would do is make a debug mode in your path tracer. In this mode you stop recursing at the first intersection and instead output the outgoing sampling direction as a colour (colour = rayDir*0.5+0.5). From there you can visualise if the out bound ray direction is being computed properly, the walls can act as a reference.  
> **username_0**: 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.  

---

## 0 answers

