Differences between revisions 6 and 7
|Deletions are marked like this.||Additions are marked like this.|
|Line 6:||Line 6:|
|Line 10:||Line 14:|
===== Figure 0. Final rendering =====
Final Project Writeup
Bill Dwyer, Stanford University
Figure 0. Final rendering
I set out on this project to explore methods for better modeling and generating geometry for botanical models. In particular, I sought to create an alternative for L-system modeling in bonsai trees, which had particular problems such as abrupt branch edges and no root structure.
Model Generation Overview
To maximize the realism of the results, the basic tree generation algorithm mimics natures own growth cycles. A coarse rectangular grid is seeded with a single cell initialized as a “meristem”. A meristem is a specialized cellular body in plants that grow the roots, trunk, and branches that make up the macrostructure of the organism. In my algorithm, meristem growth is defined by vectors. The cells are populated using a line-drawing algorithm following the vector for a short time. To create interesting structure, these vectors can be determined from the location of the meristem with respect to origin. For example, a vector field given by equation 1 would result in a helical meristem growth.
(x,y,z) = (y,x,1)
Equation 1. Helical vector field
In plants the meristems propagate a frontier one cell at a time, and the xylem and phloem push the bark outwards. To imitate this behavior, all cells that were grown by the meristem are categories as “bark”. Bark grows in any direction it can. In my algorithm, I grow the bark to any 6-connected cell. Once a bark has expanded into all its 6-connected cells, it is categorized as “wood” and no longer grows.
The original generation of these materials resulted in extremely regular shapes. For example, a single bark cell allowed to grow unbounded produced a nearly perfect double pyramid. In order to combat this problem, I randomized the growth of bark. I create a candidate set of growth directions, and pick one at random for each generative step. Even then the final model was constructed only of 6-connected cubes, which have only axis aligned normal. To smooth the surface, I ran marching cubes (code found on web) over the voxel grid by offsetting the grid by (0.5, 0.5, 0.5) so that each voxel now represented one of the eight corners of a marching cube.
Figure 1. Bark generation
The first tests I performed were to make sure that the materials would, in fact, grow how I wanted them to. It is important to be able to control the growth of the tree with general rules while letting randomness do most of the work for you.
Figure 2. Stem growth
Figures 2 shows the effects of randomization on stem growth. The rendering on the left shows the regular method of growth to every empty 6-connected cell. The meristems start at the bottom and grew upward. As you can see, the base is wider than the top because the bark in that region had more time to grow. This behavior is precisely what I sought to achieve with the algorithm. The figure on the right shows how randomized bark growth can introduce a less geometric-looking section. These models were generated from a coarse 50x50x50 grid.
Figure 3. Branch growth
Figure 3 shows how creating new meristem cells at regular intervals results in a natural-looking branching effect. The bark growth phenomenon discussed for figure 1 is present in the joint between branches and produces a more natural transition.
Figure 4. Final model
Figures 4 shows the final model generation without the other scene components. As you can see the root structure is extensive. I first exported this model in RAW format and loaded it into Rhinoceros. It was quite impressive to rotate this model in real time OpenGL and examine all its facets. The still images really don't do justice to this incredible model.
This general generation algorithm was structured in order to maximize the performance of the algorithm. In particular, an aligned array is allocated representing the regular voxel grid in a predictable manner. That is to say for any given coordinate, it is trivial to compute the array index and vice versa. All cell are initialized to null, which takes up no additional space. Cells are only allocated when they are determined as part of the plant (e.g. bark, wood, etc.). Furthermore, vectors for each type are maintained. This allows very simple and fast retrieval when writing to a file for rendering in another program. Each cell is aware of its own location in the grid, which allows me to easily look up neighbors with very simple index arithmetic.
Making it Pretty
Once I had a viable model, it was time to stick it into an interesting scene to fully round out the final rendering. I started by incorporating other components from japanese culture, such as the Kanji characters for "Bonsai" written on a gong. Below are several rendering of the gong scene under various lighting conditions. I modeled the gong, the bowl, the table, the lathed legs, and the shoji screen by hand in Rhinoceros and exported to RIB, and manually modified the source to be compatible with PBRT.
Figure 5. Variations on a gong
In the end, the gong model over-complicated the scene and effectively hid the most important part--the tree. So, for the final submission, I decided to go with the simpler scene of three point lighting with a model of just the bonsai in its bowl on a table in front of a shoji rice paper screen. I think the simplicity of the layout speaks to the nature of Japanese culture.
I would have liked to apply a displacement shader to the model to further confuse the coarse voxel approximation, but I was dismayed to discover that displacement shaders weren't built into PBRT =(. However, simple bump maps were, and I added a Fbm bumpmap texture to the tree to add additional high-frequency noise onto the surface to better mimick bark's noisy material. The dirt geometry also recieved a dose of Fbm bumpmapping. The final detail was to add woodgrain to the wooden components. The hardwood table and its dark cherrywood stain and the light cedar-colored wood of the shoji screen were rendered with a "wood" texture shader that I wrote into PBRT with the help of some web tutorials on woodgrain texture.
There are several limitations to the model generation algorithm. Most notably, the resulting geometry is extremely coarse. In the end, the algorithm worked very well and did precisely what I was looking to accomplish. I’ve very pleased with the results of the algorithm, and think that I produced an interesting idea and novel approach. Furthermore, the final rendering is simple, but interesting.
P.S. Thanks for a fun quarter.