- In this segment, we're going to go into detail
about Gouraud and Phong shading.
Let's first talk about Gouraud shading,
which is the standard smooth shading.
Note also that this is a lecture
that actually talks about the operations
that happen in rasterization.
In a standard vertex or pixel shader,
you would just give the colors
for I_1, I_2, and I_3,
and after you've done that,
you would expect the rasterization hardware
in OpenGL to do the interpolation.
So you don't need to worry about
the details of this for Gouraud shading.
However, we are interested in learning, for this course,
what actually goes on under the hood.
So here we have three vertices,
I_1, I_2, I_3,
and those correspond to the colors here,
so we assume I_1 is the color of vertex 1,
I_2 is the color of vertex 2,
I_3 is the color of vertex 3.
Notice that the vertical extent
goes from y_1, all the way to y_3.
So we want to find first, the,
to find the color of I_p,
we first want to find the colors at I_a and I_b,
and then we want to interpolate them.
So let's first talk about the color at I_a.
I_a lies between I_1 and I_2,
so if you want to do the interpolation,
we do it along the vertical direction.
Look at the length of this region,
and the length of this region.
So the length of this region is y_1 minus y_s.
The length here
is y_s minus y_2.
The total length, of course, is y_1 minus y_2.
Now it's just a question
of doing the standard interpolation formula.
Which will mean that I_1, because it's further away from I_1,
it will be multiplied by the smaller quantity.
So this will get I_1,
and this quantity will multiply I_2.
Indeed, that's the formula here:
I_1 times (y_s minus y_2),
plus I_2 times (y_1 minus y_s).
And divide the whole thing by the total length here,
which is y_1 minus y_2.
That's the formula for I_a.
We can similarly get a formula for I_b,
which will be equal to:
I_1, times, in this case,
(y_s minus y_3),
and I_3 times (y_1 minus y_s),
and now you normalize by y_1 minus y_3.
Once you have the formulae for I_a and I_b,
the question is, what do you get for I_p?
And in order to consider that,
one needs to consider the X-coordinates,
because one is interpolating within a scan line.
So we can consider this as being the x of a (x_a),
and we can consider this as being x of b (x_b).
Here, of course, you have x of p (x_p).
So the interpolation idea is the same.
You have this length, which is x_p minus x_a,
and you have this length, which is x_b minus x_p.
So I_a will be multiplied by (x_b minus x_p),
and I_b will be multiplied by (x_p minus x_a).
Indeed, this is what happens here.
So you have I_a times (x_b minus x_p),
plus I_b, which is multiplied by (x_p minus x_a).
And again, you normalize by x_b minus x_a.
So it's just interpolation first in the vertical direction,
in order to get the locations
for the end points of the scan line,
and then interpolation along the scan line to get the,
horizontally to get the final color of I_p.
The actual implementation of this is much more efficient
than the formulae will let you believe.
For example, I can pre-compute
the division by y_1 minus y_2,
I can find the reciprocal of that,
y_1 minus y_3, x_b minus x_a,
and moreover, you see,
as you go from one scan line to the next,
the multiplicative factors, they reduce to addition.
And therefore they are very efficient algorithms,
that, in fact, don't involve
multiplication or division at all,
they just incrementally add quantities
as you go from one scan line to the next.
And this is a process
which is generally known as scan conversion,
but it also does the interpolation
which is needed for Gouraud shading.
Errors, we've already talked about those
in the previous segment.
So here is an extreme case,
where I_1 and I_2 are zero,
because either the light is pointing backwards,
or the eye does not see the point.
And now you have zeros in both cases,
and you want to interpolate to make a highlight.
Of course, that's not going to happen.
If you have zero at the two vertices,
the interpolation is also zero.
And so Gouraud can have problems.
Moreover, the way we implemented it,
where we went vertically and then horizontally,
means the shading is not rotationally-invariant.
So if you were to rotate a point,
you would actually see the shading change.
For all of these reasons, Gouraud shading is useful
only when you have relatively smooth shading effects,
mostly for diffuse shading.
But prior to the advent of fragment Shaders,
you also needed to use Gouraud shading
for specular highlights.
And the way you handled that was by tessellating,
or breaking the model into enough triangles,
that, relative to the size of the triangles,
the shading was still smooth.
Next, we consider the Phong Illumnation Model,
which is really the motivation also
for the use of Phong shading,
and the use of fragment shaders.
And this corresponds to a specular or glossy material,
where you have highlights.
At the bottom I've just shown some renderings
I made many years ago,
where you've taken a lighting environment
or a light probe that was acquired at Berkeley
by Paul Debevec.
It's in the Grace Cathedral in San Francisco.
And I've rendered images
with many different roughness settings,
and you see from left to right,
it starts off being a mirror,
then it gets blurred out,
and eventually in the right,
it almost looks like a diffuse surface.
And that, the amount of roughness,
controls the width of the highlights,
and your perception of how shiny the object is.
Examples of specular or glossy materials
are polished floors, glossy paint, whiteboards.
Furthermore, highlights behave somewhat differently
from plastics or dielectric materials
as opposed to metals.
So on plastics, the highlight
is the color of the light source,
not the body color of the object.
And this is a very common thing,
you could have a green ball,
and you look at the highlight, it's still white,
because the light source is white.
Metals, on the other hand,
the highlight depends on the surface color,
and is modulated by the surface color.
We don't have time
to go into the physics of all of these effects;
hopefully that's something you can look up,
or we can cover in a future course.
But these are basic properties
of illumination and highlights,
and it's also possible to regard them, really,
as blurred reflections of the light source.
If you look in the bottom panel,
you can consider that the light source
is just being blurred out as you go from left to right.
So Phong illumination, coupled with Phong shading,
is the appropriate way to make highlights.
And again, it's worth noting the distinction between these.
They're commonly used together,
but technically, they're different aspects.
The idea of Phong shading
is simply that instead of interpolating the colors,
what one actually does is interpolate the normals.
So here we have the color was zero,
but there's a correct normal in both of these locations.
You interpolate the normal,
and so you get the correct normal at the center,
and therefore you can evaluate the illumination model
and get a highlight.
So the entire lighting calculation
is performed for each pixel.
In old style OpenGL, there was no Phong shading,
it was just Gouraud shading,
and therefore you would have to
break this geometry up into enough triangles
that it did actually compute the shading at each vertex.
But in modern OpenGL and homework 2,
you just write a fragment shader,
and in this way, you can do perfect Phong shading.
Let us now look at the vertex shader.
Remember that this vertex shader
comes from the mytest3 suite of programs.
I encourage you to look at the code
for these programs and the shaders,
to get an idea of what you can do in OpenGL.
First line says version 330 core,
which just tells OpenGL that this shader
is using GLSL 3.30,
which is what we're using in our course.
Let's now come to the per-vertex
inputs to the shader.
They're in location 0, location 1, and location 2.
The layout command says which location are they in.
in just says they're inputs,
and vec3 says what kind of information is there.
In this case, the position is the x, y and z coordinates,
which is a three-vector.
The normals, similarly, is the x, y, and z coordinates,
it's a three-vector.
The texture coordinates input are just two variables
which are typically denoted as s and t.
The outputs are the extra outputs from the vertex shader,
which will be interpolated or rasterized,
and sent to the fragment Shader.
These correspond to myvertex for lighting calculations;
mynormal, again, for the surface normals
in the fragment shader for lighting calculations;
and the texture coordinates,
which will be provided to the fragment shader.
Let's look in more detail at the shader.
Again, we have the version 330 core.
This was all the code we saw earlier.
The uniform variables
are variables that are the same for all vertices.
In this case, they are the projection matrix
and the modelview matrix.
Both projection and modelview matrices
are 4x4 matrices,
which are specified with the command uniform mat4.
istex simply says:
are we doing texturing, or are we not?
It's just a flag.
The main routine
is where the vertex shader is actually executed.
gl_Position is a defined variable
which sets the position within OpenGL
for rasterization,
which says where the vertex should go on the screen.
As is conventional,
the vertex location will be given
by multiplying by the modelview matrix
and the projection matrix;
in fact, by the product
of the projection and modelview matrices.
Notice that we are now doing things
in homogeneous coordinates,
and therefore, we have to define a vec4,
taking the x, y, z coordinates of the position,
and the w-coordinate equal to 1.
We also define the normal,
which will be used in the fragment shader.
This simply applies the normal transformation.
Note that the normal transformation
is given by the inverse transpose of the modelview matrix,
which is why we are doing transpose inverse modelview,
times the normal.
We define the vertex location in eye coordinates,
in the 3D world.
This is different from gl_Position,
which applies the projection matrix
and tells you where it goes in the screen
or in normalized device coordinates.
However, we also need the value in eye coordinates
in the 3D world,
for later shading calculations in the Fragment Shader.
That is done by multiplying the modelview matrix
times the position,
again, in homogeneous coordinates we made into vec4.
Finally, we come to the texture coordinates.
To avoid errors,
I initially set the texture coordinate to (0,0),
which is just the default value to prevent errors.
In the next step, if texturing is turned on,
then I have texture coordinates
that were input to the vertex shader,
and I just set texcoord equal to that.
These texture coordinates
will then be interpolated and rasterized,
and they will be passed on to the fragment shader
for texturing.
The next thing we're going to talk about
is the type of different lighting and materials.
Lights: points and directional,
and shading: ambient, diffuse, emissive, and specular.