A bit about Phong shading

Original author: Chubak Bidpaa
  • Transfer

“We cannot expect that we will be able to display the object exactly as it is in reality, with textures, shadows, etc. We hope only to create an image that is close enough to achieve a certain degree of realism. "

Bui Tyong Fong

Bui Thuong Fong was born in 1941 and became a computer scientist during the Vietnam War. It must have been difficult for him to complete his studies in the toxic environment of the 60s, not to mention the fact that he was called to the front! But he managed to survive and survive until 1975, before leukemia took his life only two years after he laid the foundation for the world of the modern theory of lighting and shading: Fong's shader . Vietnamese names consist of three parts: surname, middle name and personal name. That is, when people say "Fong shader", they call the personal name Bui Tyong. You can read more about personal names on Wikipedia .


Not sure if this is Phong, but if you believe Google, then yes.

“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 - a poem by Francis Harper

The shader of Phong is based on extremely concise mathematics. Which, in fact, you do not need to know, unless you sincerely wish to become a graphics programmer. However, in the long run, her knowledge will be useful. Below are excerpts from the OpenGL Superbible, 7th Edition by Graham Sellers and the Kronos Group ARB Foundation.

A) Some concepts


First off, let's figure out the concepts. If you have been dealing with 3D modeling or game development for even a minute, then you must have already encountered them, but repetition has not bothered anyone.

A-1) Ambient Light

Most books, especially low-quality ones, compare this lighting to sunlight, but this is completely untrue. Ambient lighting is not sunlight. It comes from all directions , that is, it is omnipresent, but in calculations it is just a vector with three components. In the shading of Phong, it is added at the end, but does not change.

A-2) Diffuse / Diffuse Light

Diffuse lighting has a direction. This is actually a directional component of the [sic] light source . In cinema, light is scattered using a soft box, and in computer graphics, light is scattered using a formula, which we will show below. The magnitude, i.e. the size of the diffused illumination, depends on the surface. For example, if the surface is matte, that is, it absorbs more than reflects light, then the value will be greater than in the case of a smooth surface.


Scattered / absorbed ambient light from a frosted screen

A-3) Specular Highlight

Like diffused lighting, reflected light is directional, but based on the glossiness of the surface; it leaves a highlight called shininess . In real life, gloss is not an integral part of the material. In fact, a film coating or a drop of wax will add much more to the shine than anything else. Reflected gloss is a factor that ranges from 0 to 128, because at values ​​above 128 it will not greatly affect the shader.


Film with film - thick colored paper with a glossy film coating, a real gift for a child.

A- 4) Albedo

This is the fraction of incident light reflected by the surface.

A-5) Phong Formula

The formula for calculating the material according to Phong is as follows:

$ I_p = k_ai_a + k_d \ left (\ vec {L}. \ Vec {N} \ right) i_d + k_s \ left (\ vec {R}. \ Vec {V} \ right) ^ \ alpha i_s $


Where:

$ k_a $: Ambient material

$ k_d $: Diffuse material

$ k_s $: Specular material and $ \ alpha $: gloss value

$ i_a $: ambient lighting

$ i_d $: diffuse lighting

$ i_s $: Reflected lighting

You may ask, what about vectors? Do not worry, now we will tell about them:

$ \ vec {N} $: normal to the surface

$ \ vec {L} $: the unit vector from the shaded point to the light source (in other words, the light vector)

$ \ vec {R} $: reflection of the negative value of the light vector

$ \ overrightarrow {V} $: vector directed towards the viewer


B) Gouraud Shading


Before embarking on the Phong shader, let's see how you can get Gouro shading on GLSL. Please note that I use version GLSL 4.1, as in Superbible, but if you are a fan of www.learnopengl.com , then you can use 3.3. It does not matter. So, let's see what Gouraud shading is.

This shading method was invented by Henri Gouraud in 1971. It does not surpass Phong shading in any way, and today it is mainly used as a GPU-less loading preview method in packages like Cinema 4D. His problem is that the flare he generates looks like a spark:


This problem is caused by the interpolation of colors between vertices, and gaps between triangles occur because colors are interpolated linearly. This problem was only solved in the Phong shader. Let's see how you can implement Gouro shading on GLSL 4.1.

Listing 1 : Gourox vertex shading on 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: The 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 moving on, let's remember that Phong shading and Phong lighting are two different concepts. You can get rid of the “snowflake” of Guro flare by adding more vertices, but why if we have shading in Fong? In the Phong shading, the color is not interpolated between the vertices (as in Listing 1 and 2), we interpolate between the vertices of the normal to the surface , and use the generated normal to perform all the lighting calculations for each pixel , not the vertex. However, this means that the fragment shader will have to do more work, as shown in Listing 4. But first, let's look at the vertex shader.


Listing 3: the vertex shader of the Phong shader on 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;
}

Almost nothing has changed. But in the fragment shader, the situation is completely different.

Listing 4: The Phong fragment shader 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, this is the end of the article, I hope you enjoyed it. If it has sparked a spark of interest in OpenGL in you, you can buy OpenGL Superbible on Amazon or explore learnopengl.com. If you can’t deal with shaders, I recommend Book of Shaders .

Also popular now: