# BRDF ray directions

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 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?

• The function OrenNayer() takes a light vector a normal vector, a roughness value and a view vector as a parameter. – PaulHK Jul 9 '18 at 6:21
• Look at how the variable 'rd' is setup, that's the view vector. – PaulHK Jul 9 '18 at 6:24
• but that vector points towards the scene, not the lens? It's the same direction used for the ray/sphere intersection tests. – Paul Ferris Jul 9 '18 at 7:07
• That should be the view vector. – PaulHK Jul 9 '18 at 7:24
• yes? So this (i0.wp.com/upload.wikimedia.org/wikipedia/commons/thumb/e/ed/…) isn't an accurate diagram? – Paul Ferris Jul 9 '18 at 7:28

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 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.

image source

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.

• 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 Jul 12 '18 at 5:35
• @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 Jul 18 '18 at 3:38
• Mmmm, ok. I missed the line and didn't even think to test with negative rd. Thanks for the update :) – Paul Ferris Jul 20 '18 at 0:23