Procedural Tree Generator Series – Houdini + Unity – Part-1

Last Updated by

Brief

The aim of this series is to create a modular and procedural tree generator tool that can be used with Unity Game Engine with the help of Houdini Engine.
In this part we will be covering below points:

  1. Simple Tree Trunk Creation.
  2. Scattering First Level Branch Points on the Trunk.
  3. Creating Branches and Randomizing Branch Properties.
  4. Creating Trunk and Branch Geometry.
END RESULT of this Part-1

Simple Tree Trunk Creation

  • Using Line Polygon to structure the trunk of the tree.
  • Using “Attribute Create” SOP Node to create three attributes “width”, “gradient” and “level”.
  • Creating an “Attribute Wrangle” VOP SOP Node to create a width ramp for structuring the tree using VEX Expression. For testing purposes we make the trunk width wider from starting position and narrower to end position.
  • Then we output this to a “null” node and marking it as “OUT_Trunk”.
//VEX Expression for "width_ramp" attribute wrangle node.
 float rmp = chramp("width_ramp", @grad);
 @width *= rmp;

Scattering First Level Branch Points

  • Using the above “OUT_TRUNK” as a group input using a “Group Create” node.
  • Promoting the “level” attribute with “First Match”method using “Attribute Promote” node.
  • Creating an “Attribute Wrangle” node to scatter the positions for first level branches with some basic step-based randomization and name it “scatter_points”.
  • Then we delete the group input by using “Blast” SOP Node.
  • Using Polar Coordinates to determine the rotation of the branch with some seed randomization using an “Attribute Wrangle” node named “set_branch_rotation”.
// VEX Expression Code for "scatter_points" attribute wrangle node above.
// Creating a Randomization Seed UI Input.
int seed = chi("seed");
// Creating a Step based UI Input, so that the problem of points not generating on the top points of the trunk doesn't arise.
int steps = chi("steps");
// Used for randomizing number of branches per each step.
int branches_per_step = chi("branches_per_step");
int rand_distrib_const = 149;
// Min. U position along the spline.
float min_u = ch("min_u");
// Max. U Position along the spline.
float max_u = ch("max_u");
// Used for offsetting Min. U and Max. U positions along the spline.
float random_offset = ch("random_offset");
// We don't want each step having similar no. of branches, hence creating a SKIP CHANCE input to determine how many branch positions to skip during each step generation with some randomization.
float skip_chance = ch("skip_chance");
int skip_chance_rand_const = 912;

int current_pt;
vector current_pos;
float current_grad, current_width;

// No. of branches using step randomization.
int branches = steps * branches_per_step;

// A single step is determined by delta position of MaxU and MinU by no. of steps.
float step = (max_u - min_u) / steps;
// Caching the current step.
float current_step = min_u + step / 2;

// Looping through each branch per step and increment the current step with step randomizer. Ignoring the first branch step position always.
for(int i = 0; i < branches; i++) {
        if(i != 0 && i % branches_per_step == 0)
                current_step += step;

    // Creating more randomness between steps so that each step dowsn't have same number of branches.
    if(rand(@primnum + i + skip_chance_rand_const + seed) < skip_chance)
        continue;

    float u = current_step + fit01(rand(@primnum + i + seed + rand_distrib_const), -random_offset, random_offset);

    prim_attribute(@OpInput1, current_pos, "P", @primnum, u, 0.0);
    prim_attribute(@OpInput1, current_grad, "grad", @primnum, u, 0.0);
    prim_attribute(@OpInput1, current_width, "width", @primnum, u, 0.0);
    current_pt = addpoint(geoself(), current_pos);
    setpointattrib(geoself(), "grad", current_pt, current_grad);
    setpointattrib(geoself(), "width", current_pt, current_width);
    setpointattrib(geoself(), "level", current_pt, [email protected] + 1);
}
“scatter_points” Attribute Wrangle UI generated using above VEX Code.
“set_branch_rotation” Attribute Wrangle Node VEX Code and UI.

Output of First Level Branch Position Scattering

Output for ” Scattering First Level Branch Points” Step.

Creating Branches and Randomizing Branch Properties

  • Using the exact same process of “Simple Tree Trunk Creation” Step, we create “branch_width_ramp” and “branch_gradient”.
  • Using “CopyStamp” SOP Tool to copy the points from “level” and “width” attributes created earlier. The inputs for this nodes are the “branch_width_stamp” and “set_branch_rotation” wranglers. Below is the output generated by this step, where from each determined branch point a new branch is created with variable width and length.
Creating Branch Splines using Branch Scattered Points Created Earlier using “CopyStamp” Tool.
  • We also promote the “level” attribute to be used in the next Noise VOP Network.
  • As all branch splines are exactly the same we needed some noise generation to randomize them. For this we used “Attribute VOP Network” geometry node. We use “Anti-Aliasing Noise”, “Noise Stamp” a “Noise Seed” and some other randomization by using geometry position and primitive numbers present in the geometry to output a “branch_noise”.
Creating a “branch_noise” Atribute VOP Network using VEX Builder system.
  • Next we delete the “branch_width” point and primitive attribute to optimize the generation of branch splines.
  • We then merge this branch noise generation with “OUT_TRUNK” output created earlier to attach the branch splines to trunk spline.

Output of Creating Branches and Randomizing Branch Properties


Output of “Creating Branches and Randomizing Branch Properties” step.

Creating Trunk and Branch Geometry

  • As we now have trunk and branch splines, its easy to create geometry for trunk and branches. We use “PolyWire” SOP Node which creates a tub-like geometry from curves and points as inputs.
  • Once we link the “wire radius” property of the PolyWire node with our “width” attribute, it automatically creates the require polygon geometry.
Output of the PolyWire SOP Node.

What’s next?

  • Next we will try to add more randomization to the branches by adding gravity and extending it to be more dynamic.
  • Creating structure to prevent the excessive stretching of branches due to gravity.
  • Using Spherical Coordinates and Growth Orientation changes we will restructure this Procedural Asset to make it more iterative.
  • See you in the Part-2 post for this series.