The topic of the series is carpentry, so it is critical to be able to render wood in a convincing way. The most important feature of wood planks and timbers is the grain, which results from cutting flat planes through the cylindrical tree rings in a tree trunk. Broadly speaking, tree rings result from fast and slow growth at different times of the year. The orientation of the grain has important consequences for the strength of structural members and joints. It also has a large effect on the movement of wood with time: wood swells and shrinks radially across its rings, but hardly at all along the original length of the tree. In some applications it is important to orient two pieces so that, for example, the young wood from the top of a tree is abutting or the outer growth rings end up next to each other.
The patterns resulting from the tree structure are readily apparent on cut boards. The end grain shows the characteristic tree rings, while the faces of a board will show different patterns depending on how the board was cut from a log. This site has some good illustrations of the different possibilities. Also, There are fewer rings at the top of a tree, which produces a diagonal orientation to the grain on some sides of a board and, if the board is cut near the center of a log, a distinctive wishbone pattern. If we can show these features in an illustration, then the reader will instantly know how a particular timber or board should be oriented.
In the 3D modeling world, wood grain is best implemented as a procedural texture which calculates the surface color at every visible point. We can thus develop a mathematical model that mimics the ring structure of a tree and use it to drive the procedural texture. In Blender, the most flexible way to develop a procedural texture is to use the material node editor, which enables the modeler to assemble networks of building blocks that calculate the surface properties of an object.
Here is our wish list of wood texture features:
- The texture should be easy to apply to a 3D object that represents a particular timber or board. It should be aligned with the local orientation of that timber.
- The texture needs to model the growth rings of a tree. Physical characteristics should be represented: tree rings get closer together at the outside of a tree and, as mentioned before, there are fewer tree rings at the top of a tree. The actual spacing of the rings is not critical. A physically correct model might produce so many rings that we would get severe aliasing at the scales which interest us. Also, there is a wide variation in the spacing. Nevertheless, the rings must be suggestive.
- For artistic purposes, the modeler should be able to specify the light and dark colors of the ring. Also, there should be some variation in the rings: growth rates are different from year to year, and tree trunks are not all perfectly round.
- The grain should look good and not exhibit aliasing artifacts, or at least provide ways for the modeler to avoid aliasing. On the other hand, the grain texture is not photo-realistic. I don't care at all about knots in the wood, nor about the reflectance properties of the wood at a micro level.
Blender's Wood TextureBlender already has a wood texture. Here is an example of it applied to a rectangular shape: this tutorial. The point of my rendering is to demonstrate that its structure is made of concentric spheres. This isn't suitable for rendering the cylindrical structure of a tree trunk unless you stretch it out in one direction or only use a 2D slice of the texture. Even if we did that, it would still be complicated to implement the other features we want, such as having fewer rings at the top of the tree. We could use this built-in texture as a basis for the repeating pattern of our texture, but we will get more flexibility from implementing our own periodic function.
Age from RadiusOur procedural shader, implemented as a Blender material node network (called a "noodle" in the blender world), will take as input coordinates from the surface of a piece of wood, calculate the age of the tree at that point and thus the corresponding ring color. If our modeling scale is in centimeters, for our initial effort we can assume that a tree's radius grows at 1 centimeter per year and so use the radial distance from the center of the tree as the age.
We now need to choose the proper coordinate system in which to calculate that distance. Blender provides coordinates that are called "generated" coordinates or "Orco" (for Original Coordinates) in different parts of the program. These are conveniently aligned with the local orientation of an object and its origin, but by default they are scaled to the bounding box of the object, which is not good. Fortunately you can turn off this scaling. This screen shot shows the desired option choice in the mesh panel:
We know how to get the age from the radial distance, and we can derive a function that ranges from 0 to 1 during year by using the non-integer part of the age. That will give us a value that can be used almost directly to choose a color. Blender doesn't have a truncate function, but it does have rounding function, and we can use that, after subtracting .5 from the value, to get the integer part, then do another subtraction to get the non-integer part. However, if we used that value as is, we would get terrible aliasing at the sudden transition from 1 to 0 at the end of the cycle. We use a color ramp and choose a cubic interpolating spline to get a smooth transition.
Here is our first effort at a node network:
The first improvement we can make is to simulate the fact that vertical growth happens at the top of a tree, which implies that there are fewer rings as one climbs the tree. We implement this by adding a value that increases as we move up the tree trunk to our radius / age calculation before truncating it; this has the effect of moving the pattern in towards the origin as we move up the tree. Here's the next effort at a node network:
Several factors cause the radial growth of a tree to slow down as it gets older. There is a fixed number of cells at the outside of a tree where growth occurs; these cells spread out and have less capacity to grow outwards. Also, a tree simply becomes less "vigorous" with age. Foresters use very complicated empirical models to estimate tree growth. I will choose a simple logarithmic function that captures the basic idea of a the rings getting closer together. It is plausible that a tree trunk would grow as much in its first 50 years as it does in the 500 years after that. The inverse of a logarithmic growth function is an exponential function; we implement that in our node network, and after trying some different values we arrive at this:
We are now ready to move from black-and-white to real wood colors. The output of the color ramp node is used to choose the mixture of the dark and light wood colors. The Blender mix node implements this:
There are still some problems with the texture. The rings are perfectly circular and regular, with no variation from year to year or around the circumference of the tree. The material node network is flexible, but the various parameters are scattered around it and would be painful for a user to manipulate. Also, it will be messy to copy the network into all the various wood materials that we might wish to correct. In the next installment, we will tackle the first issue using noise textures and the others using Blender node groups.