<p><img src="https://i0.wp.com/upload.wikimedia.org/wikipedia/commons/thumb/e/ed/BRDF_Diagram.svg/800px-BRDF_Diagram.svg.png" alt=""></p>

<p>This diagram is accurate. Let's look at the code:</p>

<pre><code>float OrenNayar( in vec3 l, in vec3 n, in vec3 v, float r )
{

    float r2 = r*r;
    float a = 1.0 - 0.5*(r2/(r2+0.57));
    float b = 0.45*(r2/(r2+0.09));

    float nl = dot(n, l);
    float nv = dot(n, v);

    float ga = dot(v-n*nv,n-n*nl);

    return max(0.0,nl) * (a + b*max(0.0,ga) * sqrt((1.0-nv*nv)*(1.0-nl*nl)) / max(nl, nv));
}
</code></pre>

<p>The first three parameters passed in (<code>l</code>, <code>n</code>, <code>v</code>) correspond to <code>W_i</code>, <code>n</code>, and <code>W_o</code> vectors, respectively. </p>

<p>As you can see, both <code>l</code> and <code>v</code> are used in a <a href="https://en.wikipedia.org/wiki/Dot_product" rel="nofollow noreferrer"><code>dot</code> product</a> with <code>n</code>: This is done for a purpose.</p>

<p>The dot product gives you the cosine of the angle between the two parameters if both of the parameters are normalized vectors. Otherwise, it gives you the cosine of the angle between the vectors, scaled by the product of the length of those vectors </p>

<pre><code>dot(l, n) = len(l) * len(n) * cos(theta) 
</code></pre>

<p>where <code>theta</code> is the angle between <code>l</code> and <code>n</code>. </p>

<p><img src="https://i.sstatic.net/Zr9wa.png" alt=""></p>

<p><sub><a href="https://www.mathsisfun.com/algebra/vectors-dot-product.html" rel="nofollow noreferrer">image source</a></sub></p>

<p>As we normalize our vectors, we can forget about the length and directly associate the dot product with the <code>cos(theta)</code> value.</p>

<p>The value of the dot product ranges between [-1, +1] since cosine values, by definition, range between [-1, +1]. </p>

<ul>
<li>If dot product is 1, it means that both the vectors are pointing in the same direction</li>
<li>If dot product is 0 (or very close to zero), it means that the vectors are exactly (or almost if its close to 0) perpendicular to each other.</li>
<li>If dot product is -1, it means that the two vectors point in the opposite direction</li>
</ul>

<p><strong>In other words, dot product kind of measures how similar the vectors are in terms of the direction they point to.</strong></p>

<p>Since we compare how similar <code>v</code> and <code>l</code> is to the <code>n</code> vector, we kind of look into where <code>v</code> and <code>l</code> points to relative to the surface normal. Since the surface normal points away from the surface by definition, the vectors that have a dot product with <code>n</code> larger than 0 also point <em>away</em> from the surface.</p>

<p>Now let's look at the last part of the code:</p>

<pre><code>    return max(0.0,nl) * (a + b*max(0.0,ga) * sqrt((1.0-nv*nv)*(1.0-nl*nl)) / max(nl, nv));
</code></pre>

<p>Notice this: <strong><code>max(0.0,nl)</code></strong> ?</p>

<p>This expression would evaluate to 0 if the <code>NdotL</code> is less than 0. In other words, if the light direction is pointing <em>into</em> the surface we don't take into account the contribution of that light.</p>

<p>So, the code provided doesn't ignore the rule: In order for the BRDF to return a positive light contribution value, the light vector should point away from the surface.</p>

<hr>

<p>As for the view ray part of the question, I think you've spotted a bug. The view vector <code>v</code> in the lighting calculation should point into the camera. The <code>rd</code> parameter passed in to <code>OrenNayar()</code> is a viewRay in world space, pointing away from the camera. </p>

<p>The correct interpretation of the parameter in this case is <code>-v</code> when using the <code>dot()</code> product. </p>

<p><img src="https://i.sstatic.net/laDkM.png" alt=""></p>

CC BY-SA 4.0
2018-07-12T05:35:13.553Z
Mhmm, the rule holds for the light vector, and I knew about the dot-product already. I should have said iq's view-vector points into the scene and opposes the camera ray in the diagram, that's what's confusing me.
Paul Ferris
CC BY-SA 4.0
2018-07-18T03:38:58.433Z
@PaulFerris Well yeah, I agree with you. I think the shader is buggy and the correction would be `    float nv = dot(n, -v);` as the parameter passed in is a ViewRay, pointing  away from the camera. In this sense, the nv should be between `n` and `-v`. Doing that also fixes the ugly horizontal line on one of the spheres.
Varaquilex
CC BY-SA 4.0
2018-07-20T00:23:46.637Z
Mmmm, ok. I missed the line and didn't even think to test with negative rd. Thanks for the update :)
Paul Ferris
CC BY-SA 4.0
2018-07-12T04:19:06.543Z
2018-07-18T03:46:18.277Z
2018-07-18T03:46:18.277Z
Varaquilex
<p>The BRDF takes two directions, $\omega_{o}$ and $\omega_{i}$. I've heard that both directions should point away from the surface (one towards the camera, one towards the light), but <a href="https://www.shadertoy.com/view/ldBGz3" rel="nofollow noreferrer">iq's Oren-Nayar example shader</a> has rays pointing away from the camera and works flawlessly. Are BRDFs <em>strictly</em> defined for rays pointing away from the surface, or am I misreading things and iq's approach is preferred?</p>

CC BY-SA 4.0
2018-07-09T06:21:42.787Z
The function OrenNayer() takes a light vector a normal vector, a roughness value and a view vector as a parameter.
PaulHK
CC BY-SA 4.0
2018-07-09T06:24:01.720Z
Look at how the variable 'rd' is setup, that's the view vector.
PaulHK
CC BY-SA 4.0
2018-07-09T07:07:55.600Z
but that vector points towards the scene, not the lens? It's the same direction used for the ray/sphere intersection tests.
Paul Ferris
CC BY-SA 4.0
2018-07-09T07:24:31.887Z
That should be the view vector.
PaulHK
CC BY-SA 4.0
2018-07-09T07:28:50.263Z
yes? So this (https://i0.wp.com/upload.wikimedia.org/wikipedia/commons/thumb/e/ed/BRDF_Diagram.svg/800px-BRDF_Diagram.svg.png)
isn't an accurate diagram?
Paul Ferris
CC BY-SA 4.0
2018-07-12T03:47:28.037Z
@PaulFerris The diagram is accurate. You say the shader 'totally ignores the rule for `wo` and `wi`'. Where do you expect them to see in the shader code? How would the code need to change if it took into account the rule for `wo` and `wi` ?
Varaquilex
CC BY-SA 4.0
2018-07-12T05:23:49.907Z
Sorry, I miswrote before (and definitely didn't mean to come across that strongly). iq's BRDF uses his camera direction without flipping, so it still points into the scene. That goes against the diagram, which has $\omega_o$ pointing towards the camera. Yet it works fine, and that's where I'm unsure.
Paul Ferris
<pathtracing><mathematics>
BRDF ray directions
Paul Ferris
# BRDF ray directions

(Asked by: username_0 on 2018-07-08)

The BRDF takes two directions, $\omega\_{o}$ and $\omega\_{i}$. I've heard that both directions should point away from the surface (one towards the camera, one towards the light), but [iq's Oren-Nayar example shader](https://www.shadertoy.com/view/ldBGz3) has rays pointing away from the camera and works flawlessly. Are BRDFs *strictly* defined for rays pointing away from the surface, or am I misreading things and iq's approach is preferred?

**Tags:** pathtracing, mathematics

> **Comments:**
> **username_1**: The function OrenNayer() takes a light vector a normal vector, a roughness value and a view vector as a parameter.  
> **username_1**: Look at how the variable 'rd' is setup, that's the view vector.  
> **username_0**: but that vector points towards the scene, not the lens? It's the same direction used for the ray/sphere intersection tests.  
> **username_1**: That should be the view vector.  
> **username_0**: yes? So this (https://i0.wp.com/upload.wikimedia.org/wikipedia/commons/thumb/e/ed/BRDF_Diagram.svg/800px-BRDF_Diagram.svg.png) isn't an accurate diagram?  
> **username_2**: @username_0 The diagram is accurate. You say the shader 'totally ignores the rule for `wo` and `wi`'. Where do you expect them to see in the shader code? How would the code need to change if it took into account the rule for `wo` and `wi` ?  
> **username_0**: Sorry, I miswrote before (and definitely didn't mean to come across that strongly). iq's BRDF uses his camera direction without flipping, so it still points into the scene. That goes against the diagram, which has $\omega_o$ pointing towards the camera. Yet it works fine, and that's where I'm unsure.  

---

## 1 answers

### Answer by username_2 on 2018-07-12. Score: 3 | Accepted answer

![](https://i0.wp.com/upload.wikimedia.org/wikipedia/commons/thumb/e/ed/BRDF_Diagram.svg/800px-BRDF_Diagram.svg.png)

This diagram is accurate. Let's look at the code:

```
float OrenNayar( in vec3 l, in vec3 n, in vec3 v, float r )
{

    float r2 = r*r;
    float a = 1.0 - 0.5*(r2/(r2+0.57));
    float b = 0.45*(r2/(r2+0.09));

    float nl = dot(n, l);
    float nv = dot(n, v);

    float ga = dot(v-n*nv,n-n*nl);

    return max(0.0,nl) * (a + b*max(0.0,ga) * sqrt((1.0-nv*nv)*(1.0-nl*nl)) / max(nl, nv));
}

```

The first three parameters passed in (`l`, `n`, `v`) correspond to `W_i`, `n`, and `W_o` vectors, respectively.

As you can see, both `l` and `v` are used in a [`dot` product](https://en.wikipedia.org/wiki/Dot_product) with `n`: This is done for a purpose.

The dot product gives you the cosine of the angle between the two parameters if both of the parameters are normalized vectors. Otherwise, it gives you the cosine of the angle between the vectors, scaled by the product of the length of those vectors

```
dot(l, n) = len(l) * len(n) * cos(theta) 

```

where `theta` is the angle between `l` and `n`.

![](https://i.sstatic.net/Zr9wa.png)

[image source](https://www.mathsisfun.com/algebra/vectors-dot-product.html)

As we normalize our vectors, we can forget about the length and directly associate the dot product with the `cos(theta)` value.

The value of the dot product ranges between [-1, +1] since cosine values, by definition, range between [-1, +1].

* If dot product is 1, it means that both the vectors are pointing in the same direction
* If dot product is 0 (or very close to zero), it means that the vectors are exactly (or almost if its close to 0) perpendicular to each other.
* If dot product is -1, it means that the two vectors point in the opposite direction

**In other words, dot product kind of measures how similar the vectors are in terms of the direction they point to.**

Since we compare how similar `v` and `l` is to the `n` vector, we kind of look into where `v` and `l` points to relative to the surface normal. Since the surface normal points away from the surface by definition, the vectors that have a dot product with `n` larger than 0 also point *away* from the surface.

Now let's look at the last part of the code:

```
    return max(0.0,nl) * (a + b*max(0.0,ga) * sqrt((1.0-nv*nv)*(1.0-nl*nl)) / max(nl, nv));

```

Notice this: **`max(0.0,nl)`** ?

This expression would evaluate to 0 if the `NdotL` is less than 0. In other words, if the light direction is pointing *into* the surface we don't take into account the contribution of that light.

So, the code provided doesn't ignore the rule: In order for the BRDF to return a positive light contribution value, the light vector should point away from the surface.

---

As for the view ray part of the question, I think you've spotted a bug. The view vector `v` in the lighting calculation should point into the camera. The `rd` parameter passed in to `OrenNayar()` is a viewRay in world space, pointing away from the camera.

The correct interpretation of the parameter in this case is `-v` when using the `dot()` product.

![](https://i.sstatic.net/laDkM.png)

> **Comments:**
> **username_0**: Mhmm, the rule holds for the light vector, and I knew about the dot-product already. I should have said iq's view-vector points into the scene and opposes the camera ray in the diagram, that's what's confusing me.  
> **username_2**: @username_0 Well yeah, I agree with you. I think the shader is buggy and the correction would be `    float nv = dot(n, -v);` as the parameter passed in is a ViewRay, pointing  away from the camera. In this sense, the nv should be between `n` and `-v`. Doing that also fixes the ugly horizontal line on one of the spheres.  
> **username_0**: Mmmm, ok. I missed the line and didn't even think to test with negative rd. Thanks for the update :)  

---

