Guide, 2026-11-04
Seamless PBR textures explained: maps, real-world scale and OpenGL vs DirectX normals
A PBR (physically based rendering) material is a set of image maps that each describe one property of a surface. Once you know what each map does, it is easy to plug a set into any modern 3D application. Three details cause most of the confusion: colour spaces, real-world scale and the normal map convention. This guide covers all three.
What each map does
- Basecolor (also called albedo or diffuse): the colour of the surface with no lighting baked in. It is an sRGB image.
- Normal: stores a direction per pixel, which makes flat geometry react to light as if it had small bumps and grooves. Red, green and blue hold the X, Y and Z of that direction.
- Roughness: how blurry the reflections are. Black is mirror-like, white is fully matte.
- Metallic: whether a pixel is metal (white) or not (black). Most pixels are one or the other, with grey only at transitions.
- Height (displacement): how high each pixel sits. Use it for parallax or real displacement, where geometry is actually moved.
- Ambient occlusion (AO): darkening in crevices where ambient light cannot reach. It is multiplied into the ambient lighting.
Colour space: sRGB or not
Only the basecolor map is an sRGB colour image. Normal, roughness, metallic, height and AO hold data, not colours, so they must be read as linear (called Non-Color in Blender and with sRGB turned off in Unreal and Unity). If a data map is read as sRGB, the values are bent by the gamma curve and the surface looks too shiny, too dark or oddly lit.
Real-world scale
A texture means nothing until it is mapped at the right size. Each material should represent a known physical area. If the stated tile size is 1 m, then a 2048 px map has 2048 / 1000 = about 2.05 px per millimetre, or about 0.49 mm per pixel. For a wall 4 m wide and 3 m tall you would repeat the texture 4 times across and 3 times up, so 4 x 3 = 12 tiles. If the tile size were 0.5 m, the same wall would need 8 x 6 = 48 tiles, and each pixel would cover about 0.24 mm. Brick that looks too big or too small is almost always a scale error, so set the physical size before judging anything else.
Height maps need a physical range as well. Suppose a height range of 40 mm is stored in an 8-bit image, which has 256 levels, or 255 steps. Each step is then 40 / 255 = about 0.16 mm. Fine for bricks and cobbles, but too coarse for micro-detail, so do not expect very fine real displacement from an 8-bit height map.
OpenGL vs DirectX normals
The two conventions differ in one thing: the direction of the green channel.
| Convention | Green channel | Typically used by |
|---|---|---|
| OpenGL | Y up | Blender, Unity, Godot |
| DirectX | Y down | Unreal Engine |
If you use the wrong one, lighting looks inverted on the vertical axis: bumps look like dents, and the shading seems to come from the wrong side. The fix is to flip (invert) the green channel. In Unreal Engine you can tick Flip Green Channel in the texture settings, so the original file does not need to be edited. In an image editor you can also invert only the green channel. For a map that is already in the right convention, do nothing.
Plugging the maps in
- Blender: connect basecolor to Base Color, roughness and metallic to their sockets, and the normal map through a Normal Map node, with every data image set to Non-Color.
- Unreal: use the DirectX convention for the normal map (flip green), and turn off sRGB for data textures.
- Unity: Lit shaders use smoothness, which is 1 - roughness. A roughness of 0.7 becomes a smoothness of 0.3. Invert it in Shader Graph or pack it into a mask map yourself.
- Godot: assign the maps in a standard material and enable the normal map and AO channels; roughness is used directly.
Tiling without seams
A seamless texture is made so that the left edge continues into the right and the top into the bottom. A quick test is to repeat the texture 2 x 2 and look for lines or jumps in the middle. Repeating patterns can be noticeable on large surfaces, so vary the scale between neighbouring surfaces or mix in a second, larger-scale variation.
Common mistakes
- Leaving the normal map in sRGB.
- Using the wrong normal convention and then flipping the light instead of the green channel.
- Mapping a texture at the wrong physical size.
- Plugging roughness into a smoothness input without inverting it.
When a ready-made pack saves time
Making clean tileable materials from scratch takes a while. Surfaces is a pack of 24 materials, including brick, tile, terrazzo, concrete, stone, wood, metal, fabric, sand and gravel. Each has six 2048 x 2048 PNG maps (basecolor, OpenGL normal, roughness, metallic, height and AO), and a materials.json lists a suggested tile size and height range for each. The maps are procedurally generated by code, not scanned, are 2K only with 8-bit height, and come without engine-specific material files. The README gives setup notes for Blender, Unreal, Unity and Godot, but they have not been test-imported into those programs by us.
Skip the build: Surfaces, $6.30 $9$9
Surfaces is a pack of 24 seamless PBR materials at 2048 x 2048, each with basecolor, OpenGL normal, roughness, metallic, height and AO maps, plus a materials.json with real-world tile sizes. They are procedurally generated and not test-imported into any engine.