Light
- In previous lessons we have discussed light in general.
- The problem with ray tracing is we can't do it quickly enough (or at least we couldn't in the past)
- So there is the classical lighting equation.
- Today is at best a quick overview of this process.
-
A great demo
- There are three models implemented here.
- Flat shading which is at the polygon level
- Gouraud which is a vertex/pixel level algorithm.
- Phong-Blinn which is a pixel level algorithm.
- We will discuss the lighting model behind these.
- This model uses three components to compute the light at a pixel or fragment.
- Ambient light or overall background light.
- $k_a$
- for each surface, or the amount of ambient light "reflected" off of the surface.
- Diffuse or scattered light.
- $k_d$
- Modeled with Lambert's reflectance or Lambert's Law of cosines
- Specular light
- $k_s$
- or light that is directly reflected off of the surface.
- Ambient light or overall background light.
- The intensity at a pixel is $I$ and is
- $I = k_aR_a + k_dR_d + k_sR_s$
- If there are many lights, $I = k_aR_a + \sum_{lights} (k_dR_d + k_sR_s) $
- Where $R_*$ is computed based on the light under consideration.
- This model uses three components to compute the light at a pixel or fragment.
- The model also employs a number of vectors.
-
(Source unknown)
- The point where all vectors start is the pixel or polygon we are lighting.
- N is the surface normal.
- In Gouraund this is supplied per surface.
- In Phong-Blinn this is supplied per vertex and interpolated.
- In the example code, this is multiplied by a matrix because we may need to "undo" non-uniform scaling and other transformations.
-
(source scratchpixel.com)
- L is the angle from the point to the light.
- This is
vec3 L = normalize(lightPos - vertPos);
- This is
- R is the angle of reflection
- Where direct light bounces off to.
- V is a vector that points at the viewer.
- H is a vector added by Blinn,
- it is halfway between the viewer and the light.
- It is a cheat that makes computations faster.
-
- Lights
- We tend to model lights as pinpoint light sources for simplicity.
- Lights can have many properties, but in this case we will assign the following: $L_a, L_d, L_s$
- remember, $L_*$ and $k_*$ are probably 3-vectors.
- For ambient light, $I_a = k_a * L_a$
- $k_a$ is the material constant.
- If we wish very fine control, we can break this into red, green and blue components.
- Otherwise it can be a single constant.
- $L_a$ is the ambient lighting property of the scene.
- Sometimes you see this broken down by light.
- But normally it is constant across the scene.
- $k_a$ is the material constant.
- Diffuse light is computed using Lambert's law of cosines.
- The amount of light reflected off a surface is related to the angle between the angle of incidence (L) and the normal vector (N)
- $L\cdot N = |N| |L| cos \theta$
- where θ is the angle between the two.
- But if N and L are normalized, this is the result we want.
- Let's look at the cos function on wolfram alpha
- When the normal vector and the light vector are about the same (θ small), there is much light reflected
- When the normal vector and the light vector are at a big angle, (θ large), there is little light reflected
- cos(90) = 0, we can't see this pixel.
- Note if |θ| > 90, cos(θ) < 0
- Thus the max in the equation below.
- $I_d = k_D (N \cdot L) L_d$
-
void main() { vec3 N = normalize(normalInterp); vec3 L = normalize(lightPos - vertPos); // Lambert's cosine law float lambertian = max(dot(N, L), 0.0); - Notice if this is 0, we can not see the surface.
- Specular reflection is how the light interacts with the viewer.
- This is related to the angle between the light and the viewer.
- This takes an additional parameter shininess (α)
- This indicates how "tight" the reflection is.
- Mirrors have a very high (100) shininess value) and produce vert tight reflections.
- "Flat" finish surfaces have a low shininess value and produce bigger reflections.
- Back to wolfram alpha.
- $I_s = k_s(R \cdot V)^{\alpha}L_s $
- We use the following
-
if(lambertian > 0.0) { vec3 R = reflect(-L, N); // Reflected light vector vec3 V = normalize(-vertPos); // Vector to viewer // Compute the specular term float specAngle = max(dot(R, V), 0.0); specular = pow(specAngle, shininessVal);
-
- $I = k_a L_a + k_d(N \cdot L)L_d + k_s(R \cdot V)^{\alpha}L_s$
- Blinn noted that $R \cdot V)$ can be approximated by a halfway vector between L and V.
- $H = \frac{L+V}{|L=V|} $
- This can be computed once per surface, except for very large surfaces.
- And was faster (until GPU)
- So it is common to do this replacement.
- We can tune this for various light sources.
- A common addition is to allow the light to attenuate over distance.
- The correct formula is inverse to the square of the distance.
- But we frequently use the following
- For constants a, b, c.
- $A = \frac{1}{a + bd + cd^2}$
- $I = A(k_a L_a + k_d(N \cdot L)L_d + k_s(R \cdot V)^{\alpha}L_s)$