<aside>
A small UE5 study : Geometry Script builds the parametric window frame, and a Voronoi shader turns the glass into a mosaic
</aside>
The window shape is procedurally generated with Geometry Script. I start from two rounded rectangles (top and bottom), mirror them across the vertical axis so they meet in a pointed center, then extrude and boolean to form the frame. UVs are generated procedurally with a simple planar projection.
Proc_StainedGlass - Material.mp4
This is a single-pass Voronoi mosaic in a custom node(HLSL). I scale UVs to make a grid, then for each pixel I check the 3×3 neighbor cells and place one jittered seed per cell. I keep the closest and second-closest distances (d1, d2), which gives me the standard Voronoi structure. When a candidate becomes the closest, I convert that seed back to UV and sample the source texture. Every pixel inside that cell reuses that color, which creates the mosaic look. I also mix in an optional per-cell random tint to push a stained-glass vibe without new textures.
for (int y = -1; y <= 1; ++y)
{
for (int x = -1; x <= 1; ++x)
{
float2 g = float2(x, y);
float2 cell = iUV + g;
// generate a hash
float2 h;
h.x = sin(dot(cell, float2(127.1, 311.7)) + Seed * 6.2831853);
h.y = sin(dot(cell, float2(269.5, 183.3)) + Seed * 4.1231056);
h = frac(h * 43758.5453);
// Center jitter around (0.5, 0.5)
float2 r = (h - 0.5) * Jitter + 0.5;
diff = (g + r) - fUV;
dist2 = dot(diff, diff);
// Track nearest and 2nd nearest
if (dist2 < d1)
{
d2 = d1;
d1 = dist2;
nearestSeedUV = (cell + r) / Tiles;
bestCellP = cell;
}
else if (dist2 < d2)
{
d2 = dist2;
}
}
}
// sample color from nearest seed location
float3 col = Texture2DSample(Tex, TexSampler, nearestSeedUV).rgb;
// random color hash
float3 RanCol ;
RanCol.x = frac(sin(dot(bestCellP, float2(12.9898, 78.233)) + Seed*2.0) * 43758.5453);
RanCol.y = frac(sin(dot(bestCellP, float2(39.3467, 11.135)) + Seed*3.0) * 43758.5453);
RanCol.z = frac(sin(dot(bestCellP, float2(73.1560, 52.235)) + Seed*5.0) * 43758.5453);
col = lerp(col, (col * RanCol) + 0.01, RandomColorBias);