Three.js 머티리얼(Material)
개요
- 머티리얼(Material)은 메시(mesh)의 기하 구조(geometry)에 시각적 속성을 부여하는 요소
- 실제 색상은 셰이더(shader)라는 GPU 프로그램에 의해 계산됨
- 셰이더는 직접 작성할 수 있으나 Three.js는 다양한 내장 머티리얼을 제공함
- 이 문서에서는 주요 머티리얼을 실습하며 각 속성과 사용법을 정리
기본 설정
// 재질 생성
const material = new THREE.MeshBasicMaterial();
// 메시 3개 생성 및 재질 공유
const sphere = new THREE.Mesh(new THREE.SphereGeometry(0.5, 16, 16), material);
sphere.position.x = -1.5;
const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material);
const torus = new THREE.Mesh(
new THREE.TorusGeometry(0.3, 0.2, 16, 32),
material,
);
torus.position.x = 1.5;
scene.add(sphere, plane, torus);
애니메이션 (회전)
const clock = new THREE.Clock();
const tick = () => {
const elapsedTime = clock.getElapsedTime();
sphere.rotation.x = -0.15 * elapsedTime;
plane.rotation.x = -0.15 * elapsedTime;
torus.rotation.x = -0.15 * elapsedTime;
sphere.rotation.y = 0.1 * elapsedTime;
plane.rotation.y = 0.1 * elapsedTime;
torus.rotation.y = 0.1 * elapsedTime;
renderer.render(scene, camera);
requestAnimationFrame(tick);
};
텍스처 로드
const textureLoader = new THREE.TextureLoader();
const doorColorTexture = textureLoader.load('./textures/door/color.jpg');
const doorAlphaTexture = textureLoader.load('./textures/door/alpha.jpg');
const doorAmbientOcclusionTexture = textureLoader.load(
'./textures/door/ambientOcclusion.jpg',
);
const doorHeightTexture = textureLoader.load('./textures/door/height.jpg');
const doorNormalTexture = textureLoader.load('./textures/door/normal.jpg');
const doorMetalnessTexture = textureLoader.load(
'./textures/door/metalness.jpg',
);
const doorRoughnessTexture = textureLoader.load(
'./textures/door/roughness.jpg',
);
const matcapTexture = textureLoader.load('./textures/matcaps/1.png');
const gradientTexture = textureLoader.load('./textures/gradients/3.jpg');
doorColorTexture.colorSpace = THREE.SRGBColorSpace;
matcapTexture.colorSpace = THREE.SRGBColorSpace;
주요 머티리얼 종류
1. MeshBasicMaterial
- 가장 단순한 재질 (빛의 영향을 받지 않음)
- 주요 속성: color, map, wireframe, opacity, alphaMap, side
const material = new THREE.MeshBasicMaterial({ map: doorColorTexture });
material.transparent = true;
material.opacity = 0.5;
material.alphaMap = doorAlphaTexture;
material.side = THREE.DoubleSide;
2. MeshNormalMaterial
- 표면의 노멀 벡터를 색상으로 시각화 (디버깅 용도)
- flatShading 옵션으로 정점을 기준으로 면을 평평하게 표현 가능
const material = new THREE.MeshNormalMaterial();
material.flatShading = true;
3. MeshMatcapMaterial
- 일반적인 방향 기반으로 matcap 텍스처를 사용하는 재질
- 라이팅 없이도 사실적인 효과 제공
- Matcap 리스트
const material = new THREE.MeshMatcapMaterial();
material.matcap = matcapTexture;
4. MeshDepthMaterial
- 카메라 거리 기반 밝기 표현 (가까울수록 밝음)
- 주로 그림자 계산 등에 사용됨
const material = new THREE.MeshDepthMaterial();
5. MeshLambertMaterial
- 조명을 받는 기본 재질 (성능 좋음, 품질 낮음)
const material = new THREE.MeshLambertMaterial();
const ambientLight = new THREE.AmbientLight(0xffffff, 1);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 30);
pointLight.position.set(2, 3, 4);
scene.add(pointLight);
6. MeshPhongMaterial
- 반사와 specular 하이라이트 지원
- 속성: shininess, specular
const material = new THREE.MeshPhongMaterial();
material.shininess = 100;
material.specular = new THREE.Color(0x1188ff);
7. MeshToonMaterial
- 만화 스타일 재질 (셰이딩 단계를 줄여 간단한 효과 제공)
gradientTexture.minFilter = THREE.NearestFilter;
gradientTexture.magFilter = THREE.NearestFilter;
gradientTexture.generateMipmaps = false;
const material = new THREE.MeshToonMaterial();
material.gradientMap = gradientTexture;
8. MeshStandardMaterial
- PBR(Physically Based Rendering) 지원
- 속성: metalness, roughness, aoMap, displacementMap, normalMap, alphaMap
const material = new THREE.MeshStandardMaterial();
material.metalness = 0.45;
material.roughness = 0.65;
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
const rgbeLoader = new RGBELoader();
rgbeLoader.load('./textures/environmentMap/2k.hdr', (envMap) => {
envMap.mapping = THREE.EquirectangularReflectionMapping;
scene.background = envMap;
scene.environment = envMap;
});
9. MeshPhysicalMaterial
- MeshStandardMaterial 확장 버전
- 고급 속성:
- clearcoat, clearcoatRoughness
- sheen, sheenRoughness, sheenColor
- iridescence, iridescenceIOR, iridescenceThicknessRange
- transmission, ior, thickness
const material = new THREE.MeshPhysicalMaterial();
material.clearcoat = 1;
material.sheen = 1;
material.iridescence = 1;
material.transmission = 1;
material.ior = 1.5;
디버그 UI (lil-gui)
import GUI from 'lil-gui';
const gui = new GUI();
gui.add(material, 'metalness').min(0).max(1).step(0.0001);
gui.add(material, 'roughness').min(0).max(1).step(0.0001);
재질 이름 | 특징 | 조명 필요 | 성능
| MeshBasicMaterial |
단순한 텍스처/색상 |
❌ |
★★★★★ |
| MeshNormalMaterial |
법선 디버깅 |
❌ |
★★★★★ |
| MeshMatcapMaterial |
빠르고 사실적인 스타일 |
❌ |
★★★★★ |
| MeshDepthMaterial |
깊이 시각화 |
❌ |
★★★★☆ |
| MeshLambertMaterial |
조명 반응 O |
✅ |
★★★★☆ |
| MeshPhongMaterial |
반사 효과 |
✅ |
★★★☆☆ |
| MeshToonMaterial |
만화 느낌 |
✅ |
★★★☆☆ |
| MeshStandardMaterial |
PBR 기반 |
✅ |
★★☆☆☆ |
| MeshPhysicalMaterial |
고급 PBR (유리, 광택 등) |
✅ |
★☆☆☆☆ |
참고 링크
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import GUI from 'lil-gui';
import { RGBELoader } from 'three/examples/jsm/Addons.js';
/**
* Debug
*/
const gui = new GUI();
/**
* Base
*/
// Canvas
const canvas = document.querySelector('canvas.webgl');
// Scene
const scene = new THREE.Scene();
/**
* Texture
*/
const textureLoader = new THREE.TextureLoader();
const doorColorTexture = textureLoader.load('./textures/door/color.jpg');
const doorAlphaTexture = textureLoader.load('./textures/door/alpha.jpg');
const doorAmbientOcclusionTexture = textureLoader.load(
'./textures/door/ambientOcclusion.jpg',
);
const doorHeightTexture = textureLoader.load('./textures/door/height.jpg');
const doorNormalTexture = textureLoader.load('./textures/door/normal.jpg');
const doorMetalnessTexture = textureLoader.load(
'./textures/door/metalness.jpg',
);
const doorRoughnessTexture = textureLoader.load(
'./textures/door/roughness.jpg',
);
const matcapTexture = textureLoader.load('./textures/matcaps/8.png');
const gradientTexture = textureLoader.load('./textures/gradients/3.jpg');
doorColorTexture.colorSpace = THREE.SRGBColorSpace;
matcapTexture.colorSpace = THREE.SRGBColorSpace;
/**
* Object
*/
// 1. MeshBasicMaterial - 가장 기본적인 머티리얼, 조명 영향 없음
// const material = new THREE.MeshBasicMaterial({ map: doorColorTexture });
const material = new THREE.MeshBasicMaterial();
material.map = doorColorTexture; // 텍스처 설정
material.color = new THREE.Color(0xff0000); // 색상 (hex, rgb 등 가능)
material.wireframe = true; // 와이어 프레임 true/false
material.transparent = true; // 투명도 활성화
material.opacity = 0.2; // 0 (완전 투명) ~ 1 (완전 불투명), 적용하기 위해서 transparent를 써줘야 함.
material.alphaMap = doorAlphaTexture; // 알파 텍스처
/* FrontSide(Default) / BackSide / DoubleSide(성능 저하 가능성 있음.) */
material.side = THREE.DoubleSide;
// 2. MeshNormalMaterial - 노멀을 색상으로 시각화
const material = new THREE.MeshNormalMaterial();
material.flatShading = true; // 면을 평평하게 표현
// 3. MeshMatcapMaterial - 미리 계산된 라이팅 표현
const material = new THREE.MeshMatcapMaterial();
material.matcap = matcapTexture; // map 보다 자연스러움.
// 4. MeshDepthMaterial - 깊이 기반 흑백 머티리얼 (조명 영향 없음)
const material = new THREE.MeshDepthMaterial();
// 5. MeshLambertMaterial - 기본적인 조명 반응 (성능 우수, 물건이 많으면 현실감이 떨어진다.)
const material = new THREE.MeshLambertMaterial();
material.color = new THREE.Color('#ffffff');
// 6. MeshPhongMaterial - 반사광 표현
const material = new THREE.MeshPhongMaterial();
material.shininess = 100; // 0 ~ 1000 (높을수록 반짝임 증가)
material.specular = new THREE.Color(0x1188ff); // 반사광 색상
// 7. MeshToonMaterial - 만화 느낌 표현
const material = new THREE.MeshToonMaterial();
gradientTexture.minFilter = THREE.NearestFilter;
gradientTexture.magFilter = THREE.NearestFilter;
gradientTexture.generateMipmaps = false;
material.gradientMap = gradientTexture; // 자연스럽게 생상을 연결함.
// 8. MeshStandardMaterial - 물리 기반 렌더링(PBR)
// (다른 3D 앱과 동일한 알고리즘을 사용하고, 보다 현실적인 출력값을 얻을 수 있다.)
const material = new THREE.MeshStandardMaterial();
material.metalness = 1; // 0 (비금속) ~ 1 (금속)
material.roughness = 1; // 0 (매끈) ~ 1 (거침)
material.map = doorColorTexture;
material.aoMap = doorAmbientOcclusionTexture;
material.aoMapIntensity = 1; // 0 ~ 10 (그림자 강도)
material.displacementMap = doorHeightTexture;
material.displacementScale = 0.1; // 0 ~ 1 (돌출 정도)
material.metalnessMap = doorMetalnessTexture;
material.roughnessMap = doorRoughnessTexture;
material.normalMap = doorNormalTexture;
material.normalScale.set(0.5, 0.5); // 범위 일반적으로 0 ~ 1
material.transparent = true;
material.alphaMap = doorAlphaTexture;
material.transparent = true;
material.alphaMap = doorAlphaTexture;
// gui.add(material, 'metalness').min(0).max(1).step(0.001);
// gui.add(material, 'roughness').min(0).max(1).step(0.001);
// 9. MeshPhysicalMaterial - MeshStandardMaterial 확장 + 고급 효과
const material = new THREE.MeshPhysicalMaterial();
material.metalness = 1; // 0 ~ 1
material.roughness = 1; // 0 ~ 1
material.map = doorColorTexture;
material.aoMap = doorAmbientOcclusionTexture;
material.aoMapIntensity = 1;
material.displacementMap = doorHeightTexture; // 면적 적용
material.displacementScale = 0.1; // 0 ~ 1
material.metalnessMap = doorMetalnessTexture;
material.roughnessMap = doorRoughnessTexture;
material.normalMap = doorNormalTexture;
material.normalScale.set(0.5, 0.5); // Vector2, 0 ~ 1 권장
material.transparent = true;
material.alphaMap = doorAlphaTexture;
gui.add(material, 'metalness').min(0).max(1).step(0.001);
gui.add(material, 'roughness').min(0).max(1).step(0.001);
// Clearcoat - 클리어코트(광택 층) 효과, 유리/광택 재질에 적합
material.clearcoat = 1; // 0 (없음) ~ 1 (최대 클리어코트)
material.clearcoatRoughness = 0; // 0 (매끈) ~ 1 (거칠게)
gui.add(material, 'clearcoat', 0, 1, 0.1);
gui.add(material, 'clearcoatRoughness', 0, 1, 0.1);
// Sheen - 직물이나 부드러운 표면에 적합한 광택 효과
material.sheen = 1; // 0 (없음) ~ 1 (최대 광택)
material.sheenRoughness = 0.25; // 0 (매끈한 광택) ~ 1 (퍼진 광택)
material.sheenColor.set(1, 1, 1); // 광택 색상 (기본 흰색)
gui.add(material, 'sheen', 0, 1, 0.1);
gui.add(material, 'sheenColor');
gui.add(material, 'sheenRoughness', 0, 1, 0.1);
// Iridescence - 각도에 따라 무지갯빛이 보이는 효과 (기름막, 비눗방울 등)
material.iridescence = 1; // 0 (없음) ~ 1 (완전한 무지갯빛 효과)
material.iridescenceIOR = 1; // 굴절률, 1.0 ~ 2.333 범위 추천
material.iridescenceThicknessRange = [100, 800]; // [최소, 최대 두께] 범위 설정
// iridescenceThicknessRange는 배열이므로 각각 따로 추가해야 함
gui.add(material, 'iridescence', 0, 1, 0.1);
gui.add(material, 'iridescenceIOR', 1, 2.333, 0.1);
gui.add(material.iridescenceThicknessRange, '0', 1, 1000, 1); // 최소 두께 (nm)
gui.add(material.iridescenceThicknessRange, '1', 1, 1000, 1); // 최대 두께 (nm)
// Transmission
material.transmission = 1; // 0 ~ 1 (유리 효과)
material.ior = 1.5; // 굴절률: 공기 1.0, 유리 1.5, 다이아몬드 2.4 등
material.thickness = 0.5; // 0 ~ 1 (전파 깊이)
gui.add(material, 'transmission', 0, 1, 0.01);
gui.add(material, 'ior', 1, 10, 0.01);
gui.add(material, 'thickness', 0, 1, 0.01);
/**
* Geometry
*/
const sphere = new THREE.Mesh(new THREE.SphereGeometry(0.5, 64, 64), material);
sphere.position.x = -1.5;
const plane = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), material);
const torus = new THREE.Mesh(
new THREE.TorusGeometry(0.3, 0.2, 16, 32),
material,
);
torus.position.x = 1.5;
scene.add(sphere, plane, torus);
/**
* Light
*/
const ambientLight = new THREE.AmbientLight(0xffffff, 1);
scene.add(ambientLight);
// pointLight만 있으면 음영 효과 때문에 현실감이 떨어진다.
const pointLight = new THREE.PointLight(0xffffff, 30);
pointLight.position.x = 2;
pointLight.position.y = 3;
pointLight.position.z = 4;
scene.add(pointLight);
/**
* Environment map
* 조명 효과가 좋다면 Light를 생략할 수 있다.
*/
const rgbeLoader = new RGBELoader();
rgbeLoader.load('./textures/environmentMap/2k.hdr', (environmentMap) => {
environmentMap.mapping = THREE.EquirectangularReflectionMapping;
scene.background = environmentMap;
scene.environment = environmentMap;
});
/**
* 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;
plane.rotation.y = 0.1 * elapsedTime;
torus.rotation.y = 0.1 * elapsedTime;
sphere.rotation.x = -0.15 * elapsedTime;
plane.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();
Three.js Material 속성 요약
색상 및 텍스처
| color |
기본 색상 (map과 함께 쓰면 곱해짐) |
| map |
표면 텍스처 이미지 |
| alphaMap |
투명도 조절용 흑백 텍스처 (흰색: 불투명, 검정: 투명) |
| aoMap |
음영 강조 (암부 표현) |
| metalnessMap |
금속 재질 표현용 텍스처 |
| roughnessMap |
표면 거칠기 표현용 텍스처 |
| normalMap |
디테일한 표면 방향 효과 |
| displacementMap |
실제 지오메트리 변형 (높낮이 표현) |
| envMap |
반사 환경 맵 (유리, 금속 등에 사용) |
투명도 및 렌더링
| opacity |
0.0~1.0 (0: 완전 투명, 1: 완전 불투명) |
| transparent |
opacity나 alphaMap 사용 시 필수 (true) |
| side |
앞면/뒷면/양면 렌더링 설정 (FrontSide, BackSide, DoubleSide) |
| depthWrite |
기본 true, false 시 투명한 오브젝트 겹침 처리에 유용 |
| depthTest |
기본 true, false 시 Z-buffer 무시 (UI 오브젝트 등에 사용) |
| alphaTest |
픽셀 투명도 기준 컷오프 (0.0~1.0) — alphaMap과 함께 사용 |
| blending |
블렌딩 모드 설정 (Additive, Subtractive 등) |
렌더 스타일
| wireframe |
메시를 선으로 렌더링 (디버깅/스타일용) |
| flatShading |
표면을 평면처럼 보이게 함 (각진 스타일) |
| polygonOffset / polygonOffsetFactor / polygonOffsetUnits |
Z-fighting 방지용 깊이 오프셋 설정 |
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(20, 20, 100, 100),
new THREE.MeshStandardMaterial({
alphaMap: floorAlphaTexture,
transparent: true,
map: floorColorTexture,
aoMap: floorARMTexture,
roughnessMap: floorARMTexture,
metalnessMap: floorARMTexture,
normalMapType: floorNormalTexture,
displacementMap: floorDisplacementTexture,
displacementScale: 0.3,
displacementBias: -0.2,
}),
);
displacementMap과 함께 사용하는 옵션
| displacementScale |
높낮이 변화의 크기 |
0.3 |
튀어나오는 정도 조절 |
| displacementBias |
전체적으로 기준 위치 조정 |
-0.2 |
바닥에 가까워지도록 낮춤 |