About / Context:
This is an environment I created for my Houdini 1 class taught by David Stripinis. There were a few requirements:
-
Use a heightfield
-
Generate at least two assets in houdini
-
volume made in houdini
-
procedural shading of some sort
I wanted to challenge myself so I wanted to make as much as I could entirely in Houdini. Everything excepting the tent, backpack, and tentpoles are entirely procedurally generated and shaded in Houdini and Redshift. This was also my first time touching Houdini.
Terrain
The terrain is all normal Houdini Heightfield stuff. Just various noises with a few erosion simulations. The little craters in the foreground are dirt skirts I created using the proximity of the terrain points to the surface of the rocks I have scattered around.
The water is just normal ocean spectrum stuff with the wind up a little bit for the animation, I scattered a lot of wave instancing points to try to achieve the scale I was going for.
Trees
My Trees are made entirely procedurally and took a few attempts to get right. The trees are 20 branch variations instanced onto a whole bunch of tree trunks scattered all over the place. The branches are generated through three iterations of for-loops which randomly split the branch into smaller and smaller subbranches and scatter pine needles all over it when done. (I later found out this is just an L system and there are native Houdini tools to do similar effects). I build all of this custom instead of using the LABS tree tools because I was trying to take advantage of nested instances to drastically reduce my memory footprint, unfortunate it turns out redshift (What I rendered in) does not support nested instances hence why I instance the tree branches and not the entire trees. Doing it this way also allows me to control number of branches per tree.
The different colors correspond to different values I use when shading the trees. Red is just a random value per tree so I can offset the hue and saturation on a tree by tree basis. Blue is for my "deadness Mask" which controls how yellow, rough, and sparse the trees are (this last one is not controlled in shading but by how many branches I instance to each trunk).
The "deadness mask" and other scattering masks are all created logically from the heightfield, for example: trees will be more dead where water will not flow and even more dead in places that would receive less sunlight. I did all of this logic procedurally for example by projecting an attribute along a vector to simulate sunlight.
Rocks
I have Three different rock variations: Large smooth rocks, Medium jagged rocks, Small smooth pebbles. These variations were all created on my custom rock generator just using various noises and voronoi fracturing for the more geometric rocks.
The Rocks also scatter following some basic logic. Steeper places are more likely to have a rock field, pebbles will surround larger rocks, etc. I didn't scatter rocks under the water as any visual gains were extremely miniscule.
Grass
Grass was much more difficult to make than I thought it would be. It turns out it isnt so simple as scattering a bunch of points and instancing stalks with random rotation to them.
The solution I came up with was to use a "clump by nearest point" approach. Basically I scatter a whole bunch of points and then scatter a smaller number of points on top of that I have each point query which "clump point" they are closest to and then take a distance from it. Then using all of the other "clump homies" normalize that distance and use it to lerp between an upward vector and an outward vector. Throw in a little bit of noise and I think the effect is pretty convincing! I realize this is just voronoi noise with extra steps but its cool to kind of directly see why voronoi noise is so important. I've attached some of my vex snippets I wrote for this below.
As for scattering, the grass has all the same logic as the trees plus it wont scatter within a certain distance of rocks. It will also point slightly away from the rocks as to not have them clip into them while animating.
Bushes
I have 5 bush variations generated from my custom bush generator. It works very similar to my tree branch generator though as I made it later I was able to build it much more quickly and it computes much quicker. The real key for the efficiency upgrade was using instance attributes instead of for each loops to modify each component.
I'm proud of my bush generator, I made a UI for it using the Houdini parameter interface and put time into making it easy to use. My generator is technically capable to instance any "three layer deep" foliage though I've only tried it with the bush pieces I've made procedurally. When you enable animation it will also show you a simplified view to speed up viewport playback which I think is pretty nifty.
Random Rotation was harder to implement then I thought it would be. I wanted my bushes to rotate around the normal vector inherited from the terrain, this sparked 5 weeks of linear algebra learning. But this is what I came up with: I rotate around the normal by creating a rotation matrix with a random rotation encoded in it and then converting that rotation matrix to a quaternion which I then plug into the orient attribute which is expecting a vector 4. I use this technique all throughout my project for randomizing rotation of things.
Animation
This project wasn't originally going to have any motion in it at all, one of my roommates (shoutout Gabe) said it would be really cool if things moved and I couldn't disagree. Because I was implementing animation after the assets were made (or at least their generators) I needed to work around some of the work I already had done.
Grass: Animating the grass was pretty strait forward. In my scene I have a global attribute on a null for wind direction. Basically to animate the grass I run a curl noise through it and the stronger the noise the the normal points in the direction of the wind.
Water: I already talked about this but I basically just turned up the wind in the solver and that's it.
Trees: This was a bit of a challenge because the branches don't actually adhere to the trunks. So I needed to deform the branches and trunks together as a unit. I did this by averaging each "row" of the trunk mesh (its just a sweep) to get kind of a line that represents my tree. I then deform this line and do all my transformations relative to that. As for the mechanics of the wind it just functions the same way as the grass.
Bushes: This is where I "cheated" the most. I wanted my bush generator to be usable outside of this project so in the controller for it I built in animation controls. Basically each branch component moves in a sinusoidal pattern with some noise thrown in lerping between its normal rest position and the wind direction. I also have controls for how much the leaves should move.
Tent/Assets: The tent and the backpack were modeled in marvelous designer (it was just like pitching a real tent!) so to animate it I just added a wind force and then exported an alembic.