텍스처(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
'Graphic > ThreeJS' 카테고리의 다른 글
| 12 텍스트(3D Text) (1) | 2025.07.23 |
|---|---|
| 11 머티리얼(Material) (1) | 2025.07.23 |
| 09 Three.js 디버그 UI - lil-gui 사용법 (3) | 2025.07.23 |
| 08 Three.js 기하학 구조 및 다양한 지오메트리(geometries) 정리 (1) | 2025.07.23 |
| 07 반응형 캔버스와 전체 화면 모드 설정 가이드 (2) | 2025.07.22 |