logo80lv
Articlesclick_arrow
Talentsclick_arrow
Events
Workshops
Aboutclick_arrow
profile_login
Log in
0
Save
Copy Link
Share

Building a Medieval Encaustic Floor Tile Material Using Substance 3D & UE5

Alisa Prakapenka discussed the workflow behind the Medieval Encaustic Tiles, detailing how she built height maps, grout, pattern shapes, base color, and roughness using modular subgraphs to represent clean, broken, and damaged tile states and show historical wear and aging.

Introduction

Hi everyone, my name is Alisa Prakapenka. My journey started at the age of 11, when I got my first PC and instantly fell in love with video games. Since I've always had a passion for creativity, I decided to study design at university. It was there that I had a revelation: games aren't just meant to be played; you can actually create them yourself. Everything just clicked for me — it was a perfect match. From that point on, I dove into 3D and started experimenting.

Early on, I did a lot of 3D modeling, but I gradually realized that texturing was what excited me the most. Once I opened Substance 3D Designer for the first time, I immediately knew that Material Art was my true calling in CG. I'm constantly observing the world around me, analyzing surface properties, and discovering unique materials in everyday life. Going out to hunt for references and shoot photos for upcoming materials is something I genuinely love.

I kicked off my career as a freelance artist, producing 3D assets for various projects. I also gained experience in 3D visualization, which really helped me level up my skills in lighting, composition, and rendering. One of my most significant commercial milestones was joining the team behind UFL as a full-time Material Artist. Looking back at my journey, the one constant has been my drive to keep learning, experimenting, and mastering new skills.

Medieval Encaustic Tiles

It actually started with something I do every day: building my visual library and exploring my love for material creation. The habit of building a visual library was something I developed during my mentorship with Ishan Verma, where I learned how to train my eyes to better understand textures, materials, and visual details.

As part of that process, I was browsing through a lot of images on Pinterest when I came across some beautiful medieval interiors. I found them particularly striking because of the way they were designed, constructed, and filled with small visual details. At that point, I thought, why not give it a try? It could be another great opportunity to learn and challenge myself.

I kept coming across these small red tiles with intricate white patterns, often with parts of the patterns missing, worn away, or covered in dirt. That detail really caught my attention. I realized I didn't want to create just another clean, perfect material. I wanted to create a floor that felt like a timeline, something that showed its age, history, and the passage of time through every worn edge, broken pattern, and layer of dirt.

Inspirations & References

What really hooked me was learning how encaustic tiles are made. The pattern isn't painted on top; it's white pipe clay inlaid into red clay and then fired. So as the tile ages, the white doesn’t simply fade; it chips, gets undercut, and fills with dirt and mortar dust.

That duality inspired the whole piece. You have sacred, precise geometry – the eight-petal flower, dotted arcs, and interlaced quatrefoil – contrasted with 600 years of footsteps, wear, and decay. I loved that balance between perfect design and very human, messy aging.

I also didn't want to create a single tile and repeat it endlessly. Real churches often have a main field, unusual borders, and completely different patterns alongside each other. That patchwork look in the final render was directly inspired by real church floor plans.

I was very strict about not using fantasy references. Everything had to be from real tiles. The reference collection should contain not many, but instead important, close, and clear images that give the idea of the creation, height, language, colors, and details.

I built my PureRef board as follows:

History

  • Timeline: Flourished primarily from the 13th through the 16th centuries
  • Locations: Found mainly in high-status buildings like royal palaces, abbeys, cathedrals, and manor houses due to their high production cost.
  • Design Purpose: Served as displays of wealth, piety, and lineage using heraldic shields, floral motifs, and geometric patterns.

Storytelling

  • Pattern: I collected only authentic medieval motifs. No Victorian reproductions. I ended up locking onto three that kept appearing together in history – the rosette flower, the dotted concentric bands, and the quatrefoil star.
  • Material Truth: Macro photos of excavated tiles. How the red clay fires unevenly – some tiles more orange, some more brown. How the white inlay is never pure white; it's this dirty bone color, and it's slightly recessed.
  • Time/Damage: This was the biggest board. Wide shots of floors in situ – how mortar floods over the tiles, how cracks always start at the edge of the inlay, how dust completely buries the pattern in corners where no one walks, how the border tiles are always the most destroyed.

Once I had that, I could see the story clearly – this isn't just a texture; it's a floor with centuries of storytelling built into it.

Texel Density

The very first thing I did was set up the texel density, following a standard of 2K per 2 meters to ensure high visual fidelity. This value can easily be lowered if implemented in a large-scale environment for better optimization.

From there, I began creating my textures in Substance 3D Designer. To keep things clean and structured for learning purposes, I avoided packing everything into a single graph; instead, I divided the workflow into three distinct subgraphs representing the clean, broken, and damaged versions. True to that modular approach, I started by building out the height first.

Height Building

  • Step 1: Setting Up the Base Grid. I started by dropping in a Tile Generator node to block out the basic grid pattern of the tiles. Right after that, I added an Edge Detect and a Bevel node to give the tiles some initial shape, bevel out the edges, and create distinct separation lines between them.
  • Step 2: Breaking Up Uniformity with Warps. To make the procedural tiles look more organic and natural, I routed noise nodes (like Perlin and directional noises) into Directional Warp and Anisotropic Blur nodes. This warped the tile edges and surface flatness, getting rid of that sterile, computer-generated look.
  • Step 3: Extracting Masks and Details. Using nodes like Threshold, Non-Uniform Blur, and various gradient generators, I isolated specific parts of the height map. This helped me target areas where I wanted to introduce micro-details, surface wear, and subtle elevation changes.
  • Step 4: Layering and Recombining. Finally, I used a series of Blend nodes to layer all the warped details, noise maps, and edge damage back onto the main tile structure. By carefully controlling the blend modes, I ended up with a rich, highly detailed base height map ready to feed into the clean, broken, and damaged subgraphs.

Grout Creation

Step 1: Isolating the Grout Lines. I started by taking the tile structure and passing it through a Threshold and an Invert Grayscale node. This allowed me to cleanly separate and invert the tile gaps, creating a precise mask specifically for the grout channels.

Step 2: Adding Surface Noise and Wear. To prevent the grout from looking completely flat and sterile, I set up a secondary branch using Clouds and BnW Spots nodes processed through Levels and Blend nodes. This generated organic surface grit and fine texture variations.

Step 3: Warping and Softening Details. I routed that noise map through Slope Blur Grayscale and Anisotropic Blur nodes to break up the harsh edges, giving the grout a weathered, slightly eroded look rather than a clean vector line.

Step 4: Combining and Finalizing. Finally, I used a series of Blend nodes to combine the grout mask with the eroded noise details, running the final output through a Levels node to dial in the depth and contrast before feeding it into the main graph.

Pattern Creation

  • Step 1: Constructing the Base Shapes (Shape 01 & Shape 02). I started by generating primitive shapes (using a basic Shape node) and modifying them with Transformation 2D, Skew, and Mirror Grayscale nodes to create custom petals and curved geometric elements.
  • Step 2: Distributing Elements with Tile Samplers. Using Splatter Circular Grayscale and Tile Sampler Grayscale nodes, I scattered and arranged these individual custom shapes into complex floral and geometric medallion patterns across a grid.
  • Step 3: Layering and Refining Patterns. By combining multiple tile networks and ring shapes using Blend and Anisotropic Blur nodes, I added intricate secondary details, smooth gradients, and soft transitions to enrich the design.
  • Step 4: Implementing Pattern Swapping. Finally, I organized these different configurations into distinct frames (Shape 01 and Shape 02) and routed them into a Switch Patterns node. This allowed me to easily toggle between different structural variations within the graph while keeping the workflow clean and non-destructive.

Base Color Creation

  • Step 1: Establishing Base Color Foundations. I started by feeding the core structural data and shape masks into Uniform Color and Gradient Map nodes with a Curvature Smooth Node to define the primary color palette and local color variations across the tiles.
  • Step 2: Adding Dirt, Grime, and Surface Variation. Using a network of procedural noises mixed with Blend nodes, I layered localized dirt, staining, and color shifts into the crevices and surface pores to break up the overall uniformity.
  • Step 3: Utilizing HSL and Gradients for Tone Control. I routed color data through HSL (Hue, Saturation, Lightness) and adjustment nodes to dynamically fine-tune the color values, ensuring a natural, harmonious color distribution between the tiles and the grout.
  • Step 4: Finalizing and Layering Details. Finally, I combined the ambient occlusion and edge-wear masks using multiple Blend nodes to introduce subtle highlights on edges and deeper weathering in the recesses, yielding a rich, production-ready base color map.

Roughness Creation

  • Step 1: Setting Up the Base Roughness. I started by taking the core height and structure maps and passing them through a Levels node to establish the primary roughness range, ensuring the overall surface isn't entirely uniform or flat.
  • Step 2: Extracting Curvature and Edge Wear. Using a Curvature Smooth node combined with Invert Grayscale and Levels nodes, I generated sharp masks to isolate edges, corners, and contact points where physical wear and tear naturally alter surface reflectivity.
  • Step 3: Layering Noise and Micro-Surface Details. I integrated a procedural noise source (Dirt 1) and blended it into the roughness stream using a series of Blend nodes, which helps break up the specular response and adds realistic fine-grained variation.
  • Step 4: Chaining and Finalizing the Map. Finally, I chained multiple Blend nodes together to layer the curvature wear and noise details onto the base roughness, culminating in a clean final output plugged straight into the graph's Roughness node.

I followed a similar workflow for creating the other tile texture and the trim texture, keeping the overall approach consistent with the first material. Each texture was developed with the same focus on surface variation, pattern placement, material breakup, and storytelling, so that they could work together cohesively when assembled into the final flooring layout.

I also maintained consistency in terms of scale, detail density, and visual language, ensuring that the different tile and trim elements felt like parts of the same environment rather than separate materials.

Presentation

As a game developer, I always present my materials and 3D environment work through real-time rendering. For that purpose, I primarily use Unreal Engine 5. Another reason for this is that I want my portfolio to showcase game-ready assets and demonstrate how they work within an actual game engine.

The presentation itself was pre-planned, as I intended to create three different variations of the texture. The idea behind the presentation was to demonstrate how I can tell a story through the tools and features available within the engine itself. 

I started by creating a custom texture-packing setup, which became the foundation for how the material would be presented and utilized in Unreal Engine.

Texture Packing

For this, I went with two packed texture maps, using the following channel configuration:

  • RGBA Map. The RGB channels contain the main Base Color data, while the A (Alpha) channel is used as a mask, which can represent either Height or a Grayscale Blend Mask.
  • NRH Map. The R and G channels contain the Normal Map data, while the B channel for the normal map is calculated within the shader to reconstruct the Normal. Then the B channel of the texture is used for Roughness or contains the Roughness map, and the A channel stores either Height or a Grayscale Blend Mask.

Two exported maps are provided with a proper naming convention for organized use.

Three Texture Vertex Blending Material Creation

1. Texture Setup & Organization

The first step in building this material involves setting up the core texture maps for three distinct variations: Clean (A), Broken (B), and Damaged (C).

Base Color Maps (Left Group)

  • UV Coordinate Input: All three Texture Sample nodes (01_BaseColor_Clean, 02_BaseColor_Broken, and 03_BaseColor_Damaged) are driven by a shared UV coordinate node to ensure uniform scaling.
  • Outputs: The RGB outputs from each texture sample feed into corresponding parameter collection or material attribute nodes (BaseColor_A, BaseColor_B, and BaseColor_C) to prepare them for blending.

Normal and Roughness Maps (Right Group)

  • UV Coordinate Input: Similar to the base colors, the normal map variations (01_Normal_Clean, 02_Normal_Broken, and 03_Normal_Damaged) share a common UV coordinate node.

Channel Packing & Separation

  • The RGB channels route into their respective normal map parameters (Normal_A, Normal_B, Normal_C).
  • The Blue (B) channel from the normal textures drives the Roughness parameters (Roughness_A, Roughness_B, Roughness_C).
  • The Alpha (A) channel from the Broken and Damaged normal textures connects to specific height masks (Vertex_B_HeightMask and Vertex_G_HeightMask) to be used later for vertex painting or mask-based blending.

2. UV Tiling and Vertex Color Setup

This section covers the global scaling controls for your textures and the setup for vertex-based blending masks.

UV Tiling Network (Left)

  • A TexCoord[0] node and a scalar UV parameter (default 1.0) are multiplied together to control global texture tiling and feed all texture samples.

Vertex Color Node (Right)

  • Input Data: A Vertex Color node extracts the color/mask data painted directly onto the mesh vertices.
  • Channel Breakdown:
    • The Red (R) channel connects to a Vertex_R parameter.
    • The Green (G) channel connects to a Vertex_G parameter.
    • The Blue (B) channel connects to a Vertex_B parameter.
  • Purpose: These vertex color channels will act as the driving masks to blend between your Clean, Broken, and Damaged material variations across the mesh surface.

3. Vertex Blended With Texture Masks

This network combines the vertex color channels with height maps and adjustable parameters to create clean blending masks for the broken and damaged states.

Green Channel/Broken Mask Setup (Top)

  • Height Combination: The Vertex_G_Height Mask and the Vertex_G color channel are multiplied together to blend the vertex mask with the texture's height data.
  • Strength Adjustment: The resulting value is multiplied by a scalar parameter Mask_Strength_A (default 1.0) to control the intensity of the blend.
  • Clamping & Output: A Saturate node clamps the value between 0.0 and 1.0, which then outputs to a Vertex_G_Blended parameter.

Blue Channel/Damaged Mask Setup (Bottom)

  • Height Combination: The Vertex_B_HeightMask and the Vertex_B color channel are multiplied together.
  • Strength Adjustment: This product is multiplied by a scalar parameter, Mask_Strength_B (default 1.0), to fine-tune the damage intensity.
  • Clamping & Output: A Saturate node cleans up the range, outputting to a Vertex_B_Blended parameter.

4. BaseColor Blending

  • Chain Interpolation: Three Linear Interpolate (Lerp) nodes are chained together to blend the texture variations.
  • Layer Integration: The network blends Name_1 and BaseColor_C using Vertex_B_Blended, mixes that result with BaseColor_B using Vertex_G_Blended, and finally blends with BaseColor_A using the Vertex_R mask.
  • Output: The final mixed color passes through a Mask (R G B) node before connecting directly to the BaseColor_Output.

5. Roughness Blending

  • Channel Blending: Similar to the BaseColor network, three Lerp nodes combine the roughness maps based on the vertex and height masks.
  • Transition Logic: Roughness_C and Roughness_A are blended via Vertex_B_Blended, then combined with Roughness_B using Vertex_G_Blended, and finally layered with Roughness_A driven by Vertex_R.
  • Output: The resulting value plugs straight into the Roughness_Output.

6. Normal Map Blending

  • Surface Normal Combination: A parallel setup of three Lerp nodes handles the normal map transitions.
  • Mask Application: Normal_A and Normal_C blend using Vertex_B_Blended, followed by mixing in Normal_B with Vertex_G_Blended, and concluding with Normal_A using the Vertex_R mask.
  • Output: The final combined surface normal vector routes into the main material's Normal Map output.

7. Normal Recalculate

This final polish step ensures the blended normal map retains proper lighting and vector orientation before sending it to the main material output.

  • Channel Masking: The combined NormalMap data passes through a Mask (R G) node to isolate the red and green tangent channels.
  • Range Remapping: The masked vectors are multiplied by 2.0 and then have 1.0 subtracted to properly remap the color range from texture space back into standard tangent vector space.
  • Z-Derivation & Output: A DeriveNormalZ node recalculates the missing Blue (Z) channel for the surface normal, which then connects directly to the final Normal_Output node.

Unreal Engine Presentation

I started by UV mapping both the tile textures and the trim texture into a flooring layout, with the intention of creating a more cohesive and story-driven surface rather than simply presenting the textures as individual materials. The layout was designed to create natural variation and visual rhythm across the floor while maintaining the character of the original tile patterns.

To establish a clear sense of scale and proportion, I added an unlit mannequin into the scene. This provided a useful human reference for understanding how the flooring reads in relation to a character and also helped evaluate the overall composition from a gameplay perspective.

Once the basic setup was in place, I established the camera angle and composition. From that point onward, I treated the camera view as the primary reference for developing the material and its storytelling. Instead of applying the same amount of variation uniformly across the entire floor, I focused on how the surface would actually be perceived from the final shot.

Using vertex painting, I selectively blended and distributed different material variations across the flooring. This allowed me to introduce areas of wear, exposure, damage, tonal variation, and breakup where they contributed most to the composition. More visible areas received stronger variation and detail, while less prominent areas were kept relatively subtle to avoid unnecessary visual noise.

The vertex painting was therefore used not only as a technical method for blending materials, but also as a storytelling and composition tool. I wanted the floor to feel like a surface that had naturally aged, been walked over, and interacted with over time, rather than something that was procedurally generated and evenly distributed.

I also paid attention to how the vertex-painted variations interacted with the existing tile patterns and trim details, making sure they enhanced the underlying textures without overpowering their design. This helped create a more intentional hierarchy of detail and allowed the stronger visual elements to remain readable from the chosen camera angle.

For the lighting, I intentionally kept the setup fairly simple. A single spotlight was enough to bring out the material definition, surface breakup, and tile details. I found that a soft, warm-temperature spotlight with a gradual falloff into the distance complemented the textures particularly well, so I avoided pushing the lighting further and kept the focus on the material and its storytelling.

Overall, the process was driven by the idea that the camera view is the final presentation of the asset. By combining the UV layout, material variations, vertex painting, scale reference, composition, and simple lighting, I could control where the viewer's attention goes and make the flooring feel more intentional and story-driven within the final shot.

Conclusion

As part of a dedicated learning and experimentation phase, the entire project took around 8 to 10 days to complete from start to finish. This timeframe encompassed the comprehensive lifecycle of the artwork, beginning with initial planning, reference gathering, and project organization. 

The process then moved into asset creation, specifically developing the three unique texture variations (Clean, Broken, and Damaged), followed by the intricate construction of the layered shader graphs and vertex-mask blending networks in Unreal Engine.

What I enjoyed working on the most was definitely the surfacing phase, particularly the aspect of visual storytelling through textures. Instead of viewing the maps as standard technical layers, I chose to approach them by imagining a deeper narrative for the surface's history and environment.

Translating those creative ideas into reality required leaning heavily into my accumulated visual library alongside collected references, allowing me to bridge the gap between imagination and final execution to achieve the desired look.

When it comes to learning and finding inspiration, I don't follow any single platform. However, material artists like Vincent Dérozier, Javier Perez, Eric Wiley, and Daniel Thiger have inspired me a lot. They share fantastic breakdowns in their portfolios, and their YouTube channels offer great content to learn from.

Additionally, the official Substance channel and Johnny Nodes are great places for tutorials. 80 Level has also been an invaluable resource; the large number of articles related to material art creation helped me tremendously throughout my learning phase and continues to do so today.

Special thanks to Ishan Verma for believing in me, constantly pushing me, and mentoring me from start to finish. His guidance and support helped me learn and get the most out of this journey.

I would also like to thank 80 Level for giving me the opportunity to share my work and process through this article. It was a great experience to document the workflow and showcase some of the techniques and decisions that went into creating these materials.

If you'd like to connect or follow more of my work, feel free to connect with me on LinkedIn.

Alisa Prakapenka, Material Artist

Interview conducted by Amber Rutherford

Built for the Game & Digital Art Industry
Get Our Media Kit

Comments

0

arrow
Type your comment here
Leave Comment
Built for the Game & Digital Art Industry
Get Our Media Kit