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 |