Practical 4 - Local Illumination

Objectives of this practical:

  • introduce lighting in your scenes

  • improve your understanding of illumination models

  • implement the Phong illumination model, as seen in the lecture

Pre-requisites:

  • it is best but not mandatory to have completed Practical 2 - Meshes and modeling

  • you can have a look at the GLSL tips page to get familiar with additional GLSL operations needed for this practical (normalize(), dot(), reflect(), pow()…)

For this practical, we provide a new set of files viewer.py phong.vert phong.frag, to be used with the core.py transform.py given in the previous practicals. The provided viewer.py is a very simple skeleton to load objects from files using the generic load() function, provided in the core.py utilities and render them all with the Phong shader. We now work with complete implementations of Mesh and Viewer from core.py. For this practical, you will thus be mainly working on the GLSL shader code.

Most 3D file formats are able to specify Phong parameters, for example .obj files have sister material files with .mtl extension which store these parameters in readable ASCII format.

For testing, you can find different material samples in this material file. To use, insert the following lines with any material name found in the above file, at the head of the suzanne.obj 3D file:

mtllib vp.mtl
usemtl aqua

Exercices

1. The Lambertian model

Until now, we mostly studied how to create complex geometry and how to use OpenGL functions to manipulate shaders and buffers on the GPU. Today, you will mainly work on the shaders themselves using the GLSL programming language.

Your first task will be to implement the Lambertian model, defined at any surface point by the following function:

\(I = K_d (\mathbf{n} \cdot \mathbf{l})\),

where \(K_d\) is the color of the object (also called albedo or diffuse color), \(\mathbf{l}\) is the direction of the light (a unit vector) and \(\mathbf{n}\) is the normal of the surface.


_images/lambert_vectors.png

This function will have to be written in the fragment shader to obtain the final rendering. The light vector defined is passed as a uniform variable. Make also sure to take the following remarks into account to obtain a proper result:

  • as shown in the figure above, the convention is to use only vectors directed outward with respect to the object surface.

  • if the light is located under the surface, the scalar product will result a negative value and should thus be clamped to 0.

  • after being rasterized, the normal might not be a unit vector anymore. Make sure to re-normalize it in the fragment shader.

  • the illumination computation must be done with data located in the same coordinate frame. If the normal is defined in the camera frame, the light direction must be expressed in the same space.

Note

Passing the NIT matrix

In case of non-uniform scaling, the model and view matrices cannot be used directly to transform normal vectors. As seen in lecture 1, the proper way to tranform a normal is to use the NIT matrix: \((M^{-1})^\top\) where \(M\) is the upper left 3x3 matrix of the modelview matrix. Look up the inverse() and transpose() GLSL functions to do it in GPU, or use numpy.linalg.inv() and a.T to transpose a Numpy array a on the CPU side.

2. The Phong model

The Phong model uses an additional term that accounts for specular reflections:

\(I = K_a + K_d (\mathbf{n} \cdot \mathbf{l}) + K_s (\mathbf{r} \cdot \mathbf{v})^s\),

where \(K_a\) is a constant ambient color that approximates complex inter-reflections between objects in the scene by a constant term, and \(K_s\) is the color of highlights, or specular color (usually white). The exponent \(s \in (0,\infty)\) is the shininess and controls the shape of the specular lobe. Tiny lobes (or mirror-like) surfaces are obtained with high values of \(s\).


_images/phong_vectors.png

\(\mathbf{r}\) is the reflected light vector and can easily be computed using the GLSL built-in reflect() function. The view vector \(\mathbf{v}\) can be either given as a uniform variable or directly computed in the shader via the model and view matrices. Note that a position defined in camera space (after the transformation by the model and view matrices) already provides a non-normalized view vector. Once again, be careful with negative values that must be clamped to 0 (a shading function should not create negative energy).

Optional Exercises

The exercises below are all independent except 4 and 5.

3. Control light and shading parameters

Level: easy

Assign some key handlers to control the parameters of the Phong model. You may want to change the color of the surface (\(K_d\)) or to modify the shininess \(s\) for instance.

Animate the light direction \(\mathbf{l}\) in such a way that it rotates around the scene. Hint: a uniform timer will be necessary in the shader to compute the new direction at each frame, which you can retrieve with glfw.get_time().

4. Point light

Level: medium

Previous exercices only consider a directional light (where the direction is the same everywhere on the surface). This definition is usually used when considering distant lights, such as the sun.

The goal here is to use a point light instead, for which the direction changes locally. Moreover, its intensity decreases with the square of the distance between the surface and the light. Formally, the Phong model becomes:

\(I = K_a + \frac{1}{d^2} [K_d (\mathbf{n} \cdot \mathbf{l}) + K_s (\mathbf{r} \cdot \mathbf{v})^s]\),

where \(d\) is the distance between the position of the light and the surface point at which the illumination is computed.

5. Multiple lights

Level: easy

Complex scenes usually use multiple lights to obtain more realistic renderings. Your goal is to define and animate/control 2 or more directional and/or point lights. The equation becomes:

\(I = K_a + \sum_k \frac{1}{d_k^2} [K_d (\mathbf{n} \cdot \mathbf{l_k}) + K_s (\mathbf{r_k} \cdot \mathbf{v})^s]\).

When using multiple lights, the result is simply computed as the sum of all contributions. You may try to assign a different color to each light in order to see them separatly in the rendering.

Elements of solution

We provide a discussion about the exercises in Practical 4 - Elements of solution. Check your results against them.