Learning
레슨 3 / 8·20분

조명과 애니메이션

Light — 장면에 빛 더하기

현실 세계처럼 3D 장면에서도 빛이 있어야 사물을 볼 수 있습니다. Three.js는 여러 종류의 조명을 제공합니다. AmbientLight는 전체를 균일하게 밝히고, DirectionalLight는 태양처럼 평행광을, PointLight는 전구처럼 한 점에서 퍼지는 빛을, SpotLight는 스포트라이트처럼 원뿔 형태의 빛을 만듭니다.

javascript
// AmbientLight — 방향 없이 장면 전체를 균일하게 비춤
const ambient = new THREE.AmbientLight(0xffffff, 0.3); // 색상, 강도
scene.add(ambient);

// DirectionalLight — 태양광처럼 평행한 빛
const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(5, 10, 7);
dirLight.castShadow = true; // 그림자 활성화
scene.add(dirLight);

// PointLight — 전구처럼 한 점에서 모든 방향으로 퍼지는 빛
const pointLight = new THREE.PointLight(0xff6600, 1, 20); // 색, 강도, 범위
pointLight.position.set(-3, 3, 2);
scene.add(pointLight);

// SpotLight — 원뿔 형태의 집중 조명
const spotLight = new THREE.SpotLight(0x00ff00, 1);
spotLight.position.set(0, 5, 0);
spotLight.angle = Math.PI / 6; // 원뿔 각도
spotLight.penumbra = 0.3;       // 경계 부드러움
scene.add(spotLight);

Animation — 움직이는 3D 세계

javascript
// requestAnimationFrame을 이용한 애니메이션 루프
const clock = new THREE.Clock();

function animate() {
  requestAnimationFrame(animate);

  const delta = clock.getDelta();   // 프레임 간 경과 시간(초)
  const elapsed = clock.getElapsedTime(); // 시작 이후 총 시간

  // 회전 애니메이션
  cubeMesh.rotation.x += 0.5 * delta;
  cubeMesh.rotation.y += 0.8 * delta;

  // 위아래 부유 애니메이션
  sphereMesh.position.y = 1 + Math.sin(elapsed * 2) * 0.5;

  // 크기 변화 애니메이션
  const scale = 1 + Math.sin(elapsed * 3) * 0.2;
  cylinderMesh.scale.set(scale, scale, scale);

  renderer.render(scene, camera);
}

animate(); // 루프 시작
  • AmbientLight — 기본 밝기 확보용, 단독 사용 시 입체감 없음
  • DirectionalLight — 그림자 표현이 가능하며 야외 씬에 적합
  • PointLight — 실내 조명, 불꽃 등 점광원 표현
  • SpotLight — 무대 조명, 손전등 등 집중 조명 표현
  • clock.getDelta() — 프레임 독립적 애니메이션을 위해 경과 시간 사용
javascript
// OrbitControls — 마우스로 카메라를 회전/줌/이동
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;  // 부드러운 감속
controls.dampingFactor = 0.05;
controls.maxPolarAngle = Math.PI / 2; // 바닥 아래로 못 가게

function animate() {
  requestAnimationFrame(animate);
  controls.update(); // 매 프레임 업데이트 필수
  renderer.render(scene, camera);
}
💡

delta 시간을 사용하면 60fps든 30fps든 동일한 속도로 애니메이션이 재생됩니다. rotation.y += 1 * delta처럼 항상 delta를 곱하세요. OrbitControls의 enableDamping을 사용하면 반드시 animate 루프 안에서 controls.update()를 호출해야 합니다.