We do not expect to be able to display the object exactly as it would appear in reality, with texture, overcast shadows, etc. We hope only to display an image that approximates the real object closely enough to provide a certain degree of realism.

Bui Tuong Phong

Bui Tuong was born in 1941, and became a computer scientist during the hobnobbing affair of the Vietnam War. It must have been pretty difficult for him to complete his education is the toxic environment of the 60s, let alone, be drafted to fight in the country of his fore bearers! But he managed to stay alive, until 1975, before leukemia took his life, just 2 short years before he gave us the basis of light and shading today: the *Phong shader*. Vietnamese names are comprised of three parts: the paternam name, the middle name, and the given name. All along, when people say “Phong shader”, they are referring to Bui Tuong’s given name. Read more about *given names* on Wikipedia.

“Softly let the balmy sunshine

Play around my dying bed,

E’er the dimly lighted valley

I with lonely feet must tread. “

Let The Light Enter – Poem by Frances Ellen Watkins Harper

There’s some extremely concise math behind Phong Shader. Which you need not know, really, unless you aspire to be a graphics programmer like yours truly. However, knowing them, even glancing at them will be beneficial in the long run. The following is extracted from *OpenGL Superbible, 7th Edition* by Graham Sellers and the Kronos Group ARB Foundation.

## A) Some Concepts

First, let me get some concepts out of the way. If you have been 3D modelling or game developing for more than a minute, you will certainly be positioned in their line of fire, but a reminder won’t hurt.

##### A-1) Ambient Light

Most books, especially rather shoddy ones, compare this sort of light with sunlight, however, this is simply wrong. Ambient light is not sunlight. It comes from *every direction*, meaning, it’s omnipresent, but in calculations, it’s just a vector with 3 members. In Phong shading, it gets added at the end, but is not modified.

##### A-2) Diffuse Light

Diffuse light has a direction. In fact, it’s the *directional component of a light source [sic]. *In cinema, light is diffused with a softbox, and in computer graphics, light is diffused with the formula which we’ll put forward later. The magnitude, that is, the size of the diffuse light depends on the surface. For example, if our surface is a matte surface that absorbs more light than reflecting, the magnitude must be higher than when our surface is glossier.

##### A-3) Specular Highlight

Like diffuse light, specular *light *is directional, but based on the glossiness of the surface, it leaves a *highlight* which is called *shininess*. In real life, shininess is not an inherent part of the material. In fact, a coat of film, or a speck of wax, will add more to its shininess than anything else ever could. Specular *shininess* is a factor which is set between 0 and 128, because beyond 128, it won’t affect the shader much.

##### A- 4) Albedo

It’s the proportion of the incident light which is *reflected away *by the surface.

##### A-5) Phong Formula

The formula for calculating a Phong material is as follows:

Where:

**: **Ambient material

**: **Diffuse material

**: **Specular material and **: **shininess factor

**: **Ambient light

**: **Diffuse light

**: **Specular light

You may ask, what about the vectors? Well, no worries, we’ll cover them now:

**: **Surface normal

**: **Unit vector from the point being shaded into the light (in other words, the light vector)

**: **The reflection of the negative of the light vector

**: **Vector to the viewer

## B) Gouraud Shading

Before stampeding towards Phong shader, let’s show how you can achieve Gouraud shading in GLSL. Note that I’m using version 4.1 of GLSL, as per Superbible’s instructions, however, you may be a fan of www.learnopengl.com and use 3.3. Doesn’t matter, same thing. So let’s see what a gouraud shading is. Is it edible, is it a suppository, or is it some sort of a sex toy.

This method of shading was invented by Henri Gouraud in 1971. It’s not superior to Phong shading by any means, and these days, it’s mostly used as a less GPU-intensive preview method in software such as Cinema 4D. The problem is, that the *glint* it generates looks like a spark, as shown in the picture below:

This is caused by interpolating color between the vertices, and discontinuity between triangles because the colors are being interpolated linearly, and is only solved by Phong shader. Let’s see how we can do Gouraud shading in GLSL 4.1.

**Listing 1**: Per-vertex Gouraud Shading in GLSL 4.1

#version 410 core // Per-vertex inputs layout (location = 0) in vec4 position; layout (location = 1) in vec3 normal; // Matrices we'll need layout (std140) uniform constants { mat4 mv_matrix; mat4 view_matrix; mat4 proj_matrix; }; // Light and material properties uniform vec3 light_pos = vec3(100.0, 100.0, 100.0); uniform vec3 diffuse_albedo = vec3(0.5, 0.2, 0.7); uniform vec3 specular_albedo = vec3(0.7); uniform float specular_power = 128.0; uniform vec3 ambient = vec3(0.1, 0.1, 0.1); // Outputs to the fragment shader out VS_OUT { vec3 color; } vs_out; void main(void) { // Calculate view-space coordinate vec4 P = mv_matrix * position; // Calculate normal in view space vec3 N = mat3(mv_matrix) * normal; // Calculate view-space light vector vec3 L = light_pos - P.xyz; // Calculate view vector (simply the negative of the view-space position) vec3 V = -P.xyz; // Normalize all three vectors N = normalize(N); L = normalize(L); V = normalize(V); // Calculate R by reflecting -L around the plane defined by N vec3 R = reflect(-L, N); // Calculate the diffuse and specular contributions vec3 diffuse = max(dot(N, L), 0.0) * diffuse_albedo; vec3 specular = pow(max(dot(R, V), 0.0), specular_power) * specular_albedo; // Send the color output to the fragment shader vs_out.color = ambient + diffuse + specular; // Calculate the clip-space position of each vertex gl_Position = proj_matrix * P; }

And now, the fragment shader.

**Listing 2: **Fragment shader of the same concept.

#version 410 core // Output layout (location = 0) out vec4 color; // Input from vertex shader in VS_OUT { vec3 color; } fs_in; void main(void) { // Write incoming color to the framebuffer color = vec4(fs_in.color, 1.0); }

## C) Phong Shading

Before going forward, keep in mind that Phong *shading *and Phong *lighting* are two different concepts. You can get rid of Gouraud’s “starburst” by using more vertices, but why do that when Phong shading is around? In Phong shaing, instead of interpolating the color between vertices (as seen in Listings 1 and 2), we interpolate the *surface normals *between the vertices, and the use the generated normal to perform the entire lighting calculation for each *pixel*, not each vertex. However, this means more work to be done in the fragment shader, as we’ll see in Listing 4. But first, let’s see the vertex shader.

**Listing 3:** Phong shader’s vertex shader in GLSL 4.1.

#version 410 core // Per-vertex inputs layout (location = 0) in vec4 position; layout (location = 1) in vec3 normal; // Matrices we'll need layout (std140) uniform constants { mat4 mv_matrix; mat4 view_matrix; mat4 proj_matrix; }; // Inputs from vertex shader out VS_OUT { vec3 N; vec3 L; vec3 V; } vs_out; // Position of light uniform vec3 light_pos = vec3(100.0, 100.0, 100.0); void main(void) { // Calculate view-space coordinate vec4 P = mv_matrix * position; // Calculate normal in view-space vs_out.N = mat3(mv_matrix) * normal; // Calculate light vector vs_out.L = light_pos - P.xyz; // Calculate view vector vs_out.V = -P.xyz; // Calculate the clip-space position of each vertex gl_Position = proj_matrix * P; }

Nothing much has changed. But the same isn’t true for the fragment shader.

**Listing 4: **Phong shading’s fragment shader.

#version 410 core // Output layout (location = 0) out vec4 color; // Input from vertex shader in VS_OUT { vec3 N; vec3 L; vec3 V; } fs_in; // Material properties uniform vec3 diffuse_albedo = vec3(0.5, 0.2, 0.7); uniform vec3 specular_albedo = vec3(0.7); uniform float specular_power = 128.0; uniform vec3 ambient = vec3(0.1, 0.1, 0.1); void main(void) { // Normalize the incoming N, L and V vectors vec3 N = normalize(fs_in.N); vec3 L = normalize(fs_in.L); vec3 V = normalize(fs_in.V); // Calculate R locally vec3 R = reflect(-L, N); // Compute the diffuse and specular components for each fragment vec3 diffuse = max(dot(N, L), 0.0) * diffuse_albedo; vec3 specular = pow(max(dot(R, V), 0.0), specular_power) * specular_albedo; // Write final color to the framebuffer color = vec4(ambient + diffuse + specular, 1.0); }

Well, that is it for this lesson. I hope you enjoyed it. If this has sparked your interest in OpenGL, you buy *OpenGL Superbible *from Amazon, or head to learnopengl.com. If you can’t make heads or tails of the shaders, I recommend Book of Shaders. Please leave a comment. Thank you.