Procedural Art: Plant Generation in Houdini

Kilian Baur did a breakdown of his Procedural Succulents project made in Houdini: math, leaf generation, soil variation, and more.

Introduction

Hello, my name is Kilian Baur and I’m a CG Generalist from Germany.

I've been always interested in traditional art techniques like painting, clay sculpting or molding/casting objects. During high school, I got interested in programming and made many very small programs using java. Wanting a creative job, I tried both architecture and computer graphics. Turned out, CG for me was the perfect combination of art and technical work like programming.

During my studies, I’ve tried a lot of different disciplines within 3D like animation, character sculpting or texturing. But what I have always enjoyed the most was modeling. I also started learning python to automate some repetitive processes within Maya. Because two friends of mine were really into Houdini and made some pretty awesome stuff, I decided to try it as well. I did some projects like procedurally modeling a chessboard and generated different board sizes other than the classic 8x8 grid. 

During my internship at RISE Visual Effects Studios and working on Mike Flanagan’s Doctor Sleep, I really learned to love the procedural workflow of Houdini. I started doing as much stuff as possible within this software. 

Procedural Approach

While I love 3D, some tasks can get pretty repetitive after a while. Sometimes, it is relaxing to hear music and move some polygons around but I find the procedural approach much more engaging. It is like a puzzle, you are problem-solving and trying to find a way to make something fully procedural. This is why I enjoy Houdini and programming so much. They both reward a more technical mindset.

Because a procedural setup often takes more time than a destructive approach it’s not always the right decision to make every asset fully procedural during a movie-production. At home for personal projects, I don't really have time constraints and can challenge myself.

When creating a new project, most of the time I have a loose idea of how I want to approach it. The approach is different for every project. In general, I split the projects into smaller specific tasks and then try to solve each one by one. 

Sometimes, I already know how I can achieve certain effects in Houdini (from tutorials or previous projects) or I might think of a way to make it possible. Then, I have to figure out if there are already nodes that can solve my problem or if I have to use Python or Houdini’s expression language VEX to build custom solutions.

Working procedurally is so interesting because there are many ways to solve each problem and every artist might solve them in a different way. Exchanging knowledge with other artists is a great way to learn new techniques and workflows.

Even if some approaches don't turn out to work as intended you can always go back a few steps and try another one. Because everything is procedural, you don't really lose anything trying an idea.

1 of 2

About Procedural Succulents Project

During my internship, I learned a lot about 3D but didn't get to do personal projects. I wanted a few smaller projects that I could finish in a couple of days and which showed some of my new skills.

Currently, I am interested in the shape of plants and how it is possible to generate them in 3D. I’ve learned a lot about L-Systems, which are a great way to describe the growth of plants, and wanted to make a project utilizing them. While searching for references for this project, I found two really cool photos of succulents.

1 of 2

Creating a succulent seemed like a fun challenge. At first, I only wanted to create the "classic" succulent but after finishing it, I decided to test how many different shapes I could get out of my setup.

Also, I wanted to test some other render engines. While Houdini’s own "Mantra" has many cool nodes and functions for shading, it is also known for not being particularly fast. Especially rendering noise-free subsurface scattering (SSS) can be pretty painful. For this project, I chose Arnold for Houdini because the Arnold Standard Surface Shader is one of my favorites and the GPU rendering looked promising. 

Math Part

The amount of math and scripting required is different for each project. Sometimes, it is tricky to find rules or dependencies on which other processes can be built. Some projects might require a lot of vector math, like using the angle between two vectors or using rotations matrices/quaternions to rotate objects. 

The succulents are pretty straightforward in that department. They don’t need any complex mathematics. Most math consists of simple multiplications, divisions and additions where I calculate the offset for each leaf. That way the leaves in each level are evenly distributed and then I offset them a bit for some variation.

Also, I calculate some attributes for the leaves. They are based on a few ramps that control the overall shape of the plant. That way it is quite easy to make broad changes to the look of the plant.

Leaf Generation

The general approach involved creating a single leaf as the first step. Then, I copy and rotate the leaf for the number of leaves per level. Finally, I repeat this for the number of levels per plant. 

Each level has some different settings for the leaf generation. That way, I can create interesting shapes. To control the shape, I have a few parameters. I can, among other things, set the number of levels, the count of leaves in each level or the length of individual leaves.

The length, width, angle etc. are different for each plant-level. To easily control those attributes, I have a ramp-parameter and a float-parameter (floating point number). The ramp controls the shape of each attribute. Because ramps are only easily controllable with a range from 0 to 1, I also use the float-parameter to change the amplitude of each ramp.

To use the ramps, I subdivided them to the number of levels the plant has. I read the values of each step and save them as an array of numbers. While generating the leaves, they read the values from the position that matches the current plant-level. 

With this approach, I can add as few or as many levels as I want without doing more work. The more levels each plant has the closer it resembles the shape of the ramps. 

I generally had the ramps in a way where the leaves are quite small at the bottom. They get taller and for the last few levels, they get shorter again. Also, the bending of each leaf is quite drastic between levels. On lower levels, the leaves are almost flat but the bending increases with each level until they almost form a closed shape.

In this example, the plant has 9 levels with 6 leaves each. The leaves in the first level are bend 10° and the bending amount goes up to 110°. 

To generate the leaves, I start simple with a line. I resample it to get some more resolution and also generate a “curveu” attribute. This attribute has a value of 0 at the root and goes up to 1 at the tip. Using these values, I can define the shape and finally add some color to the leaf. 

Based on a ramp, I move each point a certain amount in Z-direction to form the general shape. I add a center-line and use a “sweep” Node to create a flat half of the leaf. After that, I bend the sides upwards and mirror it to get a full leaf.

I use a “polyExtrude” and slightly smooth the positions to add thickness. A noise on the point positions is used to add some variation on each leaf. That way, each leaf looks a bit different even if they are within the same level.

Before I bend the leaves, they get their UVs by simply doing a planar projection.

To add some more details and create a visible spine in the center of the backside, I move the center-lines a tiny amount along their normals.

Procedurally finding the required points wasn't difficult. When I was mirroring the two sides, I used the “fuse” Node to combine both sides. The fuse Node has the ability to output a group that only contains the points which were modified. Those are the points which I used for the center-spine.

The color is added using a color-ramp going from root to tip. At first, I only wanted the color to visualize the different plants. But I ended up rendering this color-attribute because it was good enough, especially after adding some SSS, bump, and sheen.

The whole leaf generation setup is inside two loops. The inner, green loop repeats until the leaves for one level are generated. The outer loop is responsible for the number of total levels. Using the current level, the leaves read their specific parameters (e.g. length, angle, etc.) from the previously generated array. Also, I use the current leaf and the current level to generate some randomness for some parameters. That way, each leaf looks a little bit different.

Pots & Soil

The pot is mostly procedural. I didn't need the ability to greatly change its shape after modeling. That’s why I explicitly selected specific edges for some operations.

The soil, on the other hand, is fully procedural and it is possible to change its height. The first step I took was clipping the pot-geometry at the wanted height. Then, I deleted everything that was not on the inside. I used a “polyfill” Node and deleted everything except the result to get a perfectly fitting plane on the correct height. Finally, I remeshed the geometry into many even triangles and added UVs. During rendering, I subdivide this plane a couple of times and use some textures from Megascans for the shader and displacement.

To add some more detail, I've scattered some pebbles from Megascans on the soil. I was able to instance the pots and soil 12 times to reduce their memory footprint while rendering. A repetition wasn’t visible because most of it is hidden by the plants. The pebbles, on the other hand, are random for each pot and vary in position, size, rotation, and number of stones.

Advantages of the Workflow

The great advantage of doing everything procedurally is the ability to make changes rather quickly. I was able to try a lot of different plant-shapes. I even tested Houdini’s PDG to create many variations and get the idea of what my hero-plants are supposed to look like.

With a procedural dependency graph (PDG), I created many wedges with different attributes for the plant-generation. Each plant is rendered with OpenGL in a tiny resolution. After all the plants are rendered, they are automatically combined into one huge image.

Every one of the 12 final plants is based on the same setup with just some different parameters. The only plant which took extra work is the one with the webbing (see below). 

Dealing with Lines Intersecting Geometry

I first tried to use the "connect adjacent pieces" Node but this Node has a huge problem. The created lines are unrealistically intersecting with the plant geometry. To avoid this behavior, I created a new tool that checks if a potential connection would intersect with another geometry before adding the lines.

After creating the lines, I curve them to make them less perfectly straight while making sure I don’t add many new intersections.

The building of this tool was rather simple because I was able to reuse a lot of code from the original node. I added some minor features like deleting points that are intersecting with the geometry because, for those points, each possible line would also intersect with something. Also, that way I don’t create lines that are completely inside geometry. Otherwise, those lines would still be created because, technically, they don’t intersect with the surface of the geometry. 

To check if a connection would intersect with the geometry, I use the “intersect” VEX function to send a ray from the starting position in the direction of another point. If I get an intersection, then I don’t create a line.

Kilian Baur, CG Generalist

Interview conducted by Kirill Tokarev

Keep reading

You may find this article interesting

Join discussion

Comments 1

  • Anonymous user

    Please, share the hip file of this project

    1

    Anonymous user

    ·2 years ago·

You might also like

We need your consent

We use cookies on this website to make your browsing experience better. By using the site you agree to our use of cookies.Learn more