Three.js 디버그 UI - lil-gui 사용법
1. 디버그 UI의 중요성
창의적인 프로젝트에서 핵심은 빠르고 유연하게 수정할 수 있어야 한다는 점입니다. 디자이너나 클라이언트가 실시간으로 색상, 위치, 속도 등을 조절할 수 있어야 더 나은 결과를 얻을 수 있습니다.
2. lil-gui 소개 (vs dat.GUI)
디버그 UI 종류: dat.GUI / lil-gui / control-panel / ControlKit / Uil / Tweakpane / Guify / Oui
기존의 dat.GUI는 유지보수 부족으로 lil-gui가 대체하게 되었습니다. lil-gui는 작고 가볍고, 사용성이 좋으며 업데이트도 잘 이루어지고 있습니다.
3. 설치 및 초기 설정
npm install lil-gui
import GUI from 'lil-gui';
const gui = new GUI();
4. 기본 사용법
- Range 범위 - 최소값과 최대값을 갖는 숫자
- Color 색상 - 다양한 형식의 색상에 대해
- Text 텍스트 - 간단한 텍스트의 경우
- Checkbox 체크박스 —부울( true또는 false) 용
- Select 선택 - 값 목록에서 선택
- Button 버튼 - 기능을 트리거합니다
숫자 조절
gui.add(mesh.position, 'y', -3, 3, 0.01);
- .min(), .max(), .step() 으로 체이닝 가능
- .name()으로 라벨 설정
불리언 (체크박스)
gui.add(mesh, 'visible');
색상
const debugObject = {
color: '#ff0000',
};
gui.addColor(debugObject, 'color').onChange(() => {
material.color.set(debugObject.color);
});
함수 호출 버튼
const debugObject = {
spin: () => {
gsap.to(mesh.rotation, { y: mesh.rotation.y + Math.PI * 2, duration: 1 });
},
};
gui.add(debugObject, 'spin');
5. 동적 geometry 재생성
BoxGeometry의 subdivision 값을 동적으로 조정하려면 다음과 같이 처리합니다.
debugObject.subdivision = 2;
gui.add(debugObject, 'subdivision', 1, 20, 1).onFinishChange(() => {
mesh.geometry.dispose();
mesh.geometry = new THREE.BoxGeometry(
1,
1,
1,
debugObject.subdivision,
debugObject.subdivision,
debugObject.subdivision,
);
});
6. 폴더로 정리
const cubeFolder = gui.addFolder('Cube Controls');
cubeFolder.add(mesh, 'visible');
cubeFolder.add(material, 'wireframe');
7. GUI 설정 옵션
const gui = new GUI({
width: 300,
title: '디버그 UI',
closeFolders: false,
});
닫기/숨기기/보이기
gui.hide();
window.addEventListener('keydown', (event) => {
if (event.key === 'h') {
gui.show(gui._hidden);
}
});
8. 팁
- debugObject를 잘 활용하면 색상, 위치, 기능 등을 깔끔하게 관리할 수 있습니다.
- geometry 재생성 시 .dispose()를 반드시 호출해 메모리 누수 방지
- 프로젝트 시작부터 디버그 UI를 설정해두면 유지 관리가 쉬워집니다.
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import gsap from 'gsap';
import GUI from 'lil-gui';
/**
* Debug
*/
// Object Property만 수정 가능
const gui = new GUI();
/**
* Base
*/
// Canvas
const canvas = document.querySelector('canvas.webgl');
// Scene
const scene = new THREE.Scene();
/**
* Object
*/
const geometry = new THREE.BoxGeometry(1, 1, 1, 2, 2, 2);
const material = new THREE.MeshBasicMaterial({ color: '#5691a3' });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// gui.add(mesh.position, 'y').min(-3).max(3).step(0.01).name('elevation y');
gui.add(mesh.position, 'y', -3, 3, 0.01).name('elevation y'); // y축 위치 조절 (위와 동일)
gui.add(mesh, 'visible'); // 끄고 켜기
// gui.add(material, 'wireframe');
gui.add(mesh.material, 'wireframe'); // 와이어 프레임 켜기 (위와 동일)
// gui.addColor(material, 'color'); // 색상 변경
gui.addColor(material, 'color').onChange((value) => {
// 생상 값 얻기
console.log('value has changed :', material.color.getHexString());
console.log('material.color === ', value.getHexString());
});
/*
동작하지 않음.
let myVariable = 1337;
gui.add(myVariable, 'myVariable');
아래처럼 같은 이름을 가진 객체 형태만 사용할 수 있다. ( 현재 기능은 없음. )
*/
const myObject = {
myVariable: 1337,
};
gui.add(myObject, 'myVariable'); // 기능 없음.
/**
* 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 controls
controls.update();
// Render
renderer.render(scene, camera);
// Call tick again on the next frame
window.requestAnimationFrame(tick);
};
tick();
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import gsap from 'gsap';
import GUI from 'lil-gui';
/**
* Debug
*/
const gui = new GUI({
width: 300, // gui 창 넓이
title: 'Nice debug UI', // 이름 지정
closeFolders: false, // 최초 폴더 열림 여부
});
// gui.close();
gui.hide();
// h 키로 gui 보기/숨기기
window.addEventListener('keydown', (event) => {
if (event.key == 'h') {
gui.show(gui._hidden);
}
});
const debugObject = {};
/**
* Base
*/
// Canvas
const canvas = document.querySelector('canvas.webgl');
// Scene
const scene = new THREE.Scene();
/**
* Object
*/
debugObject.color = '#5691a3';
const geometry = new THREE.BoxGeometry(1, 1, 1, 2, 2, 2);
const material = new THREE.MeshBasicMaterial({
color: debugObject.color,
wireframe: true,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
const cubTweaks = gui.addFolder('Awesome cube'); // 하나의 폴더에 담기
cubTweaks.close(); // 호출 시 기본값 폴더 닫음.
cubTweaks.add(mesh.position, 'y').min(-3).max(3).step(0.01).name('elevation y');
cubTweaks.add(mesh, 'visible'); // 끄고 켜기
cubTweaks.add(mesh.material, 'wireframe'); // 와이어 프레임 켜기 (위와 동일)
cubTweaks.addColor(debugObject, 'color').onChange(() => {
material.color.set(debugObject.color);
});
// 객체 회전 시키기
debugObject.spin = () => {
gsap.to(mesh.rotation, { y: mesh.rotation.y - Math.PI * 2 });
};
cubTweaks.add(debugObject, 'spin');
debugObject.subdivision = 2;
cubTweaks
.add(debugObject, 'subdivision')
.min(1)
.max(20)
.step(1)
.onFinishChange(() => {
// onChange는 값이 너무 많이 변해서 GPU memory 성능저하를 일으킬 수 있음.
mesh.geometry.dispose(); // 이전 값과 대치
mesh.geometry = new THREE.BoxGeometry(
1,
1,
1,
debugObject.subdivision,
debugObject.subdivision,
debugObject.subdivision,
);
});
/**
* 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 controls
controls.update();
// Render
renderer.render(scene, camera);
// Call tick again on the next frame
window.requestAnimationFrame(tick);
};
tick();'Graphic > ThreeJS' 카테고리의 다른 글
| 11 머티리얼(Material) (1) | 2025.07.23 |
|---|---|
| 10 텍스처(Texture) (3) | 2025.07.23 |
| 08 Three.js 기하학 구조 및 다양한 지오메트리(geometries) 정리 (1) | 2025.07.23 |
| 07 반응형 캔버스와 전체 화면 모드 설정 가이드 (2) | 2025.07.22 |
| 06 Three.js 카메라 타입 및 OrbitControls (3) | 2025.07.22 |