Texture baking

텍스처 베이킹

Computing a complex, high-resolution model’s detail — bumps, occlusion, color — once and “baking” it into a flat texture applied to a much simpler model.

Also known as: BakingBake to texture
···
html
<div class="sub-caps"><span>raw low-poly</span><span>baked textures</span></div>
css
.sub-caps{position:absolute;left:0;right:0;bottom:6%;display:flex;justify-content:space-around;font-size:clamp(8px,2.4vmin,12px);color:#dfe3ffcc;text-align:center;padding:0 3%}
js
import * as THREE from 'three';
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0d0d16);
const camera = new THREE.PerspectiveCamera(42, 1, 0.1, 100);
camera.position.set(0, 0.5, 6.2);

const S = 96;
const colorCv = document.createElement('canvas'); colorCv.width = colorCv.height = S;
const cc = colorCv.getContext('2d');
cc.fillStyle = '#caa46a'; cc.fillRect(0, 0, S, S);
const bumps = [];
for (let i = 0; i < 36; i++) bumps.push({ x: Math.random() * S, y: Math.random() * S, r: 3 + Math.random() * 5 });
for (const b of bumps) {
  const g = cc.createRadialGradient(b.x, b.y, 0, b.x, b.y, b.r);
  g.addColorStop(0, 'rgba(60,40,20,0.5)'); g.addColorStop(1, 'rgba(60,40,20,0)');
  cc.fillStyle = g; cc.beginPath(); cc.arc(b.x, b.y, b.r, 0, Math.PI * 2); cc.fill();
}
const colorTex = new THREE.CanvasTexture(colorCv);

function heightAt(x, y) {
  let h = 0;
  for (const b of bumps) {
    const d = Math.hypot(x - b.x, y - b.y) / b.r;
    if (d < 1) h += Math.pow(Math.cos(d * Math.PI * 0.5), 2);
  }
  return h;
}
const normalCv = document.createElement('canvas'); normalCv.width = normalCv.height = S;
const nc = normalCv.getContext('2d');
const nimg = nc.createImageData(S, S);
for (let y = 0; y < S; y++) {
  for (let x = 0; x < S; x++) {
    const hl = heightAt(x - 1, y), hr = heightAt(x + 1, y), hu = heightAt(x, y - 1), hd = heightAt(x, y + 1);
    const vx = -(hr - hl) * 5, vy = -(hd - hu) * 5, vz = 1;
    const len = Math.hypot(vx, vy, vz);
    const i = (y * S + x) * 4;
    nimg.data[i] = ((vx / len) * 0.5 + 0.5) * 255;
    nimg.data[i + 1] = ((vy / len) * 0.5 + 0.5) * 255;
    nimg.data[i + 2] = ((vz / len) * 0.5 + 0.5) * 255;
    nimg.data[i + 3] = 255;
  }
}
nc.putImageData(nimg, 0, 0);
const normalTex = new THREE.CanvasTexture(normalCv);

const geo = new THREE.IcosahedronGeometry(1.05, 2);
const raw = new THREE.Mesh(geo, new THREE.MeshStandardMaterial({ color: 0x8a7454, roughness: 0.75 }));
raw.position.x = -1.7;
const baked = new THREE.Mesh(geo, new THREE.MeshStandardMaterial({ map: colorTex, normalMap: normalTex, roughness: 0.6 }));
baked.position.x = 1.7;
scene.add(raw, baked);
const key = new THREE.DirectionalLight(0xffffff, 1.9); key.position.set(3, 4, 5); scene.add(key);
scene.add(new THREE.AmbientLight(0xffffff, 0.45));

function resize() { renderer.setSize(innerWidth, innerHeight); camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); }
addEventListener('resize', resize); resize();
renderer.setAnimationLoop((t) => {
  const r = t * 0.00026;
  raw.rotation.y = r; baked.rotation.y = r;
  camera.lookAt(0, 0, 0);
  renderer.render(scene, camera);
});

A high-poly sculpt with millions of polygons can’t go straight into anything with a real-time budget — a game, the web. Instead, the high-poly and low-poly versions get overlaid, and the difference between them — surface bumps, occlusion (AO), color — gets computed per pixel and “baked” as an image onto the low-poly’s UVs. The result is a set of textures: a normal map, an AO map, a color map.

The actual shape is then just the simple low-poly mesh, but the textures do the lighting math’s heavy lifting so it still reads as detailed. Baking is the normal-map and bump-vs-displacement entries’ “fake shading with a texture” principle, applied at production scale.

Blender has Cycles’ Bake panel; dedicated tools like Marmoset Toolbag or Substance Painter exist for baking alone. Common pitfalls: seams between UV islands showing a lighting mismatch unless padding is added around them, and a bake breaking down when the high-poly and low-poly silhouettes differ too much — usually fixed by adjusting the cage’s projection distance.

When to use

Almost always the step that moves a high-poly sculpt or scan into a low-poly asset that fits a real-time budget.