You can improve this part of the code this way:float geo_a = (2.0 * NdotH * NdotV) / VdotH;

float geo_b = (2.0 * NdotH * NdotL) / VdotH;

float G = min(1.0, max(0, min(geo_a, geo_b)));

float g_min = min(NdotV, NdotL); float G = saturate(2 * NdotH * g_min / VdotH);Also, don't ever use max(0, dot(a, b)). Instead use saturate(dot(a, b)) which compiles into a single instruction.

It is, but is using the complement of Fspecular actually a good one? I don't think so (unless you're using Fdiffuse).Isn't real-time graphics programming all about approximations?

I think someone should approximate a diffuse BRDF using the equation I posted in my post above. That would be a way better approximation.

To get back to the original topic:

The last one is the correct Cook-Torrance microfacet model. Sometimes you find (ns + 2) / (2 * pi) or (ns + 2) / (8 * pi) as the normalization factor for Blinn-Phong. The second one is already pre-multiplied with the 1/4 while the first one is the distribution function for the microfacet model.

And this one is the correct Beckmann NDF:

I wouldn't recommend the Beckmann NDF though. It's pretty damn slow in comparison to other NDFs because of 2 reciprocals and the exponential function. (Y u no use GGX xD)

This is the BRDF I'm using:

I'm using GGX as the distribution function, Schlick's approximation of fresnel as fresnel term and Walter's geometric term for the GGX distribution function.

I color-coded everything for implementation details. The grey parts are just parts of the BRDF and don't need to be implemented. The green parts can be calculated once for every pixel. And the red parts are the only parts, that actually need to be calculated for every light.