Bump vs. displacement

범프 vs 디스플레이스먼트

Both read the same grayscale height texture, but bump mapping only fakes the shading while displacement actually pushes the geometry’s vertices.

Also known as: Bump mapDisplacement mapHeight map
···
html
<div class="sub-caps"><span>bump map · silhouette unchanged</span><span>displacement · silhouette changes</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(0x0b0b14);
const camera = new THREE.PerspectiveCamera(42, 1, 0.1, 100);
camera.position.set(0, 0.5, 6.4);

const cv = document.createElement('canvas');
cv.width = cv.height = 128;
const cx = cv.getContext('2d');
cx.fillStyle = '#777'; cx.fillRect(0, 0, 128, 128);
for (let i = 0; i < 26; i++) {
  const x = Math.random() * 128, y = Math.random() * 128, r = 6 + Math.random() * 10;
  const g = cx.createRadialGradient(x, y, 0, x, y, r);
  g.addColorStop(0, '#fff'); g.addColorStop(1, 'rgba(119,119,119,0)');
  cx.fillStyle = g; cx.beginPath(); cx.arc(x, y, r, 0, Math.PI * 2); cx.fill();
}
const tex = new THREE.CanvasTexture(cv);

const bumpSphere = new THREE.Mesh(
  new THREE.SphereGeometry(1.05, 64, 64),
  new THREE.MeshStandardMaterial({ color: 0x8ecbff, bumpMap: tex, bumpScale: 0.06, roughness: 0.55 })
);
bumpSphere.position.x = -1.7;
const dispSphere = new THREE.Mesh(
  new THREE.SphereGeometry(1.05, 128, 128),
  new THREE.MeshStandardMaterial({ color: 0xffb648, displacementMap: tex, displacementScale: 0.34, roughness: 0.55 })
);
dispSphere.position.x = 1.7;
scene.add(bumpSphere, dispSphere);
const key = new THREE.DirectionalLight(0xffffff, 1.8); 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.00028;
  bumpSphere.rotation.y = r; dispSphere.rotation.y = r;
  camera.lookAt(0, 0, 0);
  renderer.render(scene, camera);
});

A bump map is the simpler ancestor of the normal map — it computes a tilt on the fly from a grayscale height texture’s brightness changes and feeds only that into shading. Not a single vertex moves, so the object’s silhouette stays perfectly smooth from the side no matter how bumpy it looks head-on.

A displacement map reads the same grayscale texture, but adds its value to the vertex positions themselves instead of the shading. That needs enough vertices to actually move — a densely segmented mesh — so it costs far more than bump mapping. In exchange, the silhouette genuinely gets bumpy, and shadows fall the way real geometry would.

Blender and C4D expose these as separate “Bump” and “Displace” inputs on a material node, and real-time renderers handle displacement with GPU tessellation or a vertex shader. When there’s no close-up and performance matters, bump is enough; once the camera gets close to the surface or the silhouette matters, displacement is what you need.

When to use

Pick displacement when a close-up shot or the silhouette matters; pick bump when the surface sits in the background and performance comes first.