So turns out Jim Blinn tweaked Phong’s shading so it would be more efficient, which gave us the Blinn-Phong model we all know and love today.

Instead of calculating viewDir · reflectionDir per frame, as we did before, Jim uses a half-vector viewDir + lightDir and compares that with the normals of the fragment.

Since the normals are calculated in the vertex pass, they are calculated once for N fragments. It saves us the reflect(L, N) call per fragment, which is nice.

void main() 
{
    vec3 N = normalize(fragNormal);
    vec3 L = normalize(lightDir);

    vec3 viewDir = normalize(viewPos - fragPosition);
    
    // Predefined light directions, like sunlight, need to be inverted
    vec3 halfVector = normalize(-L + viewDir);
    float onlySpecular = max(0, dot(halfVector, N));

    vec3 specular = specularTint * pow(onlySpecular, smoothness * 100);

    finalColor = vec4(specular, fragColor.a);
}

Plus, it also makes elliptical speculars possible when viewed at a steep angle, which is more realistic! A classic win/win situation.

Comparison of Phong speculars and Blinn-Phong speculars when viewed at a steep angle.

To make those just a tad more realistic, we are going to darken the albedo in relation to the specular tint, to simulate glossiness:

Comparison of two very specular Blinn-Phong spheres. One of them shows the glossy effect.

And here is our park again, a comparison between the two:

Park diorama shaded with Lambert-Phong.
Lambert-Phong with Specular at 1.0
Park diorama with several different specular surfaces.
Blinn-Phong with different Specular values

The most noticeable differences are the fountain and the cobblestones.

If you made it this far...

Thank you! I hope you liked it!

I do not allow comments in my blog because I do not want to deal with bots. However, feel free to contact me!

And if you would like to support my work, please, consider doing so through ko-fi:

Support me!