Practical 3: Advanced Shaders: shadow maps and noise
The deadline for giving back Practical 3 is: Friday, December 2nd (midnight, European Time)
There are two independent parts in this practical: Shadow mapping and Procedural textures with noise. You can do them in any order.
Shadow mapping
We render scenes with shadow maps in two steps:
- First, we render the scene as viewed from the light source, offscreen. We store the depth map of this rendering into the shadow map.
- Second, in the standard rendering, for each fragment, we compute its position in light space (as well as in screen space). We use this position in light space to access the shadow map, and compare the depth in light space with the depth in the shadow map:
- If the point being rendered is further away than the depth stored in the shadow map, then it is in shadow. We render it using only ambient lighting.
- If the depths are equal, then the point is lit. We render it using ambient, diffuse and specular lighting.
You will have to add shadow mapping to a shader that already does local illumination, such as 2_phong:
- In the C++ source file glshaderwindow.cpp, add the transforms that go from world space to the (projective) light space (it is near the point that says TODO_TP3 in the source code). You will need to pass these transformation matrices to the shader that computes the shadow map, h_shadowMap (this one is already written).
- You can check that the shadow map exists, and what it contains, by uncommenting the line debugPix.save("debug.png"); in the source. Check that the shadow map changes as you move the light source.
- In your shader, when you compute local illumination, you have to compute object coordinates both in camera space and in light space. You have already done the former (in Practical 1). For the latter, reuse the transformation matrices you used to compute the shadow map. Send them to the shader that is computing illumination.
In the vertex shader, apply both transforms to vertex coordinates, and store both results:
No bias With biaislightSpace = worldToLightSpace * worldCoords; gl_Position = perspective * matrix * worldCoords;
In the fragment shader, use lightSpace both for:
- the distance from this point to the light source, lightSpace.z,
- texture coordinates in the shadow map, lightSpace.xy
As in the previous practical (with bump mapping), the coordinates you have after transform are in , while texture coordinates are in . You will have to rescale from one system to the other.
Don't forget that a projection matrix changes the w coordinate in homogeneous coordinates. You have to account for this in your code.
Start by computing shadow mapping without any bias in the depth comparison. You should get a picture that looks like the top picture (maybe with even more stripes). In a second stap, add the bias to the depth comparison. There are several bias methods: add epsilon, multiply by (1 + epsilon), take the slope into account, etc. Experiment with them, keep the one that seems to be most efficient / working for your test scenes.
Question: what happens when you query the shadow map beyond its boundaries? How can you detect is? What should you do? What should you do when w is negative?
Procedural textures with noise
In the shader noiseAlone there is a function, perlinNoise. This function computes a Perlin noise, as a function of position.
It takes several parameters: the number of octaves, the amplitude, lacunarity and persistence. You can act on these parameters, depending on what you need.
Low persistence | High persistence |
Your work is to write shaders using this noise function to compute procedural material textures. Start with unstructured materials, such as marble or jade:
Procedural Jade texture | Procedural Marble texture |
For these materials, you just need a mapping from noise to a 1D colormap, with linear interpolation.
Next, try a structured material such as wood. Start by defining a line, and compute the distance to this line. Use this distance to access a 1D colormap, giving a regular wood structure. Then, perturb this function with Perlin noise, resulting in irregular wood structure.
Procedural wood, without noise. | with Perlin noise |