Good but the Pattern of the foam doesn't change, very disturbing.
Dan Robson shared an explicit breakdown of his complex procedural material made with Substance Designer. A must-read for anyone who wants to master SD!
My name is Dan Robson and I’m currently a senior environment artist at Machinegames in Uppsala, Sweden. I’ve been working in and around the games industry for almost 10 years now. And whilst I have always worked in environment art, by far my favorite part of the job is the texture and material creation.
When it comes to my personal work, I’ve made a point of trying to advance my knowledge of Substance Designer by creating some more complex, unusual and challenging surfaces. This tends to have taken the form of layered surfaces or substances with a degree of depth to them.
I, of course, always intend to have a pleasing result at the end of each project, but they are first and foremost learning exercises – pushing how far I can take a material without relying on fancy shaders or additional geometry.
The aim for my Aquatic Plants substance was to see how far I could push the depth of a material using only a flat plane with a basic shader setup. Since a large portion of the material would be underwater and would rely solely on the diffuse to give the illusion of 3D geometry, I would have to employ a number of techniques to fake this depth.
My first step is always to collect a large cross-section of the reference material. Once I have this I’ll identify specific images which I feel are the most pleasing examples of the subject’s key characteristics. It’s a good idea to always have these on hand to refer back to.
In this case, I knew I wanted lilies that were relatively flat since this would be a tessellated flat plane relying on a height map for displacement. I also looked for reference that had some interesting underwater details that I knew I would be able to recreate. Additionally, I found some nice examples of features that broke the surface of the water. This would give further support to the underwater details and help sell them as having volume and shape.
Once I had an understanding of the material and what features it would entail, I began to think about how I would like to setup the substance graph and how I would achieve the various features I had identified and wanted to include. Since this was going to be quite a complex substance I decided to split the material into more than one graph. I would create a separate graph for the Water Lilies, a separate graph for the Pond Debris and then finally the parent graph where I would combine everything and create the final material.
Graph 01: Water Lilies
Since they are the main feature in this substance, I began with the actual water lilies. I knew that there would be quite a few of these scattered around the final material and there needed to be variation in their shape, size, and features, so I would need to approach this in a way that would allow me to adjust these features per-instance in the parent graph. For now, let’s look at creating the shapes for the lily. We’ll only be looking at creating the heightmap here, as the other channels will all be addressed in the parent graph.
We can start creating the lily pad using a basic circle shape and subtracting a splatter circular node for the cut-out triangle shape. This gives us the basic Pacman silhouette that we need.
The reason I’m using the splatter circular for this is that it allows us to change the spread of the cut-out shape which is one of the details that I wanted to adjust in the parent graph later. Exposing the splatter circulars ‘spread’ parameter allows us to do this.
In addition to this, I also subtract the radial gradient, which sinks the center of the lily and I use a curve node to adjust the profile of this gradient.
Next, I start adding some surface details to the lily pad. To do this I’ve again relied on a number of splatter circular nodes to get the radial patterns on the lily pads.
Large, medium and small details are progressively layered on top of each other.
You might notice that I frequently use directional warp nodes which are driven by existing height information in the graphs (in this case I’ve warped some of the smaller details using larger height details). This helps these details adhere to the larger shapes on the leaf and makes the noises look less procedural and rigid.
Adding some more surface details. This includes the larger crease or fold into the center of the lily pad using a basic linear gradient. Again I rely on the curve node which allows us to adjust the profile of the gradient.
I wanted the edges of the lily pad to be slightly raised, however, I didn’t want it to be a consistent height all the way around. Non-uniform blurring the basic leaf shape is a great way to achieve this effect as it gives us an uneven edge with some of the blacks cutting into the lily pad. If we then use the ‘divide’ blending mode, it essentially turns these blacks into whites and then subtly adds them onto the background layer, giving us an uneven, higher edge around the lily.
The ref also often shows some larger, radial ‘folds’ around the edges. In order to get these larger raised folds, I use a starburst node and masked the center so it only affects the outer parts of the lily pad.
The final touch is adding some more subtle veins to the surface, the cells 1 node works great for creating the veiny patterns you see on leaves and vegetation. Here we multiply some smaller scale details which I’ve masked with the base shape from earlier.
As I mentioned earlier there are certain characteristics of the lily that I would like to be able to change in the parent graph. However, in order for this to work effectively, I need to make sure a number of different nodes and parameters are updated together, in sync. E.g. I want to distort the base shape of the leaf, but the surface details will also need to distort along with it.
Here is a great Allegorithmic’s video that goes through this process:
I’ve used this method to adjust the center point of the lily pads:
This gradient node is used to drive various directional warps in the graph. The warp intensities on the directional warps are then linked to one input tweak so they all update together. This shifts the center point of the leaf along with all associated details.
The ‘Spread’ of the cutout arc is also linked to its own input tweak.
I am outputting a height map and also an alpha which will allow me to easily mask the leaves later in the parent graph. I do the same in the following Water Debris graph.
Final Lily heightmap:
Graph 02: Water Debris
This ‘water debris’ graph is where I create the small leaves that will be scattered around and on top of the lilies. Since they will be so small it’s not really necessary to go too heavy with detail.
Again, I start by creating some basic shapes and blending them together to create my leaf shape, before multiplying a gradient to give the impression of an angle on the leaf.
Once we have the basic leaf heightmap, we can then use the splatter circular node to create a larger ‘flower’ of leaves. This node is great for creating any kind of radial pattern, but the offset and randomize options allow you to create piles and clusters of objects as well. It’s worth playing around with the nodes different parameters to see what kind of results you can get.
I output a large cluster which will be used for the majority of the leaf debris, but I also create a small group which I output to scatter around in the parent graph as ‘loose debris’. Some of the flowers clip through each other but since these details will be so small in the final material it won’t really be noticeable. I use the Splatter Circulars ‘Luminance Random’ parameter to add some depth to the different leaves.
Like in the lily pad graph, I create a height output along with an alpha output which allows us to easily mask these details in the parent graph.
Final Pond debris heightmap:
Now we have the two main elements that will allow us to get started on the main graph. Like always I start with the height map and drag in the lily pads graph.
I have created 3 base lily pads and given each instance different parameter settings which helps make each look unique. The instance parameters I am changing are the ones we exposed in the lily pad graph earlier.
Next, we use tile sampler nodes to scatter the 3 different lilies using various position and scale parameters to get a suitable result. There is one for the height map and one for the mask – both sharing the same settings to make sure they’re in sync. I have used the ‘color random’ parameter on the height to give the pattern some height variation – this helps stop the lilies clipping through each other when using the ‘max’ blending mode as brighter pixels will mask any darker pixels that happen to be in the same place. There was no real trick to getting a good result here – I used a Perlin noise as a displacement map, then it was a case of playing with the position settings until the clipping was the least noticeable and gave a pleasing result.
Most of the reference collected prior to starting the project showed the lily pads as being damaged in some way or another – either by weathering or being eaten by insects etc, so this mask was created to multiply over the base lily pads and give that effect. I could have put this detail into the lily pad graph, but the detail would then become repetitive since it would be the same in every instance. The mask is a mix between basic cuts (scratches generator) and some bigger gouges around the edge of the lily pads. I use an edge detect node to mask these gouges so they only affect the edge of the lilys.
Here you can see the difference in height maps with and without the scratches:
To give the impression of water tension around the edges of the lily pads we want to add some subtle height information around the lilys. To do this I’ve blurred the lily mask and then subtly added it on top of the existing heightmap. If we mask away the lilies when we do this it ensures the raised edges only affect the water.
The last detail I wanted to add to the lily height map is the small pools of water that accumulate on the lilies. To do this I’ve isolated a specific lower range in the heightmap as these will be the typical areas water would collect – in the crevices etc. Once we have isolated the areas of the heightmap where we want the pools to be, we can soften the mask with various blurs, before using a histogram scan to tighten up the edges. The mask comes in handy for the diffuse and gloss maps.
Final lilies heightmap:
Once we have the heightmap for the lily we can begin to use that information to create a diffuse for it:
A gradient map is a nice way to get some simple base colours for your diffuse based on your height map information. A good idea is to colour pick from your reference material. I usually keep these colours simple and then add finer detail later.
Looking at my reference for the lilies, the worn or dead parts of the leaf have a yellowish tint, and the areas in between seem to take on a maroon type of color. To achieve this I make two masks which pick out the areas where we want these colors to be.
We start the yellow edge mask by isolating specific parts of the height map using histogram select nodes and blending them together. Since we only want this mask to affect the edges of the lily we use an edge detect node to identify and mask away the center of the lilies. The slope blur softens these shapes and we can then break them up further by multiplying some noise over the top.
The second mask is the red colored patches on the lily. Generally, these cling around the edges and between the yellow and green parts of the lily. To find the edges of the lilies I create a curvature map which I modify with a levels node. To add the outlines around the yellow patches I use an ambient occlusion node on the yellow edge mask which casts a dark ring around the inside of them. I invert this before adding it to the mask, then add some more spots and scratches.
The final step for the lily diffuse is darkening the water patches slightly using the mask I created earlier.
Base Water Diffuse
Although a lot of the water detail will be obscured by reflections, it’s still important to try and add a convincing level of depth in the waters base color.
In my reference material there was usually some very subtle hint of underwater vegetation, so to try and recreate this I simply used the lily pad .sbs with a tile sampler again, blurred it to give some depth and gave it some different base colors.
Once we have this underwater vegetation we add it to a dark uniform color node (which will be our base color for the water) before layering up silt of varying granularity. A non-uniform blur is used to randomly blur the silt, giving the impression of it sitting at different levels of depth.
Now we can make use of the Pond Debris graph that we created earlier. This detail will be loose debris that has fallen from trees, etc. and has settled in the water and on top of the lily pads. The debris in the water will only have the top of the leaves exposed so it will only be these parts that have height information – the rest will be purely in the diffuse.
Our pond debris graph gives us our base cluster of leaves – for the pond debris we use a splatter circular to create a larger cluster before scattering these with a tile sampler node. Again, using a gradient map for the base colors gives us a starting point – here I basically wanted to get a variation between a base green and some lighter specks to break it up, so worked into the gradient map until I had this effect.
Usually, my last step when creating the diffuse in a graph is to add curvature and ao, however since these nodes rely on height and normal information (and these details will have none since they’re primarily underwater) I add the curvature and ao into the pond debris diffuse now.
I set up more debris in the lily debris diffuse frame, this is specifically debris that will sit on top of the lily pads so it is masked accordingly. The diffuse here is slightly brighter since it doesn’t sit under the water.
Lastly, I use the ‘individual leaf’ output on the pond debris graph, to scatter some smaller loose debris.
Additionally, I’ve added some other simple details – Twigs on the lilies and in the water and some smaller grit/specks on the lilies. All these details are blended with the existing diffuse.
We can now go back and update the height map with the new debris details we have created.
We will start with the debris in the water – the exposed pond debris. For this, we use the histogram select node to isolate the range which we want to appear in the heightmap. This will be the part of the debris that appears to be exposed – i.e. not underwater. We also add a blurred version of the exposed debris which gives us the water tension around it – similar to what we did with the lily pads earlier.
The remaining debris sits on top of the lily pads – the lily debris height. Here we take the various remaining lily debris (twigs, etc.) that we created for the diffuse and add it to the lily heightmap. This will be the debris that sits specifically on the lilies and not in the water.
The final major detail in both the diffuse and the heightmap was to add the long reeds that are visible beneath the surface of the water. These are primarily underwater but I decided to add a couple of points where the reeds break the surface.
As with the twigs, the scratches generator node proved to be very useful for creating the reeds. For the underwater reeds, I add two different color variations, with the lily pads masked away.
The exposed reeds use the same setup only they are fewer and slightly sharper than those below the water. The mask is then used below as a heightmap aswell.
Similar to how we created the exposed pond debris heightmap – we do the same with the exposed reeds. using a levels node to isolate the range which we want to be exposed. Since these reeds move under the lily pads we can multiply an ambient occlusion here to give the lily pads a soft ‘buffer’ around them. This ensures the reeds move softly under the lily pads.
AO and Cavity
The last step for the diffuse is to add some subtle cavity and ao information to help pick out details. Making this too strong can have an adverse effect on the texture, sometimes making it look too ‘gamey’ since it’s technically not PBR correct.
Gloss and Spec
Both the gloss and the spec map are relatively simple for this material. The spec, in particular, is very straightforward and uses two slightly different values – one for water and one for the lily pads.
For the gloss map I have essentially started with base values for the water and lily pads then progressively multiplied and subtracted details taken from the diffuse and the heightmap. I found the important thing here is to get the high reflections from the water but identify when to reduce the gloss to pick out certain details, e.g. most of the exposed debris has slightly lower gloss values to make them stand out a little more.
The reflections do a great job of obscuring the detail beneath the surface of the water and is quite important to the success of the material, so it is important this surface is convincing and contains enough detail to imply that there is more going on than it being essentially a flat plane.
As I said at the start of the breakdown, the thing I wanted to take away from this project was successfully giving the illusion of depth under the water and I hope I have given an insight into how I approached this problem. It really came down to having enough detail in the water diffuse, using blurs and varying opacities to give the impression of this depth. The final touch was having certain details break the surface of the water – this helped to reinforce and give form to the 2D underwater information that was solely in the diffuse.
If you have any questions about the breakdown or want to chat then feel free to get in touch online and I’ll get back to you as soon as I can.
Thanks for reading!