돌리 줌

Dolly zoom

피사체 크기는 그대로 두고 카메라가 뒤로 빠지며 동시에 화각(FOV)을 좁혀, 배경만 늘어나 보이게 만드는 촬영 기법. 히치콕의 <현기증>에서 유래했습니다.

다른 이름: Vertigo effectZolly shotTrombone shot
···
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(0x0a0a12);
const FOV0 = 50;
const camera = new THREE.PerspectiveCamera(FOV0, 1, 0.1, 100);

scene.add(new THREE.AmbientLight(0x556080, 1.4));
const dLight = new THREE.DirectionalLight(0xffffff, 1.3);
dLight.position.set(2, 4, 3);
scene.add(dLight);

const subject = new THREE.Mesh(
  new THREE.CapsuleGeometry(0.5, 1.1, 8, 16),
  new THREE.MeshStandardMaterial({ color: 0xff6a4d, roughness: 0.5 })
);
subject.position.set(0, 0.85, 0);
scene.add(subject);

const corridor = new THREE.Group();
const N = 8;
for (let i = 1; i <= N; i++) {
  const z = -i * 2.4;
  const h = 2.2 + (i % 3) * 0.6;
  const mat = new THREE.MeshStandardMaterial({ color: new THREE.Color().setHSL(0.58, 0.35, 0.22 + i * 0.02), roughness: 0.8 });
  const left = new THREE.Mesh(new THREE.BoxGeometry(0.5, h, 0.5), mat);
  left.position.set(-2.4, h / 2 - 0.4, z);
  corridor.add(left);
  const right = new THREE.Mesh(new THREE.BoxGeometry(0.5, h, 0.5), mat.clone());
  right.position.set(2.4, h / 2 - 0.4, z);
  corridor.add(right);
}
scene.add(corridor);

const floor = new THREE.Mesh(
  new THREE.PlaneGeometry(6, N * 2.4 + 4),
  new THREE.MeshStandardMaterial({ color: 0x14141f, roughness: 1 })
);
floor.rotation.x = -Math.PI / 2;
floor.position.set(0, -0.4, -N * 1.2);
scene.add(floor);

const baseDist = 4.2;
const baseFovRad = THREE.MathUtils.degToRad(FOV0);
const k = baseDist * Math.tan(baseFovRad / 2);

function resize() { renderer.setSize(innerWidth, innerHeight); camera.aspect = innerWidth / innerHeight; camera.updateProjectionMatrix(); }
addEventListener('resize', resize); resize();

renderer.setAnimationLoop((t) => {
  const time = t * 0.0006;
  const dist = baseDist + (Math.sin(time) * 0.5 + 0.5) * 6.5;
  const fovRad = 2 * Math.atan(k / dist);
  camera.fov = THREE.MathUtils.radToDeg(fovRad);
  camera.position.set(0, 0.85, dist);
  camera.lookAt(0, 0.85, 0);
  camera.updateProjectionMatrix();
  subject.rotation.y = time * 0.4;
  renderer.render(scene, camera);
});

카메라가 피사체로부터 멀어지면 원래는 피사체가 작아지고, 렌즈를 당겨(줌인) 화각을 좁히면 피사체가 다시 커집니다. 두 움직임의 속도를 정확히 맞추면 피사체 크기는 화면에서 일정하게 유지되지만, 배경은 화각이 좁아진 만큼 확대되어 마치 늘어나거나 빨려 들어가는 것처럼 보입니다.

물리적으로는 거리(distance) × tan(화각/2)가 일정하게 유지되도록 두 값을 반대로 움직이면 됩니다. three.js에서는 매 프레임 camera.position.z 를 옮기면서 camera.fov 를 그 공식으로 다시 계산하고 camera.updateProjectionMatrix() 를 호출해야 합니다.

이 효과는 어지러움·불안·현실 감각의 붕괴를 표현할 때 씁니다. 실사 촬영에서는 트랙 위에서 카메라를 물리적으로 밀고 당겨야 해서 '돌리 줌' 또는 '트롬본 샷'이라 부릅니다.

언제 쓰나

충격적인 깨달음이나 불안한 순간을 강조하는 씬 전환에. 남용하면 우스꽝스러워지니 한 장면에 한 번 정도만.