My first encounter with non-destructive workflows was through 3DS Max’s layer stacks, but I truly fell in love when I discovered Substance Designer.
It was then that I decided to pursue a role as a material artist. I seized the opportunity when I was offered the position of Lead Material Artist at Poliigon.
While mentoring other talented material artists was rewarding, I missed producing art myself. So, I transitioned to a role as a Technical Substance Artist, focusing mainly on creating custom nodes to assist the team and improve workflows.
I also had the chance to produce and release numerous assets on the site, including some advanced generators capable of exporting multiple materials from a single file thanks to their array of parameters.
Driven by a constant desire to acquire new skills, I left Poliigon and am now self-employed. I sell a few materials and nodes on Gumroad while taking on occasional freelance work, all the while learning procedural modeling in Houdini.
The material is based on the tile floor in the hallway of my wife’s parents’ house. This type of tile pattern is common in the UK in buildings from the Victorian and Edwardian eras and features a border that follows the edges of the room.
I’ve always admired it and was inspired to recreate it in Substance Designer. The problem I wanted to solve in this case was how to approach the border so that it could be applied to any floor plan without having to create one huge material for the entire floor.
I also enjoy working with patterns and like to explore ways of parameterizing them. In this case, I aimed to make it easy to control the colors.
When I first started using Substance Designer, it immediately clicked for me, and I felt completely at home with it. I was inspired by its power and found myself spotting interesting materials everywhere I went, relishing the challenge of recreating them procedurally. Around that time, there was also an explosion of fantastic material artists to learn from and draw inspiration.
Artists such as Daniel Thiger, Josh Lynch, Eric Wiley, and Vincent Dérozier, to name just a few. I eagerly anticipated their next material studies on ArtStation, which motivated me to seek ideas for my own work.
Each study I undertook was an opportunity to push my skills further and gain a deeper understanding of what the software could do. I constantly challenged myself to identify problems and create custom nodes to solve them.
dedicated a lot of time to mastering the most powerful aspects of Substance Designer: function graphs, the FX-map, and the Pixel Processor, all of which are now integral to my workflow.
The first step was to gather reference material. In this case, I was fortunate to have access to the actual material, which was a significant advantage.
It allowed me to capture the exact shots needed to answer the questions I knew I’d have later on. It’s best to obtain a mix of close-up and mid to far-range shots and include shots of all unique parts, leaving as little to the imagination as possible when creating the material.
It’s also crucial to capture a few low-angle shots to record specular reflections, providing a better understanding of how the material interacts with light. This is invaluable for creating a realistic roughness/glossiness map, a critical element in making the material appear more authentic.
I also took measurements, as part of my workflow involves working with real-world measurements and ensuring everything is accurately scaled. To achieve this, I make all sizes relative to a ‘global texture scale’ value. This allows me to change the scale of the entire material while ensuring that all detail frequencies scale accordingly, providing a high degree of control over the final texel density.
Next, I examined the pattern to determine the best approach for recreating it and to identify the nodes I’d need to create. I realized that all the tile shapes could be categorized into three major shape types: rectangles, right triangles, and trapezoids.
So, I created three tile shape nodes that took the bounding box size and the global texture scale into account to ensure accurate and consistent measurements.
These nodes generated data maps for each uniquely sized tile, including height, edge falloff masks, and opacity, with each packed into the channels of a color map.
They also provided local centroid positions and a data map containing corner positions and minimum edge length, which would be used later to create cracks and chips on the tiles’ corners.
I also needed to create two emblem designs. For one of them, I used the spline nodes to create many of the shapes, combined with some simple circles. For the other, it made more sense to draw the shape manually with the SVG node.
In both cases, I created the minimum required parts of the emblem and used a Pixel Processor to mirror and replicate that segment to complete the design.
I then proceeded to work on the tile pattern itself. To address the border issue, I decided to split the pattern into two materials: a standard repeating material for the main section and a trim sheet containing the parts needed to construct the border by UV mapping the geometry to the correct part of the texture.
Apart from the differences in the layouts of the two materials, I aimed to keep the surface material itself and the grout consistent across both. I didn’t want to manage two identical graphs except for the initial tile layout.
So, I divided the structure into two separate graphs containing all the data outputs required for mapping colors and details to the main and trim sheet tile layouts. I also included a graph that could take those outputs and generate a realistic tile material from whichever tile data node was connected.
To create the tile patterns and their data, I utilized numerous functions and Pixel Processors. I designed functions to create local UVs and bounding box sizes for the three different tile shapes, as well as additional functions to assist in copying, moving, and rotating the tiles into place.
I also tracked each tile’s individual index, shape index, and color index, in addition to the total tile count.
A useful tip is that you can effectively create more than one output for a function by using the set node to establish a variable name, the sequence node to control the evaluation order, and another sequence node outside the function with the function’s output fed into the sequence node’s “in” input, with a set node referencing the desired variable fed into the “last” input.
Channel packing is also highly valuable, and I frequently use it when creating data maps like this. This allows you to store up to four maps in each color texture.
I repeated these processes for the main pattern, the border edges, and the border corners. Then, I created the two tile data graphs required for generating the final material.
Each graph included the same outputs: Tiles Base Color, Emblem Colors, Tile Data 0 (UVs, bounding box sizes), Tile Data 1 (indices and rotations), Tile Data 2 (height, edge falloff maps, and opacity), Centroids, Corner Data (positions, minimum edge lengths, corner count, stored as an array), Color ID, global texture scale, and height depth in centimeters.
Tile Surface Base Material
Subsequently, I designed a material graph called Tile Surface, which described the surface of the tiles. The base color remained gray, but with subtle variations in hue, saturation, and lightness.
This base color could then be blended using add/subtract operations over the tile colors. I created most of the details for the height map using a custom node called MT Voronoi Plus, which functions similarly to a 3D Voronoi Fractal but also works in 2D with additional options and outputs.
This node was beneficial for achieving the “salt and pepper” look of the fine stones in the clay, as well as for creating small pits, holes, and general surface imperfections. This material would later be mapped to the tile surfaces in the main material graph.
The Tile Maker
Finally, I was ready for the enjoyable task of creating the final material from all these maps and data! It’s important to note that when working on a material generated from inputs, the inputs do not include any information within the graph itself. This means the material won’t appear correct and will be challenging to work on.
You can enable graph editing in context in the preferences and right-click an instance of the graph with its inputs set up, selecting “Open Reference In Context” (or Ctrl+E), although I have found this method to be somewhat unreliable.
Personally, I just instanced one of the tile data nodes into the Tile Maker graph, shift-dragged the wires from the inputs to the outputs of the data node, worked on my material, and then swapped the wires back to the inputs (don’t forget this step!).
For me, most of the material work usually involves building the height map, and I make heavy use of a custom node I created for height blending. This node contains numerous options for different types of height blends and has become a significant part of my workflow.
I chained multiple instances of this node to add edge wear, surface details (from the Tile Surface subgraph), scuff marks, an edge-curling effect, a subtle stone effect, and cracks.
Then, it was time to use the corner data from earlier to generate the corner cracks and chips. To do this, I created a separate node that loops through the local corner positions for all tiles and calculates the distance to the local UVs, creating a cone-like shape on all the corners.
These shapes are then assigned random scales and depths, and some fractal noise and tile surface noise are added to make them appear natural.
A slider controls the number of included cracks, and another slider determines how many of the included chips are transformed into cracks. Finally, some random gradients are introduced to give the tiles random tilts, and the tile heights are adjusted.
The grout height is generated from the tile heights. Essentially, I run the pre-tilted tile heights through a histogram scan to create a mask. I use an edge detect operation to expand the grout parts by a pixel, and then I employ this mask with a distance node to distribute the pre-existing height from cracks and chips into the grout areas.
The process is smoothed with a few blurs, and bumpy imperfections, cracks, chips, and a rounded, recessed profile are added. Additionally, some surface details from the Tile Surface are incorporated with an increased intensity for a rougher appearance.
The tiles and grout are then blended together using max blending, and a grout mask is generated from the areas where the grout is higher than the tiles.
The rest of the process is straightforward. The normals and ambient occlusion are generated from the final height map. The colors and roughness already have a good starting point.
The tile colors have already been generated by the tile data nodes, and the tile surface material provides some details to mix in, serving as a solid foundation for the roughness.
The remaining adjustments involve blending in variations from many of the details created throughout the height-building phase into both the base color and roughness.
One significant aspect that had a substantial impact on improving the appearance of the color and roughness maps was a clay grunge effect. This is a method I frequently use to create these grungy details that appear to respond to surface variations.
The process involves starting with a fractal noise, passing it through a Non-Uniform Directional Warp node that utilizes maps such as the height map (or maps generated from the height map, like curvature) for its intensity and warp angle inputs.
I then adjust the parameters until it looks satisfactory. Some additional processing was performed afterward, but this initial step is critical for creating interesting details.
Finally, I reconnected the inputs and created the final graph, which contained the two tile data nodes, a series of switches connected to an Output Mode parameter for switching between the main section and the trim sheet, and all these switches were connected to the Tile Maker node, with its outputs routed to the final graph’s outputs. I completed the process by promoting any parameters I needed from the tile data nodes and the Tile Maker node.
Lighting & Rendering
For the final renders, I swiftly constructed a straightforward floorplan mesh, skirting, and walls in Houdini. I included a few doorways and a window to enable outdoor lighting to enter the scene.
The floor plan had to be modeled in a specific way to ensure the material could be correctly mapped to the geometry. To achieve this, I created a grid plane divided into exact squares, each of the same size as one of the corner sections of the border, based on my earlier measurements. I selected and removed the faces that were not part of the floor plan.
I then mapped all the faces along the edges and corners to the appropriate sections of the trim sheet, ensuring they were properly oriented. To create the skirting, I drew a Bezier curve skirting profile and used Convert Line to extract the skirting path from the boundary edge selection of the floorplan mesh.
I used both the floorplan and the result of Convert Line to create the walls, employing a few PolyExtrudes and PolyBevels.
Then I imported this scene into Marmoset Toolbag 4, set up and applied the materials, and began setting up the lighting. My intention was to create a mixture of warm indoor and cool outdoor lighting and to use the comparative brightness of the outdoor light to create some nice specular highlights to show off the details in the roughness map.
For the skylight, I chose an HDRI with plenty of bright sunshine, buildings, and a few trees. Basically, a street scene, as this made sense in the context of the scene and wouldn’t contain any colors or lighting that wouldn’t fit the scene.
I placed a child light where the sun is, then adjusted its diameter under the Area/Shape tab until I was happy with the softness of the shadows.
Then I placed a few indoor lights to brighten up the dark areas. One spotlight at the opposite end of the hallway from the front door, and one omni light that I imagined was a lamp sitting on a small table in the corner.
Again, I set their diameters to achieve the desired shadow softness, then enabled the Temperature tick box under the Light tab and adjusted the °Kelvin to values in the warm range, as this gives the impression of them being a man-made light source. I used 3000° (orangey) for the lamp light and 5125° (pale yellowish) for the spotlight.
I created a few cameras and placed them around the scene, looking for shots that nicely showcased the material properties and illustrated the different parts of the border pattern.
Low-angle shots with a bright light source worked best for highlighting the specular details. During this process, I continually tweaked the light values and camera settings, trying to find the right balance.
I opted for ACES tone mapping and adjusted the settings until I got the look I wanted. The specific settings were different for each camera, but I generally started by finding the right exposure, then adjusted dark and light tones to create a good amount of contrast and clarity of details and then adjusted the midtones last.
Any adjustments that increased contrast also tended to increase saturation, so I slightly reduced that too to compensate.
The final touches for the camera settings were to add some subtle sharpening. This can help bring out some of the finer details, but it’s best not to go overboard with it.
Less is more. Other things that can really add to the realism are depth of field and film grain. For the grain, I chose the film mode and left it at its default.
I used Ray Traced for the depth of field method and used the focus tool (next to the Focus Distance slider) to pick the area of interest that I most wanted to be in focus. Then I adjusted the focal length slider from side to side until I liked the result.
The key here is to create enough of a sense of depth without over-blurring and losing too many of the details you worked so hard to create. It’s a very nice effect, so it’s easy to get carried away, but remember that it should enhance your work, not cover it up.
For rendering, I used ray tracing, enabled Advanced Light Sampling, and increased the number of bounces to 7 to spread the outdoor light as far into the scene as possible. There were no transmissive materials in the scene, so I set Transmission to 0.
I was getting a few fireflies, so I reduced both the direct and indirect radiance clamp values a bit and increased Rays Per Pixel to 2. I output at 4K resolution with 1024 samples.
The higher sample count increased the render time significantly, but I thought it was worth it for the extra quality. And that’s it!
Advice to readers
To anyone starting out learning Substance Designer, my advice is to have fun with it! It can be a very enjoyable process full of interesting and fun challenges, and if you enjoy it, that tends to come through in the final material.
Keep practicing and experimenting, and don’t be disheartened if something you try doesn’t work out because it’s all part of the learning process. Look more closely at the world around you because we’re surrounded by materials, and you’ll be surprised how much inspiration you can draw from even the most mundane things.
Be ready to whip out your camera or phone and take pictures because you never know if those tiles in the toilets at your local pub may be your next work of art! (Bad example. Please don’t go around taking photos in public toilets on my advice!) Follow the work of others. Learn from them, let them inspire you, but don’t try to emulate them completely.
Your process should be your own, and there’s no substitute for experimentation and learning from your own mistakes. The fact that you’re reading this article shows you have a hunger to learn from others, and that will serve you well.
I know that my process won’t be for everyone, but it’s mine, and I love it! A big thank you to Games Artist for asking me to share my process, and thanks for reading.
I hope that you were able to learn something useful from it and are feeling inspired to explore your technical side.