../

Materials, textures & lighting

How surfaces and light work in any modern renderer: the PBR metal/roughness model and sane values, texture maps and their color spaces, normal maps, packing and baking, light types and units, image-based and three-point lighting, shadows and global illumination, tone mapping, and cameras. Geometry basics are in Core 3D concepts; doing it in Blender is in Materials & UVs and Lighting & rendering.

PBR: the metal/roughness model

PBR (physically based rendering) describes a surface by measurable properties instead of hand-tuned "shininess", so one material looks right under any light. glTF, three.js, Godot and Blender's Principled BSDF all share the metal/roughness model.

glTF packs these into one ORM image: R · G · B base color sRGB normal Non-Color occlusion (AO) Non-Color roughness Non-Color metallic Non-Color one material Blender: Principled BSDF three.js: MeshStandardMaterial Godot: StandardMaterial3D color maps: sRGB data maps: Non-Color (linear)
One material, five maps: only base color is a color; the rest are data.
InputControlsValuesMap color space
Base color (albedo)diffuse color of non-metals; reflection color of metals; no light or shadow painted insee the values tablesRGB
Metallicmetal or not0 or 1; in-between only where they blend (rust, worn edges, dirt)Non-Color
Roughnessmicro-surface: 0 = mirror, 1 = fully matte (gloss = 1 − roughness in older workflows)0–1Non-Color
Normalfakes small bumps by bending the shading normaltangent-space RGBNon-Color
Ambient occlusion (AO)darkens crevices that ambient light can't reach; affects indirect light only0 (hidden) – 1 (open)Non-Color
Emissionlight the surface gives off (screens, neon, lava)color × strengthsRGB (color)
Alpha / opacitycutout (alpha clip / mask) or see-through (blend)0–1Non-Color (often base color's A channel)
IORindex of refraction: how strongly non-metals reflect head-on1.5 default (≈4% reflectance); water 1.33value
Transmissionlight passing through (glass, liquids); costly in real time0–1Non-Color
Extrasclearcoat (car paint), sheen (velvet), subsurface (skin, wax), anisotropy (brushed metal)per extensionvaries

glTF core covers base color, metallic, roughness, normal, occlusion, emission and alpha; the rest travel as KHR_materials_* extensions that Blender exports and three.js reads. Godot's StandardMaterial3D covers most of them natively.

Typical values

Values from Physically Based (opens in a new tab), as linear RGB (the number you type in a color field set to linear; image textures store the brighter-looking sRGB equivalent).

MaterialBase color (linear RGB)MetallicRoughness (typical)IOR
Charcoal (darkest common)0.02, 0.02, 0.0200.91.5
Tire rubber0.023, 0.023, 0.02300.71.5
Brick0.26, 0.10, 0.0600.91.5
Concrete0.51, 0.51, 0.5100.5–0.91.5
Skin (darkest → lightest)0.09, 0.05, 0.02 → 0.85, 0.64, 0.5500.4–0.61.4
Fresh snow (brightest common)0.85, 0.85, 0.8500.51.31
Water / glass1, 1, 1 + transmission 100–0.11.33 / 1.52
Iron0.53, 0.51, 0.4910.3–0.6n/a
Aluminum0.92, 0.92, 0.9210.2–0.5n/a
Copper0.93, 0.62, 0.5210.2–0.5n/a
Gold1.0, 0.77, 0.3110.1–0.4n/a
RuleWhy
Non-metals (dielectrics): base color between ~0.02 and ~0.85 linear (≈ 40–240 in 8-bit sRGB)nothing real absorbs or reflects all light
Never pure black (0) or pure white (1) albedoblack reads as a hole and kills bounce light; white glows and blows out GI (bounced light)
Metals: bright, often tinted base color (≈ 0.5–1.0 linear) and metallic = 1a metal's color is its reflection; dark metal looks like plastic
Metals need something to reflectwith no environment map or HDRI (a 360° photo of surroundings, see Lighting a scene), metallic = 1 renders nearly black
Put variation in roughnessroughness maps sell realism more than base color detail does
Keep lighting out of base colorbaked shadows in albedo double up with real shadows

Texture maps & color spaces

A texture map is an image the material samples through the mesh's UVs. Some maps hold colors, others hold data (numbers stored in pixels). They need different color spaces:

  • sRGB: the image's values are gamma-encoded for human eyes; the renderer decodes them to linear before lighting. Right for anything you'd call a color.
  • Non-Color / linear: the numbers are used as-is. Right for anything that is a measurement.
MapColor spaceBlender Image Texture nodethree.js texture.colorSpaceGodot
Base color, emissionsRGBsRGBSRGBColorSpaceautomatic in albedo / emission slots
NormallinearNon-ColorNoColorSpace (default)import as normal map (detected when used)
Roughness, metallic, AO, ORMlinearNon-ColorNoColorSpace (default)automatic in those slots
Height / displacement, masks, opacitylinearNon-ColorNoColorSpace (default)same

This is the most common beginner bug. The symptoms:

MistakeLooks like
Data map left on sRGBroughness too shiny (0.5 becomes ≈ 0.21), normal maps light from the wrong angle, lumpy shading
Color map on Non-Colorwashed-out, pale, low-contrast colors
three.js texture loaded by hand without SRGBColorSpacesame washed-out look; GLTFLoader sets it for you

Also set the output: three.js renderer.outputColorSpace is SRGBColorSpace by default; leave it.

Normal, bump & displacement maps

TechniqueStoresMoves geometry?CostUse
Normal mapper-pixel surface direction (RGB = XYZ)no, shading onlycheapthe default for game and web detail
Bump / height mapgrayscale height; renderer derives normalsnocheap, softer than normal mapsquick detail in Blender; converts to normal maps
Parallax occlusionheight, ray-marched in the shaderno, but fakes depthmediumbricks, cobbles in engines (Godot has it; not in glTF)
Displacementgrayscale heightyes: vertices moveneeds dense mesh or subdivisionoffline renders, terrain; bake to geometry for export

Tangent-space normal maps (the light blue-purple ones; flat = RGB 128, 128, 255) store directions relative to the surface, so they survive deformation and reuse. They need tangents on the mesh; the standard is MikkTSpace, which Blender bakes with and glTF expects.

OpenGL vs DirectX: the green channel means "up" in OpenGL style (Y+) and "down" in DirectX style (Y−).

ConventionUsed by
OpenGL (Y+)Blender, glTF, three.js, Godot, Unity
DirectX (Y−)Unreal, many texture sites' "DX" downloads

Wrong convention: bumps look like dents, lit from the opposite side. Fix by inverting the green channel (or download the "GL" version).

Resolution, power of two & compression

SizeUncompressed RGBA8 in GPU memory (+ mipmaps)Typical use
512²1 MB (≈ 1.3 MB)small props, mobile
1024² (1K)4 MB (≈ 5.3 MB)props, web default
2048² (2K)16 MB (≈ 21 MB)hero assets, web max on phones
4096² (4K)64 MB (≈ 85 MB)film, desktop hero close-ups
  • The PNG/JPEG file size is not the memory cost: every image is decompressed to the table above on the GPU. Ten 4K maps are ~850 MB of VRAM, which crashes phones.
  • Power-of-two sizes (256, 512, 1024, 2048) are safest: mipmaps and GPU block compression want them (or at least multiples of 4). WebGL2 accepts other sizes, but some pipelines don't.
  • Mipmaps are pre-shrunk copies used at distance; they stop shimmering and cost +33% memory.
  • GPU compression (KTX2 / Basis Universal on the web, BCn on desktop, ASTC/ETC on mobile) stays compressed in VRAM, about 4–8× smaller; see three.js compression decoders.

Channel packing (ORM)

Grayscale maps use one channel each, so engines pack three into one RGB image: one file, one texture fetch, a third of the memory.

ChannelglTF (and three.js, Unreal "ORM")
Rocclusion (AO)
Groughness
Bmetallic

glTF's metallicRoughnessTexture reads G = roughness, B = metallic; occlusionTexture reads R, and the two may point at the same image. Blender's glTF exporter packs them for you when the maps are wired separately. Godot has ORMMaterial3D for this layout. Other engines pack differently (Unity HDRP's mask map is metallic, AO, detail, smoothness), so check before reusing a texture set.

Procedural textures & baking

Image texturesProcedural textures
Made ofpixels painted, photographed or generatedmath: noise, Voronoi, gradients, node graphs
Resolutionfixedinfinite, no UVs needed for 3D noise
Tweakablerepaintchange a number
Exports to glTF / enginesyesno: engines can't run Blender node trees

Baking renders something expensive into an image the engine can sample cheaply:

BakeFromInto
Procedural → imageBlender node materialbase color, roughness… maps
High-poly → low-polysculpted detailnormal map (+ AO) on the light mesh
Ambient occlusionthe mesh's own crevicesAO map
Lightinglights + GI of a static scenelightmap (needs unique UV2)
Animation / simulationconstraints, physicskeyframes or shape keys (see Animation & rigging)

Games bake because runtime cost matters more than flexibility. Blender bakes with Cycles; the steps are in Blender materials & UVs.

Lights and their units

TypeEmitsBlenderglTF (KHR_lights_punctual)three.jsGodot
Pointall directions from a pointPoint (W)point (candela)PointLightOmniLight3D
Spota coneSpot (W)spot (candela)SpotLightSpotLight3D
Sun / directionalparallel rays from infinitely far; position doesn't matterSun (W/m²)directional (lux)DirectionalLightDirectionalLight3D
Areaa rectangle or disk; soft shadowsArea (W)not supportedRectAreaLight (no shadows)none: use emission + GI, or light size for soft shadows
Emissive surfacea glowing meshEmission shaderemissive materialemissive materialemission (+ GI to light others)
Sky / ambienteverywhereWorldnot storedHemisphereLight, scene.environmentWorldEnvironment

Units differ, so brightness rarely survives export:

  • Blender: point, spot and area power in watts of radiant power (not a bulb's electrical watts); sun strength in W/m² (the manual gives ~1000 for a clear sky; lower it with exposure, since eyes adapt and renders don't).
  • glTF: point and spot in candela, directional in lux. Blender's exporter converts with its Lighting Mode (Standard = physical units, or Unitless).
  • three.js: GLTFLoader passes those values straight to its lights; PointLight and SpotLight intensity is documented in candela.
  • Godot: unitless energy by default; physical units (lumens, lux) are an opt-in project setting.

Plan to re-balance lights in the engine, or bake the lighting.

Lighting a scene

Image-based lighting (IBL): an HDRI (a 360° high-dynamic-range photo, .hdr / .exr) wraps the scene, lights it from every direction and gives shiny surfaces something to reflect. Fastest route to a believable look; Poly Haven (opens in a new tab) has free CC0 ones. Blender: World > Environment Texture. three.js: scene.environment. Godot: WorldEnvironment with a panorama sky. glTF doesn't carry it, so set it up again in the engine.

Three-point lighting is the classic setup for a character or product:

subject camera key light main, brightest; ~45° to the side and ~30–45° above fill light opposite side, softer, ~25–50% of the key rim (back) light behind, outlines the silhouette Three-point lighting, seen from above An HDRI often stands in for the fill.
Key shapes the form, fill softens its shadows, rim separates it from the background.
LightWhereStrength
Key~45° to one side of the camera, ~30–45° abovebrightest; sets the shadow direction
Fillthe other side, lower~25–50% of the key; an HDRI or sky often does this job
Rim / backbehind the subject, abovebright, thin highlight on the silhouette

Shadows & global illumination

Direct light travels from a light straight to a surface. Global illumination (GI) adds light that bounces: the red glow a red wall throws on a white floor, light filling a room from a window.

TechniqueWhatWhere
Shadow mapsrender depth from the light's view; per shadow-casting light, each frameevery rasterizer; watch resolution, bias (acne vs "peter-panning"), cascades for the sun
Contact / screen-space shadowssmall shadows the shadow map missesGodot, EEVEE
Path-traced GIfull bounced lightCycles; the reference
Screen-space GI / ray tracingbounce from what's on screenEEVEE (4.2+), Godot SSIL
Real-time GI volumesvoxels or signed distance fieldsGodot VoxelGI, SDFGI
Baked lightmapsGI precomputed into textures on static meshesGodot LightmapGI; three.js lightMap; Blender bakes
Reflection probes / env mapsa captured cube of surroundings for reflectionsGodot ReflectionProbe; three.js env maps
Baked lightmapsReal-time lighting
Runtime costalmost freeshadow maps and GI every frame
Qualityhigh: soft shadows, bounceslimited by the frame budget
Moving lights or objectsno (static only)yes
Needsunique second UV set, bake time, texture memorynothing extra
Best forarchitecture, web scenes, static levelsday/night cycles, destructible or dynamic scenes

Common hybrid: bake static lighting, add one real-time light for moving characters, and use probes for reflections. three.js has no built-in GI, so web scenes bake or rely on an HDRI.

Exposure & tone mapping

Rendered light values range from near 0 to thousands (high dynamic range); screens show 0–1. Exposure scales the scene (one "stop" = twice or half as bright), then tone mapping squeezes the range into the display without harsh clipping.

Tone mapperLookWhere
None / Standard / Linearclips highlights; colors match input exactlyBlender "Standard"; for UI, pixel art, color checks
Filmicsoft film-like highlights, desaturatesBlender (default before 4.0), Godot
AgXsmooth highlight rolloff that keeps hues; Blender's default since 4.0Blender, three.js AgXToneMapping, Godot
ACESpunchy, contrasty filmic curvethree.js ACESFilmicToneMapping, Godot
Khronos PBR Neutralkeeps base colors true under neutral light; for productsBlender (4.2+), three.js NeutralToneMapping

Use the same tone mapper in Blender and the engine, or the export will look different from the render. Texture maps and baked colors should be authored and baked without tone mapping in them.

Cameras

SettingMeaningNotes
Focal length (mm)lens zoom; with the sensor size it sets the field of view24–35 mm wide, 50 mm "normal", 85 mm+ portrait / tele; Blender uses mm
Field of view (FOV)the angle the camera seesthree.js fov is vertical, in degrees (default 50); Godot fov vertical by default (75)
Sensor sizefilm or chip widthBlender default 36 mm (full frame)
Depth of fieldblur in front of and behind the focus distance; stronger at low f-stopcheap in Cycles, a post effect in real time
Perspective vs orthographicdistant things smaller vs no foreshorteningortho for isometric games, technical views, UI, sun shadows
Clip start / end (near / far)nothing closer or farther is drawndepth precision depends on far ÷ near: raise near first (three.js defaults 0.1 / 2000, Godot 0.05 / 4000)

Converting a Blender focal length to an engine FOV: FOV=2arctan⁡(sensor2f)\text{FOV} = 2 \arctan\left(\frac{\text{sensor}}{2 f}\right). A 50 mm lens on a 36 mm sensor gives ≈ 39.6° across the sensor's fitted side; use the sensor height (or Blender's Sensor Fit: Vertical) when the engine wants vertical FOV.

Recipes

Debug a black or pink material

Work down the list; the first match is usually it.

SymptomCauseFix
Magenta / pink in Blenderimage file missing or movedFile > External Data > Find Missing Files, or pack resources
Black metalmetallic = 1 with nothing to reflectadd an HDRI / environment map
Black or dark everywhere in the engineno lights, or exposure too lowadd a light or environment; check exposure
Dark or missing facesflipped normals + backface cullingrecalculate normals (see Core concepts)
Weird lighting direction, lumpy looknormal map on sRGB, or DirectX green channelNon-Color; invert green
Too shiny or too matteroughness map on sRGBNon-Color
Pale, washed-out colorsbase color on Non-Color, or missing SRGBColorSpacesRGB
Fine in Blender, flat gray in the engineprocedural nodes or unsupported nodesbake to image textures

Wire a texture set correctly

Use for any downloaded or generated set (base color, normal, roughness, metallic, AO).

  • Base color → Base Color, sRGB
  • Normal → Normal Map node (Tangent Space) → Normal, Non-Color; check GL vs DX
  • Roughness, metallic, AO → Non-Color; ORM split with a Separate Color node (R, G, B)
  • Alpha → Alpha, and set the material to clip or blend
  • All maps share the same UV map and resolution
  • Preview under an HDRI, not just the default light

Make a material survive export

Use before exporting to glTF, three.js or Godot.

  • One Principled BSDF per material, fed only by Image Texture, Normal Map and Separate Color nodes
  • Procedural, mix and custom nodes baked to images
  • Texture sizes power of two, ≤ 2K for web
  • Emission strength and alpha mode set deliberately
  • Few materials per object (each is a draw call)
  • Re-check in the target engine with its own lights and tone mapper

Light a hero shot fast

Use for a product, prop or character render or a web viewer.

  1. HDRI for ambient light and reflections; rotate it until the highlight falls where you want.
  2. Add a key light ~45° to the side and above; the HDRI now acts as fill.
  3. Add a rim light behind to separate the subject from the background.
  4. Tone mapping: AgX (or Khronos PBR Neutral for exact product colors); set exposure so whites aren't clipped.
  5. Soft shadows: make the key light larger (Blender radius / size).

Pick a texture resolution

Use when choosing how big each map should be.

  1. Choose a texel density for the project (e.g. 512 px/m for third-person, 1024 px/m for first-person).
  2. Longest side of the UV area in meters × density = pixels needed: a 2 m wall at 512 px/m → 1024.
  3. Round up to the next power of two, cap by target (2K on phones).
  4. Reuse tiling textures and trim sheets for big surfaces instead of one huge unique texture.

Match a Blender camera in the engine

Use when a web or game view must match a Blender render.

  1. Note Blender's focal length, sensor size, Sensor Fit, and the render resolution.
  2. Convert to vertical FOV with the formula above (use sensor height = sensor width × height ÷ width for a horizontal fit).
  3. Export the camera with glTF (it stores vertical FOV) or set fov by hand in three.js / Godot.
  4. Match near/far clip and the tone mapper.

References