M. Oren and S.K. Nayar proposed a diffuse BRDF in Generalization of Lambert's Reflectance Model, Michael Oren, Shree K. Nayar, July 1994, Siggraph, pp. 239-246 that takes the roughness of a surface into account. This BRDF is often cited as too slow for realtime use, because of the sin and tan instructions in the equation. In this post I present a rewritten version of the equation which I'd like to call "van Ouwerkerk's rewrite". The rewritten equation evaluates significantly faster on modern GPU's, making it more feasible to use the Oren Nayar BRDF in realtime applications.
|Figure 1 - backscattering towards light source in V-cavities|
The Oren Nayar BRDF is based on mathematical analysis of light interacting with a surface consisting of V-cavities. In their work they predict that roughness results in more light being reflected back in the direction of the light source, which is illustrated in figure 1. This backscatter effect is contained in the resulting simplified Oren Nayar BRDF listed in equation 1.
|Equation 1 - (simplified) Oren Nayar BRDF|
|Figure 2 - graphical overview of angles|
|Equation 2 - angles constrained to meaningful range|
Our goal is to remove the sin and tan instructions from the Oren Nayar BRDF. Since a cos can often be replaced by a much faster dot product, we start by transforming the sin and tan instructions into cos instructions using the two trigonometric identities listed in equation 3. Note that the square root would mathematically have both a positive and a negative result. Since the zenith angles are constrained to the 0 to π/2 range, we can assume the positive result.
|Equation 3 - trigonometric identities|
This will transform the BRDF into the expanded form listed in equation 4. For easier reading, the part with the original sin and tan instructions is split into a separate part C of the equation.
|Equation 4 - application of trigonometric identities|
|Figure 3 - cosine from 0 to π/2|
|Equation 5 - moving min and max outwards|
Since both the minimum and the maximum of the incoming and reflected zenith angles are processed in the same way, we can apply another simplification as listed in equation 6. If one of the angles is the minimum, the other one is automatically the maximum. This means we can replace the mirrored min and max instructions with the two parameters contained in them. We can also combine the two square roots into one.
|Equation 6 - mirrored min and max|
At this point we can replace all cosine functions with dot products and transform the equation into the vector math commonly found in realtime shaders. The final result is listed in equation 7.
|Equation 7 - van Ouwerkerk's rewrite|
The proposed equation is implemented in an HLSL pixel shader as shown in the listing below. The determination of the surface albedo and the amount of incoming light is not included in this listing as it depends on many other factors.
// Input vectorsFinal words
float3 normal = normalize(input.normal);
float3 light = normalize(input.light);
float3 view = normalize(input.view);
// Roughness, A and B
float roughness = input.roughness;
float roughness2 = roughness * roughness;
float2 oren_nayar_fraction = roughness2 / (roughness2 + float2(0.33, 0.09));
float2 oren_nayar = float2(1, 0) + float2(-0.5, 0.45) * oren_nayar_fraction;
// Theta and phi
float2 cos_theta = saturate(float2(dot(normal, light), dot(normal, view)));
float2 cos_theta2 = cos_theta * cos_theta;
float sin_theta = sqrt((1-cos_theta2.x) * (1-cos_theta2.y));
float3 light_plane = normalize(light - cos_theta.x * normal);
float3 view_plane = normalize(view - cos_theta.y * normal);
float cos_phi = saturate(dot(light_plane, view_plane));
float diffuse_oren_nayar = cos_phi * sin_theta / max(cos_theta.x, cos_theta.y);
float diffuse = cos_theta.x * (oren_nayar.x + oren_nayar.y * diffuse_oren_nayar);
I've hereby presented a faster way to evaluate the Oren Nayar BRDF specifically targeted at usage in realtime shaders. This document has been submitted as proposal to the GPU Pro 3 book.