본문 바로가기
Graphic/ThreeJS

11 머티리얼(Material)

by curious week 2025. 7. 23.

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 바닥에 가까워지도록 낮춤

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

13 Go Live  (1) 2025.07.23
12 텍스트(3D Text)  (1) 2025.07.23
10 텍스처(Texture)  (3) 2025.07.23
09 Three.js 디버그 UI - lil-gui 사용법  (3) 2025.07.23
08 Three.js 기하학 구조 및 다양한 지오메트리(geometries) 정리  (1) 2025.07.23