본문 바로가기
Graphic/ThreeJS

10 텍스처(Texture)

by curious week 2025. 7. 23.

텍스처(Texture)란 무엇인가?

Three.js에서 텍스처(Texture)는 지오메트리(Geometry)의 표면을 덮는 이미지입니다. 이는 단순한 색상 효과를 넘어서 조명, 표면 세부묘사, 반사 등 다양한 시각적 효과를 만들어냅니다.

1. 주요 텍스처 종류

1.1 Color / Albedo Map (색상 맵)

  • 지오메트리의 기본 색상을 제공합니다.
  • 텍스처의 RGB 값을 그대로 적용합니다.
  • 예: 나무 문, 바닥 무늬 이미지

1.2 Alpha Map (알파 맵)

  • 투명도 정보를 담고 있습니다.
  • 흰색 = 불투명 / 검정 = 완전 투명 / 회색 = 반투명

1.3 Height Map (높이 맵)

  • 정점을 실제로 이동시켜 입체감을 표현합니다.
  • 세분화(Segments)가 많을수록 효과가 뚜렷합니다.

1.4 Normal Map (노멀 맵)

  • 정점은 이동시키지 않고, 조명을 속여 표면 디테일을 더합니다.
  • 낮은 비용으로 고퀄리티 표현이 가능

1.5 Ambient Occlusion (AO Map)

  • 틈새나 모서리 등의 그림자 표현
  • 실제 조명은 아니지만 사실감을 더합니다.

1.6 Metalness Map

  • 어떤 부분이 금속성인지 정의
  • 금속성은 반사나 광택 처리에 영향을 줍니다.

1.7 Roughness Map

  • 거칠기를 나타냅니다.
  • 흰색 = 거침 (빛 확산), 검정 = 매끄러움 (빛 반사)

2. PBR (Physically Based Rendering)

  • 위의 Metalness + Roughness 기반의 조명/반사 방식
  • 사실적인 재질 표현을 위해 사용
  • Three.js, Blender, Unity, Unreal 등 현대 엔진의 표준 방식

3. 텍스처 로딩 방법

더 많은 텍스처 찾기

3.1 기본 JavaScript 방법

const image = new Image();
const texture = new THREE.Texture(image);
texture.colorSpace = THREE.SRGBColorSpace;

image.onload = () => {
  texture.needsUpdate = true;
};

image.src = '/textures/door/color.jpg';
  • texture.needsUpdate = true는 텍스처 데이터가 준비되었음을 알립니다.

texture.colorSpace = THREE.SRGBColorSpace를 생략하게 되면, 선형 공간처럼 처리하기 때문에 원래 이미지와 다른 색상을 띄게 된다.

  • 색이 탁하게, 회색빛, 밝기 과장처럼 보임
  • 특히 light와 함께 쓸 때 차이가 큼

3.2 TextureLoader

const loader = new THREE.TextureLoader();
const texture = loader.load('/textures/door/color.jpg');
texture.colorSpace = THREE.SRGBColorSpace;
  • 훨씬 간단하고 직관적입니다.
  • .load()는 콜백도 받을 수 있습니다.
/**
 * Textures
 */
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load(
  '/textures/door/color.jpg',
  () => {
    // load
    console.log('load');
  },
  () => {
    // progress
    console.log('progress');
  },
  () => {
    // error
    console.log('error');
  },
);
texture.colorSpace = THREE.SRGBColorSpace; // 해당 줄을 추가

3.3 LoadingManager

const manager = new THREE.LoadingManager();
manager.onLoad = () => console.log('All textures loaded');
const loader = new THREE.TextureLoader(manager);
  • 다수의 리소스를 일괄적으로 관리하고 싶을 때 유용합니다.

4. UV 언래핑 (UV Unwrapping)

  • 3D 객체를 2D 텍스처 위에 "펼쳐"서 매핑합니다.
  • geometry.attributes.uv에 저장됩니다.
  • 기본 지오메트리는 자동 생성되지만, 직접 만든 Geometry에는 수동으로 지정해야 합니다.

5. 텍스처 변형

5.1 반복

texture.repeat.set(2, 3);
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;

5.2 오프셋

texture.offset.set(0.5, 0.5);

5.3 회전

texture.rotation = Math.PI / 4;
texture.center.set(0.5, 0.5);

6. 필터링 & 밉맵

밉맵이란 텍스처의 축소된 버전들을 여러 단계로 미리 만들어 두는 것입니다.

  • ... 512 x 512 ... 최종적으로 1×1까지 이렇게 크기를 줄인 버전들을 GPU에 함께 업로드

6.1 축소 필터 (Minification)

  • 기본값: LinearMipmapLinearFilter
  • 성능 향상 & 아티팩트 제거
texture.minFilter = THREE.NearestFilter;

6.2 확대 필터 (Magnification)

  • 기본값: LinearFilter
  • 픽셀화 텍스처에 유리:
texture.magFilter = THREE.NearestFilter;

6.3 밉맵 비활성화

  • 성능에는 좋지만, 멀리서도 보여야하는 텍스처에는 사용하면 안됨.
texture.generateMipmaps = false;
// (필수!) 이 조합이 아니면 경고가 뜨고 텍스처가 이상하게 보일 수도 있어요.
texture.minFilter = THREE.NearestFilter

 

  • 모아레 패턴 (moiré artifacts)
    → 격자 같은 줄무늬가 번쩍거리며 깨져 보임
  • 계단 현상 (aliasing)
    → 텍스처가 부드럽지 않고 뚝뚝 끊겨 보임

 

7. 최적화

7.1 이미지 무게

  • .jpg: 더 작지만 손실 압축
  • .png: 무손실이지만 크기 큼
  • 압축 도구 사용 권장 (예: TinyPNG)
  • 무게가 적을수록 GPU 성능에 도움이 됨.

7.2 해상도 (2의 제곱)

  • 예: 512x512, 1024x1024
  • Three.js가 밉맵 생성 시 2의 제곱을 요구

7.3 데이터 유형

  • .png는 알파 채널 지원
  • 노멀 맵 등은 손실 없는 .png 사용 권장(일반 텍스처는  무손실 압축 방식으로 값을 보존하는 PNG 파일을 사용)

참고 자료


javascript

/**
 * Textures
 */
const image = new Image();
const texture = new THREE.Texture(image);
// 최신 Three js 문법에서는 해당 줄을 추가해야함
texture.colorSpace = THREE.SRGBColorSpace; 

image.onload = () => {
  texture.needsUpdate = true; // 데이터가 준비되었음
};

image.src = '/textures/door/color.jpg';

/**
 * Object
 */
const geometry = new THREE.BoxGeometry(1, 1, 1);
// console.log(geometry.attributes); // position, normal, uv
// console.log(geometry.attributes.uv); // 많은 uv 좌표를 기진다.
// const geometry = new THREE.TorusGeometry(1, 0.4, 12, 48);
const material = new THREE.MeshBasicMaterial({ map: colorTexture });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

TextLoader, LoadingManager, load, offset ...

/**
 * Textures
 */
const loadingManager = new THREE.LoadingManager();

loadingManager.onStart = () => {
  console.log('loading started');
};
loadingManager.onLoad = () => {
  console.log('loading finished');
};
loadingManager.onProgress = () => {
  console.log('loading progressing');
};
loadingManager.onError = () => {
  console.log('loading error');
};

const textureLoader = new THREE.TextureLoader(loadingManager);
const colorTexture = textureLoader.load('/textures/door/color.jpg');
colorTexture.colorSpace = THREE.SRGBColorSpace; // 해당 줄을 추가
const alphaTexture = textureLoader.load('/textures/door/alpha.jpg');
const heightTexture = textureLoader.load('/textures/door/height.jpg');
const normalTexture = textureLoader.load('/textures/door/normal.jpg');
const ambientOcclusionTexture = textureLoader.load(
  '/textures/door/ambientOcclusion.jpg',
);
const metalnessTexture = textureLoader.load('/textures/door/metalness.jpg');
const roughnessTexture = textureLoader.load('/textures/door/roughness.jpg');

// colorTexture.repeat.x = 2;
// colorTexture.repeat.y = 3;
colorTexture.repeat.set(2, 2);
/*
 * 위의 반복을 적용하기 위해 아래의 wrapS, T를 설정해줘야한다.
 * wrapS = x축
 * wrapT = y축
 * THREE.RepeatWrapping; // 동일한 이미지를 계속 반복
 * THREE.MirroredRepeatWrapping; // 이미지가 좌우/상하로 대칭되며 반복
 */
colorTexture.wrapS = THREE.RepeatWrapping;
colorTexture.wrapT = THREE.MirroredRepeatWrapping;

/*
 * 0, 0인 uv 좌표가
 * x = 0.5: 텍스처가 오른쪽으로 절반만큼 밀림
 * y = 0.25: 텍스처가 위로 1/4만큼 밀림
 */
colorTexture.offset.x = 0.5; // 좌측에서 시작
colorTexture.offset.y = 0.5; // 아래에서 시작

// 180deg = Math.PI
colorTexture.rotation = Math.PI * 0.5;
// 회전의 피벗을 변경
colorTexture.center.x = 0.5;
colorTexture.center.y = 0.5;

Filter

const loadingManager = new THREE.LoadingManager();

const textureLoader = new THREE.TextureLoader(loadingManager);
const colorTexture = textureLoader.load('/textures/minecraft.png');
colorTexture.colorSpace = THREE.SRGBColorSpace; // 해당 줄을 추가

colorTexture.generateMipmaps = false; // 성능에는 좋지만, 멀리서도 보여야하는 텍스처에는 사용하면 안됨.
colorTexture.minFilter = THREE.NearestFilter; // 많은 texture 픽셀 수에 적합
colorTexture.magFilter = THREE.NearestFilter; // 적은 픽셀 수에 적합

 

 wrapS / wrapT

설명

  • 텍스처 좌표(UV)가 0~1 범위를 벗어날 경우, 어떻게 처리할지를 지정합니다.
  • wrapS: 수평 방향(U) wrapping 방법
  • wrapT: 수직 방향(V) wrapping 방법

주요 값 (모두 THREE에서 제공)

THREE.ClampToEdgeWrapping 기본값. 가장자리 픽셀을 연장해서 채움
THREE.RepeatWrapping 텍스처를 반복시킴
THREE.MirroredRepeatWrapping 반사 대칭 방식으로 반복

예시

floorColorTexture.wrapS = THREE.RepeatWrapping
floorColorTexture.wrapT = THREE.RepeatWrapping

→ 텍스처를 x축과 y축 모두에서 반복하게 만듦.


주요 Texture 인스턴스 속성 요약

속성명 | 타입 / 예시 값 | 설명

image HTMLImageElement 로드된 이미지
wrapS THREE.RepeatWrapping 등 수평 방향 반복 방식
wrapT THREE.ClampToEdgeWrapping 등 수직 방향 반복 방식
repeat new THREE.Vector2(1, 1) 텍스처 반복 횟수
offset new THREE.Vector2(0, 0) 텍스처 위치 보정 (UV 오프셋)
center new THREE.Vector2(0.5, 0.5) 회전 기준점
rotation 0.0 텍스처 회전 (라디안)
flipY true / false 수직 반전 여부 (glTF 사용 시 false 필요)
needsUpdate true 텍스처 변경 후 재적용 트리거
magFilter THREE.LinearFilter 등 확대 시 텍스처 필터링 방식
minFilter THREE.LinearMipmapLinearFilter 등 축소 시 텍스처 필터링 방식
mipmaps 자동 생성된 텍스처 축소 이미지들 직접 설정 가능 (고급용)
anisotropy 1 이상 선명도를 높이는 고급 설정 (GPU가 지원해야 함)
generateMipmaps true minFilter가 mipmap 사용하는 경우 true
colorSpace THREE.SRGBColorSpace, LinearSRGBColorSpace 색상 공간 (sRGB 등)
encoding (구버전) THREE.sRGBEncoding 이전 버전에서 사용되던 색상 인코딩 방식
format THREE.RGBAFormat 등 픽셀 포맷
type THREE.UnsignedByteType 등 텍스처 타입 (디폴트: 8비트)

참고: 텍스처 설정 전형적인 흐름

const texture = textureLoader.load('./textures/brick_diffuse.jpg')

// 반복 및 래핑 설정
texture.wrapS = THREE.RepeatWrapping
texture.wrapT = THREE.RepeatWrapping
texture.repeat.set(4, 4)

// 좌표 이동, 회전
texture.offset.set(0.1, 0.2)
texture.center.set(0.5, 0.5)
texture.rotation = Math.PI / 4

// 색상공간 설정 (색상 텍스처인 경우)
texture.colorSpace = THREE.SRGBColorSpace

// 텍스처 변경 반영
texture.needsUpdate = true