Modular Material System

Color Space

Jonathen Wilks

A Brief Intro

Color Space uses a dynamic and modular material system to represent all gameplay color states. The system combines hand-drawn texture work, material functions, and data-driven logic to ensure consistency, scalability, and flexibility across the project.

Hand-drawn Textures

Each material uses a surface texture I drew in Adobe Fresco to add visual detail and a tactile quality that felt more interesting than a flat procedural look.I started with the darkest version, then adjusted contrast and brightness to create the variants. This gave me a consistent yet flexible base to work with.

T_BaseColor_Dark, Hand-drawn Texture

Credit: Jonathen Wilks

T_BaseColor_Light, Hand-drawn Texture

Credit: Jonathen Wilks

T_BaseColor_SuperLight, Hand-drawn Texture

Credit: Jonathen Wilks

Material Function

These textures are blended in the MF_BaseColor material function using simple lerps and masks. The function outputs the BaseColor, roughness, ambient occlusion, and specular values, reducing redundant logic and allowing for centralized updating in the future for similar materials. This gave every material a shared surface feel while still allowing distinct coloration.

The core material, M_ColorMatch, uses this function and exposes a BaseColor vector parameter. I then created dozens of material instances (like MI_CM_R4, MI_CM_G2, MI_CM_Y3, etc.), each with a specific RGB value tied to gameplay color logic.

MF_BaseColor, Unreal Material Function

Credit: Jonathen Wilks

M_ColorMatch, Unreal Material

Credit: Jonathen Wilks

M_ColorMatch, Unreal Material Instance

Credit: Jonathen Wilks

MF_BaseColor, Unreal Material Function

Credit: Jonathen Wilks

Why a Material Function?

MF_BaseColor, Unreal Material Function

Credit: Jonathen Wilks

Earlier in development, I explored the idea of using unique surface patterns to communicate different object interactions, such as collision based mechanics for passing through or walking over puzzle objects.

Originally, the M_ColorMatch material housed everything, including the custom logic and textures. But as I began designing multiple materials for different interaction types, I realized I needed centralized control. So I restructured the material to rely entirely on a single input: the BaseColor vector. The material function handled everything else—meaning I could globally update visual logic across all materials with one change.

This modular design made it easy to prototype new material types without duplicating effort. For instance, I created a test material (M_CM_2xStripe) using a simple UV math setup (TexCoord, Frac, Clamp, Step) to generate customizable stripes. These were meant to visually distinguish different color logic types. While I ultimately scrapped the idea for gameplay clarity, the system made that experimentation quick and painless—and it remains flexible enough to revisit later if needed.

Conclusion

Unreal doesn’t currently make it easy to pull vector parameters directly from Material Instances at runtime. To work around this, I created a custom Data Table where each Material Instance was manually assigned a name and corresponding RGB vector.

A name and matching RGB vector value was manually entered for each material instance in the table. This ensured that my Blueprint logic could reference the exact same color information used in the visuals.

I explore that system more deeply in a separate case study, but this material pipeline was built specifically to support that data-driven structure.