Making of …
The perfection of the geometry and textures from some rendered scenes tends to reveal its artificial origin. Real photos can be used for texturing providing a more realistic look, but creating these textures can be a time consuming task. For instance, when we take a photo of a brick wall, we use some sort of illumination to be able to take an appealing picture. By looking at the picture, mainly its shadow areas, we can guess where the light comes from. This light direction may or may not coincide with the light direction of the rendered scene, and when it doesn’t the mismatch can be visible, making the scene look awkward.
Furthermore, once we get a nice picture of a wooden floor we may need it to be tilable so that large areas can be covered with the texture. After constructing a tileable texture we are left with pattern repetitions, for instance that small imperfection in a wood plank will appear in the same position in every tile. Even if this is fixed there is the issue of the required texture size, how close can we get the camera to that wooden floor without getting the pixelated effect?
Now consider an even bigger challenge, lets make a wooden chess pawn. Creating a 2D texture that would cover this volume, displaying plausible wood rings and veins seems a very hard task. What if you wanted the whole chess set? Then this process would have to be repeated for every figure (assuming that we don’t want any two pawns to look exactly equal).
There is no magic wand to solve these issues, but there are some techniques to build materials entirely based on algorithms. These are procedural materials, aka procedural textures [1] since they serve the same purpose as traditional textures.
These materials come with the following bennefits:
- no repeating patterns;
- able to zoom up close without getting a pixelated effect;
- can deal with flat surfaces as well as volumes;
- not dependent on the geometry, in the sense that the same function can be used for all the chess figures;
- the same algorithm can generate a wide range of effects by playing with its parameters;
- the surfaces get a “natural” look due to small random variations introduced by the algorithms;
- the surfaces have not been “pre-lit”, so they fit in every lighing scenario;
- no memory is required to store the textures, these are computed on the fly.
On the negative side we have:
- can be slower than a solution based solely on texture fetches since the “texture” color must be computed on the fly;
- can be difficult to fine tune the effect to get the required look and feel.
Before exploring some procedural materials we need to discuss a feature that provides a major contribution to their natural look: noise. Not just any noise, but smooth noise. We do not want major variations from one pixel to the next, rather a subtle effect will be more effective.
Ken Perlin [2] developed a method to compute this noise efficiently. Tutorials on noise, and how to build the noise function are available in [2,3,4,7,8]. Justo to give an hint on the results obtained when using the noise function here are some examples:
noise(p) |
noise(2p) |
noise(4p) |
noise(8p) |
These images were obtained by doubling the space between samples, i.e. by sampling successive octaves. The leftmost image is smooth noise, whereas the image on the right is almost pure noise. By themselves they are not very interesting, but when composed in a fractal sum they result in a more useful function.