Geometric art holds a special place in my heart. Pieces like Islamic tile patterns captures an amazing blend of beauty and mathematics. Lifeless shapes are tiled together to create beautiful intricate art.
The simplest and most common tile art is right under your feet, the grid. In shaders, the grid is also the starting place for a lot of types of patterns. For example, in this week's demo!
While it's not visible, I create a hidden grid and draw a bunch of quarter circles in each of the cells. Because I'm doing this for every cell, the circles are bound to connect and create a swirly pattern.
Getting started
- Hexagon Grid Tiling explained
- Rose Patterns by Coding train
- Gorgeous handmade Geometric design for beginners!
- Islamic start patterns part1
- Islamic star patterns Part2
Square and triangle grids
This is how you make the most basic grid in a shader:
vec2 grid = uv * 10.;
vec2 gridFract = fract(grid);
The UVs have a 0-1 range. If we want 10 cells, we multiply by 10.
- gridFract(fract) - Taking the decimal(fract) part of the number gives individual grid cells with gradients from 0 to 1. This is what we use to create our tiling pattern.
The grid in this demo is based on Equilateral Triangles, but you can also make grids out of Right Triangles by splitting squares.
Rotating Grids
In this demo, all the cells share the same line pattern. However, some of them are handpicked and rotated depending on the cell index:
vec2 gridIndex = floor(grid);
float rotation = gridIndex.x * HALF_PI;
uv = rot2(angle) * uv;
gridIndex - This makes all the pixels within each cell share the same value/index 1,2,3...
rotation - As the index gets bigger, the cell is rotated more and more.
uv - Apply the rotation to the UVs. Since each cell had a different rotation value, the resulting UV is going to have the cells rotated individually.
Hexagon Grid
The inner workings of hexagon tiling are a bit complicated. The Art of Code has an amazing step-by-step video explaining it. But the functions work like any other grid functions:
vec2 hexUV = hexCoord(uv * 10.).xy;
float dist = hexDist(hexUV) ;
- hexCoord - transforms our UVs into hexagon UVs.
- hexDist - Gives us the to the center of the hexagon cell.
Here's a barebone hexagon grid demo for you to start playing!
Truchet patterns
The idea of Truchet patterns is to make tiles with edges that match up. One of the simplest Truchet patterns is the Smith tiles, where two corners have a semi-circle. When tiled, these corners connect. Then, they can be rotated/flipped to create interweaving patterns.
Truchet patterns become more interesting when you have multiple types of tiles. In The Truchet-Carlson pattern, all the tiles match at the edges. However, in the middle of the tile, they are different from each other which creates a lot of variation.
Voronoi patterns
Even though Voronoi patterns look all irregular, they can be made with good old grids:
vec2 gridPoint = random2d(gridIndex.xy);
float dist = length(uv.xy, gridPoint.xy)
- Create pick a random point for each grid cell.
- For every pixel in the image, loop through all the cells and figure out which cell's point is the nearest:
However, comparing with all the cells might get too expensive to calculate. We can optimize this by only checking the 9 cells that surround the current pixel's cell.
If you think about it, each cell is surrounded by 8 neighbors cells. So, the nearest point has to be between those 9 points (counting itself). See the grid in action in this shadertoy.