Hex-tiling anti-tiling
Same 3 m ground texture on both halves of this field.
Left: plain tiling. Once the camera rises, the grid shows.
Right: hex-tiling. Every hex cell reads the texture with its own offset and rotation.
The problem
Any tiled texture repeats. Up close you don't notice. Raise the camera and a 3 m ground texture becomes a grid of the same moss patch and the same 2 big pebbles. The left half is plain tiling.
How it works
Hex-tiling (Mikkelsen, JCGT 2022) lays a hexagon grid over the UVs.
- Each pixel finds the 3 nearest hex centres and barycentric weights for them.
- Each hex cell gets its own random offset and rotation, so it reads a different part of the texture at a different angle.
- The 3 reads blend together. The weights are raised to the 7th power, so cell interiors show 1 read and only the borders mix.
- Weights also lean on luminance (contrast 0.6), so bright features win at borders instead of fading into grey.
Normal maps go through the same 3 reads, but blended as surface gradients and rotated back into place, so the lighting stays correct.
What I hit
The source is HLSL with row-major float2x2 matrices. GLSL mat2 is column-major, so every mul() needed its order checked by hand, one call at a time.
I painted the test texture with large, distinctive features on purpose. A busy noise texture hides the repeat on its own and the comparison would look weaker than it is.
My build
three.js 0.170, ported into MeshStandardMaterial with onBeforeCompile. Own procedural 1024² soil texture with moss, a wet patch and 36 pebbles, plus a normal map from its height. Hex cells come out about 0.87 m wide with the 3 m tile. Same camera on both halves, rendered twice with a scissor.
Trade-offs
- Works with any existing tileable texture, no new art
- Normals stay correct after rotation
- 3 colour reads and 3 normal reads per pixel instead of 1 each
- Textures with strong direction (bricks, planks) look wrong when rotated, so turn rotation off for those
When to use it
Large ground, rock and sand surfaces seen from far away. For directional textures, keep the offsets and drop the rotation.
Source and credits
Technique: Practical Real-Time Hex-Tiling by Morten S. Mikkelsen (JCGT 2022), building on Heitz & Neyret and Deliot & Heitz.
Repo: github.com/mmikk/hextile-demo (MIT license)
Code: I ported TriangleGrid, LoadRot2x2, MakeCenST, hex2colTex, bumphex2derivNMap and ProduceHexWeights from hextile-demo/hextiling.h (HLSL) to GLSL in three.js. The ground texture, rocks and scene are my own; no textures, models or screenshots from the repo.
Topic inspired by @toreler's anti-tiling posts. TileShuffle (paid Fab product) code and assets were not used.