본문 바로가기
Graphic/ThreeJS

14 조명(Light)

by curious week 2025. 7. 26.

Three.js 조명(Light) 가이드

이 문서는 Three.js에서 사용할 수 있는 다양한 조명 유형과 특징, 성능, 도우미 사용법까지 정리한 참고 자료입니다. 실습 예제와 함께 주요 조명 클래스들을 설명합니다.


1. 기본 개념

Three.js에서 조명을 추가하는 방법은 간단합니다.

const light = new THREE.SomeLightType(color, intensity)
scene.add(light)
  • 다양한 조명 타입마다 고유의 속성과 시각적 효과가 있습니다.
  • MeshStandardMaterial이나 MeshPhysicalMaterial과 같이 조명 반응형 머티리얼을 사용해야 효과를 확인할 수 있습니다.

2. 주요 조명 타입

AmbientLight (환경광)

const ambientLight = new THREE.AmbientLight(0xffffff, 1)
scene.add(ambientLight)
  • 장면 전체에 균일하게 빛을 줍니다.
  • 방향, 거리, 위치 개념 없음 (전방향).
  • 성능 부담 거의 없음 (저비용 조명).
  • 재질에 색이 적용되지만 입체감은 없음.
  • 실제 조명의 반사를 흉내 낼 때 유용.
ambientLight.color = new THREE.Color(0xffffff)
ambientLight.intensity = 0.5

☀️ DirectionalLight (태양광)

const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
directionalLight.position.set(1, 0.25, 0)
scene.add(directionalLight)
  • 태양처럼 평행 광선을 시뮬레이션
  • 위치를 설정해 빛 방향 지정
  • 거리는 영향을 미치지 않음
  • 성능 부담: 중간

☁️ HemisphereLight (하늘 + 땅)

const hemisphereLight = new THREE.HemisphereLight(0x0000ff, 0xff0000, 0.6)
scene.add(hemisphereLight)
  • 위쪽은 skyColor, 아래쪽은 groundColor로 나뉨
  • 자연광 느낌을 줄 수 있음
  • 성능 부담 거의 없음

💡 PointLight (전구)

const pointLight = new THREE.PointLight(0xff9000, 1.5, 0, 2)// 색상, 밝기, 거리, 제한, 감쇠도
pointLight.position.set(1, -0.5, 1)
scene.add(pointLight)
  • 한 점에서 모든 방향으로 퍼지는 조명
  • distance: 빛이 영향을 미치는 거리 (0이면 무한)
  • decay: 빛이 줄어드는 감쇠 비율 (기본 2)

🟪 RectAreaLight (면광원)

const rectAreaLight = new THREE.RectAreaLight(0x4e00ff, 6, 1, 1)//색상, 밝기, 가로, 세로
rectAreaLight.position.set(-1.5, 0, 1.5)
rectAreaLight.lookAt(new THREE.Vector3())
scene.add(rectAreaLight)
  • 사진 스튜디오 조명과 유사
  • 넓은 면에서 부드러운 빛을 방출
  • MeshStandardMaterial과 MeshPhysicalMaterial에서만 동작
  • 성능 부담 높음

※ 사용 전 RectAreaLightUniformsLib.init() 필요


🔦 SpotLight (손전등)

const spotLight = new THREE.SpotLight(0x78ff00, 4.5, 10, Math.PI * 0.1, 0.25, 1)
// 색상, 밝기, 거리, 각도, 경계, 감쇠도
spotLight.position.set(0, 2, 3)
scene.add(spotLight)

spotLight.target.position.x = -0.75
scene.add(spotLight.target)
  • 특정 방향으로 원뿔 형태의 조명을 쏨
  • angle: 원뿔의 크기
  • penumbra: 테두리 부드러움
  • target: 조명이 바라볼 대상 객체 (반드시 scene에 추가)

3. 성능 비용 가이드

AmbientLight 모든 방향에서 모든 오브젝트에 고르게 조명 성능 비용 매우 낮음 (최고 효율) - 그림자 없음- 실시간 반사나 방향 정보 없음- 장면 전체에 은은한 기본 조명 제공
HemisphereLight 하늘색 + 바닥색으로 분리된 방향 없는 조명 성능 비용 매우 낮음 - 자연광 흉내에 적합- 실내/실외 구분 없는 전체 광원
DirectionalLight 태양과 같이 무한 원거리에서 동일한 방향으로 비추는 조명 성능 비용 낮음 ~ 중간 - 그림자 가능- 위치는 방향을 위한 참고일 뿐, 거리에 무관함- 그림자 계산 시 성능 비용 증가
PointLight 모든 방향으로 퍼지는 점광원 (전구처럼 작동) 성능 비용 중간 - 거리 기반 감쇠(distance, decay)- 그림자 가능- 복수 개 사용 시 GPU 부하 증가
SpotLight 특정 방향으로 원뿔 모양의 빛을 비추는 조명 (손전등) 성능 비용 높음 - 방향성과 범위 모두 가짐- 그림자 계산이 복잡 (특히 penumbra 포함 시)- 타겟 객체 필요
RectAreaLight 직사각형 형태로 확산되는 면광원 성능 비용 매우 높음 - 실내 조명, 광고판, 창문 빛 표현- MeshStandardMaterial 또는 MeshPhysicalMaterial에서만 작동- RectAreaLightUniformsLib 등록 필요
LightProbe 주변 환경에서 조도를 샘플링하여 전체에 반영하는 라이트 (간접광) 성능 비용 낮음 - 주로 PMREMGenerator나 환경 맵 기반 Lighting에 사용- 직접 조명 아님 (reflections 중심)
AmbientLightProbe / HemisphereLightProbe LightProbe의 확장 (PBR과 통합된 라이트) 성능 비용 낮음 - 사실적인 간접광 표현용- 직접적으로 씬을 밝히지는 않음
저비용 AmbientLight, HemisphereLight
중간 비용 DirectionalLight, PointLight
고비용 SpotLight, RectAreaLight

필요한 경우가 아니라면 저비용의 조명을 먼저 사용


4. 베이킹 (조명 텍스처화)

  • 조명을 미리 텍스처에 저장해 실시간 계산 없이 조명 효과 제공
  • 주로 Blender 같은 외부 도구에서 수행
  • 실시간 성능은 높지만 조명 변경 불가
  • 예: Three.js Journey 포털 챕터

5. 조명 도우미 (Helpers)

조명의 위치와 방향을 시각화하기 위한 도우미 객체입니다.

const hemisphereLightHelper = new THREE.HemisphereLightHelper(hemisphereLight, 0.2)
scene.add(hemisphereLightHelper)

const directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight, 0.2)
scene.add(directionalLightHelper)

const pointLightHelper = new THREE.PointLightHelper(pointLight, 0.2)
scene.add(pointLightHelper)

const spotLightHelper = new THREE.SpotLightHelper(spotLight)
scene.add(spotLightHelper)

RectAreaLightHelper

import { RectAreaLightHelper } from 'three/examples/jsm/helpers/RectAreaLightHelper.js'

const rectAreaLightHelper = new RectAreaLightHelper(rectAreaLight)
scene.add(rectAreaLightHelper)

 three/examples에서 따로 불러와야 함


6. 요약

  • AmbientLight: 전체 조명
  • HemisphereLight: 하늘/땅 색상 분리 조명
  • DirectionalLight: 태양광 느낌
  • PointLight: 라이트 전구처럼
  • RectAreaLight: 확산 면광원 (비싼 성능)
  • SpotLight: 손전등처럼 방향 원뿔

적절한 조명 조합으로 씬의 분위기와 사실감을 조절하세요.


import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import GUI from 'lil-gui';
import { RectAreaLightHelper } from 'three/examples/jsm/helpers/RectAreaLightHelper.js';

/**
 * Base
 */
// Debug
const gui = new GUI();

// Canvas
const canvas = document.querySelector('canvas.webgl');

// Scene
const scene = new THREE.Scene();

/**
 * Lights
 */
// # AmbientLight
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
// ambientLight.color = new THREE.Color(0xffffff);
// ambientLight.intensity = 0.5;
scene.add(ambientLight);
// gui.add(ambientLight, 'intensity').min(0).max(3).step(0.01);

// # DirectionalLight
const directionalLight = new THREE.DirectionalLight(0xfffffc, 0.9);
directionalLight.position.set(1, 0.25, 0);
scene.add(directionalLight);

// # HemisphereLight
const hemisphereLight = new THREE.HemisphereLight(0xff0000, 0x0000ff, 0.9);
scene.add(hemisphereLight);

// # PointLight
const pointLight = new THREE.PointLight(0xffffff, 1.5, 10, 2);
pointLight.position.set(1, -0.5, 1);
scene.add(pointLight);

// # ReactAreaLight
const rectAreaLight = new THREE.RectAreaLight(0x4e00ff, 6, 1, 1);
rectAreaLight.position.set(-1.5, 0, 1.5); // 위치
rectAreaLight.lookAt(new THREE.Vector3()); // 회전
scene.add(rectAreaLight);

// # SpotLight
const spotLight = new THREE.SpotLight(
  0x78ff00,
  4.5, // 밝기
  10, // 거리
  Math.PI * 0.1, // 각도
  0.25, // 경계 (0이면 경계가 분명하게 나눠짐)
  1, // 너무 작게 설정하면 거리감 없이 쭉 밝음
);
spotLight.position.set(0, 2, 3);
scene.add(spotLight);

spotLight.target.position.x = -1.75; // (조명이 바라볼 대상 )
scene.add(spotLight.target);

// Helper
const hemisphereLightHelper = new THREE.HemisphereLightHelper(
  hemisphereLight,
  0.2,
);
scene.add(hemisphereLightHelper);

const directionalLightHelper = new THREE.DirectionalLightHelper(
  directionalLight,
  0.2,
);
scene.add(directionalLightHelper);

const pointLightHelper = new THREE.PointLightHelper(pointLight, 0.2);
scene.add(pointLightHelper);

const spotLightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotLightHelper);

// three/examples에서 따로 불러와야 함
const rectAreaLightHelper = new RectAreaLightHelper(rectAreaLight);
scene.add(rectAreaLightHelper);

/**
 * Objects
 */
// Material
const material = new THREE.MeshStandardMaterial();
material.roughness = 0.4;

// Objects
const sphere = new THREE.Mesh(new THREE.SphereGeometry(0.5, 32, 32), material);
sphere.position.x = -1.5;

const cube = new THREE.Mesh(new THREE.BoxGeometry(0.75, 0.75, 0.75), material);

const torus = new THREE.Mesh(
  new THREE.TorusGeometry(0.3, 0.2, 32, 64),
  material,
);
torus.position.x = 1.5;

const plane = new THREE.Mesh(new THREE.PlaneGeometry(5, 5), material);
plane.rotation.x = -Math.PI * 0.5;
plane.position.y = -0.65;

scene.add(sphere, cube, torus, plane);

/**
 * Sizes
 */
const sizes = {
  width: window.innerWidth,
  height: window.innerHeight,
};

window.addEventListener('resize', () => {
  // Update sizes
  sizes.width = window.innerWidth;
  sizes.height = window.innerHeight;

  // Update camera
  camera.aspect = sizes.width / sizes.height;
  camera.updateProjectionMatrix();

  // Update renderer
  renderer.setSize(sizes.width, sizes.height);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});

/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(
  75,
  sizes.width / sizes.height,
  0.1,
  100,
);
camera.position.x = 1;
camera.position.y = 1;
camera.position.z = 2;
scene.add(camera);

// Controls
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
  canvas: canvas,
});
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

/**
 * Animate
 */
const clock = new THREE.Clock();

const tick = () => {
  const elapsedTime = clock.getElapsedTime();

  // Update objects
  sphere.rotation.y = 0.1 * elapsedTime;
  cube.rotation.y = 0.1 * elapsedTime;
  torus.rotation.y = 0.1 * elapsedTime;

  sphere.rotation.x = 0.15 * elapsedTime;
  cube.rotation.x = 0.15 * elapsedTime;
  torus.rotation.x = 0.15 * elapsedTime;

  // Update controls
  controls.update();

  // Render
  renderer.render(scene, camera);

  // Call tick again on the next frame
  window.requestAnimationFrame(tick);
};

tick();

'Graphic > ThreeJS' 카테고리의 다른 글

16 Haunted House 프로젝트  (4) 2025.07.26
15 그림자(Shadow)  (4) 2025.07.26
13 Go Live  (1) 2025.07.23
12 텍스트(3D Text)  (1) 2025.07.23
11 머티리얼(Material)  (1) 2025.07.23