A while back I posted an update about my Tenochtitlan project:
I’m showing the way I am generating a procedural infinite forest in a shader.
The underlying concept is what I’m calling view-aligned islands. Essentially,
I’m generating little UV islands which always look at the camera, but also stay anchored in their centers.
Let’s Begin
It starts with a voronoi node. This creates the cells as a basis for the islands.
A vector pointing towards the center of each island is created using this Vector Transform trick:
The node transforms (0,0,0) from camera space into world space, which gives me the position of the camera in the world.
Using a couple of cross products, I can create two important vectors:
- One pointing to the right as seen from the camera
- One pointing up, but along the camera’s plane
Note the first cross product with (0,0,1); this determines the up direction of the trees.
Next, we can use dot products to create two UV coordinates: One based on the actual world position, the other on the center of each voronoi island:
This is ok as is, but we need to correct a perspective mistake.
First, we divide both UV coordinates based on the original camera vector lengths:
Then, we scale it back up based just on the voronoi island camera vector length:
Result of step 1:
And we’re done here! Now we have little UV islands that face the camera:
We can use this coordinate to sample a texture:
Blending the borders:
If you find the harsh island borders off-putting, we can use a white noise texture to “blur” the borders.
This jitters each sample by a little bit, and when multiple render samples, the result should be a blur:
Looks great in Cycles, not so much in Eevee:
The borders are all mushy and grey. This is the result of mip-mapping. The noisy offsets trick Eevee into thinking the texture is very far away. Choose “smart” texture filtering, and the problem goes away:
Wrapping up:
So, these are view-aligned voronoi islands.
For my trees, I generated one big texture with trees, in various passes, like this:
Based on the desired tree density at any point I shift the UV a bit to the right.
I also have a random-per-tree texture, which I use to randomly colour my trees.
Additional tips:
The voronoi node has a “color” output, which returns a random color per island.
This is in turn 3 random values, in R G B.
You can use these values to randomly rotate, shift, and scale each island.
Combine this random color with a random color per tree to break up the tiling of the tree texture.
If you generate a normalmap from your trees, you can use the generated vector to construct a worldspace normal map. This helps with lighting.
Pro’s and Con’s
The cool thing about this is that the render cost is flat - the same for every pixel, not matter the size of your forest. This also means that iteration is super fast, much faster than using something like individual trees or even billboards.
The downside of course is that it is a hack; There’s no acutal volume, no correct shadows, no silhouetting.
.BLEND
Have fun and try for yourself:
ViewAlignedVoronoiIslands.blend (1.1 MB)