# PBR textures

Metalness/roughness materials from a folder of maps. Three.js `MeshStandardNodeMaterial`. Blender / FreePBR `bl` sets (OpenGL normals).

## Folder layout

```
assets/textures/oily-tubework/
  albedo.png
  metallic.png
  roughness.png
  normal.png      (OpenGL, Y+)
  ao.png
  height.png
```

Albedo is sRGB. The rest are linear. Skip Unity (packed smoothness) and Unreal (DirectX normals).

## Load

```js
import { loadPbrMaterial, prepPbrGeometry } from './graphics/PbrMaterial.js';

const mat = await loadPbrMaterial(assets, 'oily-tubework', {
  envMap: environment.envMap,
});
environment.trackIbl(mat);

applyWorldUVs(geo, 2, origin);
prepPbrGeometry(geo);
```

`prepPbrGeometry` after `applyWorldUVs`. Subdivide if you use height (sandbox boxes: 16 segments).

### Height on a box

A box is six UV islands. At a corner the same point has three UVs, so three height samples. Pushing those verts along the maps **opens a hole**. Averaging the normals instead **bends the faces**.

NGIN pins UV-seam verts (`dispWeight` 0) and displaces only face interiors along the face normal, using the albedo UVs. Edges stay a closed box. Relief is on the faces. The normal map still shades the edges.

Do not triplanar-displace a UV-mapped box. Do not weld box edge normals.

Reflections: pass `envMap` on the **material** (`environment.envMap`). Do not set `scene.environment`. `environment.trackIbl(mat)` so night dims the cubemap (otherwise metals stay day-bright).

## Sandbox

Eight boxes cycle four sets: oily-tubework, red-scifi-metal, storage-container2, worn-factory-siding. GUI **Textures → displacement** is a live uniform (0–0.2, default 0.045). Drag it on a box.
