I made a program in MATLAB that can generates geometric models of trees. It varies the size and colour of leaves to simulate the changes in seasons, and can produce several species of tree or you can invent your own.
What is procedural generation?
Procedural generation means the generation of models or data algorithmically rather than having a person design it by hand.
Why is procedural generation of trees useful?
Procedural generation of trees is used in movie and game animation to generate a large number of unique and realistic trees without requiring a person to design each one. The tree models can also be generated at the time of animation, rather than being stored as geometric models, reducing the storage requirements for games. It is also useful in BIM and building performance analysis models to generate tree models that simulate the size, shading effects, and season variation of real trees.
Previous work in this field
Pirk, et al. (2012) - simulated the natural processes of tree growth, including tropism, pruning, and interaction with obstacles. This produces realistic representation of what a tree might look like if grown in a certain environment. However, it is too advanced and computationally heavy for animation processes.
Paul Brunt – Proctree.js uses forking branches to generate a realistic 3D model of the trunk and branches, and then uses texture mapping to add foliage on the end of branches. This is not always appropriate for geometry import / export or shading analysis as required by BIM packages.
Andrew Marsh (2020) – 3D Tree generator is a web app built using JavaScript, which builds on Proctree.js, adding options for volumetric clumps or geometric leaves, which are better suited to analysis and import to BIM packages. It also added support for simulation of coniferous trees with radial branches, and simulation of seasonal variation in colour and foliage size / density. It is built into a web app which allows users to vary parameters and see the effect in real time.
When the program is opened, the user is presented with the following screen, which includes a display of the tree model, and parameters split up into sections.
Parameters
The parameters that can be ajusted are as follows:
Tree size can be varied, by adjusting both the height of the top of the trunk, and the height of the leaf base.
The number, size, and orientation of branches can be adjusted. More specifically, the number of branches splitting off from the trunk, as well as the number of splits before each branch ends. At each split, the number of new branches and their angle can be adjusted.
Drooping can be simulated, by adjusting the weight of the branches and leaves.
Wind can be simulated.
Leaves can be turned on and off, and leaf colour, size, and complexity can be varied.
Tapering of the tree can be turned on and off.
Species
There are presets for 6 species of tree. From left to right: beech, purple leaf plum, weeping willow, dragons blood, baobab, pine.
Seasons
The seasons presets adjust the colour and size of the leaves.
Randomness
In order to make sure each generated tree is unique, the length and orientation of every branch and the colour of every leaf, is random within certain bounds.
Tree generation method
When the update button is pressed in the program, tree.m is run, which generates a tree model using the followng method:
First, a trunk is generated, with several segments.
At the top of each segment, a certain number of 1st generation branches split off. The angle and length is determined randomly within defined limits.
Each branch then splits again, into a defined number of 2nd generation branches. The angle and length of each is determined randomly within defined limits. The 2nd gen branches are rotated evenly around the axis of the 1st generation branch so they do not overlap.
Depending on the defined weight (gravitational constant), the end of these branches will move downwards.
Depending on the defened wind speed, the end of these branches will move horizontally.
Depending on the defined taper, the end of these branches will move towards the trunk relative to its current height.
The process of splitting process continues a defined number of times, producing 3rd generation branches and so on. Each generation of branches is slightly thinner than the previous one.
At the end of each branch in the final generation, a simple polygon leaf is drawn. The colour is determined randomly within defined limits.
Pseudocode
The pseudocode of tree.m is below:
Parameters fed into the function from the app:
2. Random numbers generated
3. The next section creates a series of arrays p_0 to p_n where:
Each array represents 'generation' of branches. Trunk = gen0; branches = gen1 onward.
Each row represents the start and end point of a single section of branch.
4. Trunk array:
5. Branches array
6. Drawing branches
7. Drawing leaves
Generating a realistic tree
The initial script treated the trunk identically to the branches, meaning that the number of branches from the trunk was equal to the number of branches produced at each node, or split. This lead to a poor representation of a tree. Andrew Marsh’s 3D tree generator provided inspiration for an improved approach. The trunk was built from a larger number of segments, whereas each branch would split into 2 or 3 children. This produced a much more symmetric and beleivable tree model whilst allowing differentiation of length between generations of branch.
Old model
New model
Adding randomness
The length and angle of each branch is random. Initially, the random function was called each time a new branch was created. However, running the profiler showed that approximately 60% of script runtime was due to the random function, as over 300 branches were created for some trees. Therefore, the approach was changed to run the random function once at the start of the script, generating an array of 10000 numbers uniformly distributed between 0 and 1.
MATLAB structures for elegant expansion of model
A challenge was encountered when coding the parameter n_generations. This was due to the iterative nature of branch generation. During the process of branch generation, the co-ordinates of the branches in each successive generation were represented by matrices from p_0 to p_n. The matrix p_1 was produced by a function taking p_0 as an input, and so on up to the iteration specified by n_generations. A solution was necessary to provide the ability to control at which point these iterations would stop. One method considered was as follows:
if n_generations >= 1
p_1 = function(p_0)
if n_generations >= 2
p_2 = function(p_1)
etc…
However, this approach is not elegant and is more importantly not expandable. Furthermore, a solution was required to refer to the matrix representing the final generation of branches, so that leaves could be drawn on the ends of these branches. The solution chosen was to create a structure to which the matrices p_0 to p_n would be successively added. A more concise version of the above code could then be written:
repeat n_generations times
append array to structure equal to function (last array in structure)
The final generation of branches could be defined as the final array in the structure. Therefore this elegant solution solved two problems simultaneously.
Implementation of pine tree
The radial branches of the pine tree were achieved using the same model as the branching model used by the deciduous trees. The number of trunk segments was increased to produce a large number of branches from the trunk. The tapering parameter reduces the length of the branches towards the top of the tree. At first, a spiral effect was visible, making the tree look like a helter skelter. This effect was mitigated by setting the rotation around the trunk from one branch to the next to 140 degrees, which is not a factor of 360.
Before changes
After changes
Setting discrete variables using continuous sliders
The sliders allow selection of parameter values within a range. Some of the parameters must have an integer value. To ensure this, each time a slider is moved, a call-back function rounds the value to the nearest integer and moves the slider to this value.
Code: The code for this project is stored in a GitHub repository:
Executable program: Since the program was written in MATLAB, you will need to either have MATLAB already installed on your PC, or install a MATLAB runtime in order to run the program:
If you already have MATLAB installed: download the program file from the first link. Unzip, and then run Tree_generator.exe.
If you don't have MATLAB installed: download the installer from the second link. Run Tree_generator_mcr.exe. This should prompt you to install the tree generator and a MATLAB runtime.