You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					555 lines
				
				35 KiB
			
		
		
			
		
	
	
					555 lines
				
				35 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								<!DOCTYPE html><html lang="ko"><head>
							 | 
						||
| 
								 | 
							
								    <meta charset="utf-8">
							 | 
						||
| 
								 | 
							
								    <title>텍스처(Textures)</title>
							 | 
						||
| 
								 | 
							
								    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
							 | 
						||
| 
								 | 
							
								    <meta name="twitter:card" content="summary_large_image">
							 | 
						||
| 
								 | 
							
								    <meta name="twitter:site" content="@threejs">
							 | 
						||
| 
								 | 
							
								    <meta name="twitter:title" content="Three.js – 텍스처(Textures)">
							 | 
						||
| 
								 | 
							
								    <meta property="og:image" content="https://threejs.org/files/share.png">
							 | 
						||
| 
								 | 
							
								    <link rel="shortcut icon" href="/files/favicon_white.ico" media="(prefers-color-scheme: dark)">
							 | 
						||
| 
								 | 
							
								    <link rel="shortcut icon" href="/files/favicon.ico" media="(prefers-color-scheme: light)">
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    <link rel="stylesheet" href="/manual/resources/lesson.css">
							 | 
						||
| 
								 | 
							
								    <link rel="stylesheet" href="/manual/resources/lang.css">
							 | 
						||
| 
								 | 
							
								<!-- Import maps polyfill -->
							 | 
						||
| 
								 | 
							
								<!-- Remove this when import maps will be widely supported -->
							 | 
						||
| 
								 | 
							
								<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<script type="importmap">
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								  "imports": {
							 | 
						||
| 
								 | 
							
								    "three": "../../build/three.module.js"
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</script>
							 | 
						||
| 
								 | 
							
								    <link rel="stylesheet" href="/manual/ko/lang.css">
							 | 
						||
| 
								 | 
							
								  </head>
							 | 
						||
| 
								 | 
							
								  <body>
							 | 
						||
| 
								 | 
							
								    <div class="container">
							 | 
						||
| 
								 | 
							
								      <div class="lesson-title">
							 | 
						||
| 
								 | 
							
								        <h1>텍스처(Textures)</h1>
							 | 
						||
| 
								 | 
							
								      </div>
							 | 
						||
| 
								 | 
							
								      <div class="lesson">
							 | 
						||
| 
								 | 
							
								        <div class="lesson-main">
							 | 
						||
| 
								 | 
							
								          <p>※ 이 글은 Three.js의 튜토리얼 시리즈로서,
							 | 
						||
| 
								 | 
							
								먼저 <a href="fundamentals.html">Three.js의 기본 구조에 관한 글</a>과
							 | 
						||
| 
								 | 
							
								<a href="setup.html">개발 환경 설정하는 법</a>을 읽고 오길 권장합니다.</p>
							 | 
						||
| 
								 | 
							
								<p>※ 텍스처, Texture는 "질감"으로 번역할 수 있으나, 그대로 표기하는 쪽이
							 | 
						||
| 
								 | 
							
								직관적이라 판단하여 <strong>텍스처</strong>로 번역하였습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>Three.js에서 텍스처를 이야기하기란 쉽지 않습니다. 텍스처는 워낙 방대한
							 | 
						||
| 
								 | 
							
								주제이고, 각 주제끼리도 서로 연결되어 있어 한 번에 설명하는 것이 거의
							 | 
						||
| 
								 | 
							
								불가능에 가깝기 때문이죠. 어떻게 설명해야 잘 설명했다고 할 수 있을지
							 | 
						||
| 
								 | 
							
								확신은 없지만, 일단 해보기로 합시다. 다음은 이 글의 간략한 목차입니다.</p>
							 | 
						||
| 
								 | 
							
								<ul>
							 | 
						||
| 
								 | 
							
								<li><a href="#hello">하이, 텍스처</a></li>
							 | 
						||
| 
								 | 
							
								<li><a href="#six">육면체 각 면에 다른 텍스처 지정하기</a></li>
							 | 
						||
| 
								 | 
							
								<li><a href="#loading">텍스처 불러오기</a></li>
							 | 
						||
| 
								 | 
							
								<ul>
							 | 
						||
| 
								 | 
							
								  <li><a href="#easy">간단한 방법</a></li>
							 | 
						||
| 
								 | 
							
								  <li><a href="#wait1">텍스처를 불러온 후 처리하기</a></li>
							 | 
						||
| 
								 | 
							
								  <li><a href="#waitmany">다수의 텍스처를 불러온 후 처리하기</a></li>
							 | 
						||
| 
								 | 
							
								  <li><a href="#cors">다른 도메인(origin)에서 텍스처 불러오기</a></li>
							 | 
						||
| 
								 | 
							
								</ul>
							 | 
						||
| 
								 | 
							
								<li><a href="#memory">메모리 관리</a></li>
							 | 
						||
| 
								 | 
							
								<li><a href="#format">JPG vs PNG</a></li>
							 | 
						||
| 
								 | 
							
								<li><a href="#filtering-and-mips">필터링과 Mips</a></li>
							 | 
						||
| 
								 | 
							
								<li><a href="#uvmanipulation">텍스처의 반복(repeating), 위치 조절(offseting), 회전(rotating), 래핑(wrapping)</a></li>
							 | 
						||
| 
								 | 
							
								</ul>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<h2 id="-a-name-hello-a-"><a name="hello"></a> 하이, 텍스처</h2>
							 | 
						||
| 
								 | 
							
								<p>텍스처는 <em>일반적으로</em> 포토샵이나 김프 등의 프로그램으로 만든 이미지입니다.
							 | 
						||
| 
								 | 
							
								예를 들어 아래 이미지를 정육면체에 씌워보죠.</p>
							 | 
						||
| 
								 | 
							
								<div class="threejs_center">
							 | 
						||
| 
								 | 
							
								  <img src="../examples/resources/images/wall.jpg" style="width: 600px;" class="border">
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p>예제는 처음 만들었던 것을 사용하겠습니다. 추가로 <a href="/docs/#api/ko/loaders/TextureLoader"><code class="notranslate" translate="no">TextureLoader</code></a>를 새로 생성한
							 | 
						||
| 
								 | 
							
								뒤, 인스턴스의 <a href="/docs/#api/ko/loaders/TextureLoader#load"><code class="notranslate" translate="no">load</code></a> 메서드에 이미지의 URL을 넘겨주어 호출하고,
							 | 
						||
| 
								 | 
							
								반환 받은 값을 재질(material)의 <code class="notranslate" translate="no">map</code> 속성에 지정합니다(<code class="notranslate" translate="no">color</code> 속성은 지정하지
							 | 
						||
| 
								 | 
							
								않습니다).</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loader = new THREE.TextureLoader();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const material = new THREE.MeshBasicMaterial({
							 | 
						||
| 
								 | 
							
								-  color: 0xFF8844,
							 | 
						||
| 
								 | 
							
								+  map: loader.load('resources/images/wall.jpg'),
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>※ <a href="/docs/#api/ko/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>을 사용했으므로 광원을 사용할 필요가 없습니다.</p>
							 | 
						||
| 
								 | 
							
								<p></p><div translate="no" class="threejs_example_container notranslate">
							 | 
						||
| 
								 | 
							
								  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/textured-cube.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<h2 id="-a-name-six-a-"><a name="six"></a> 육면체 각 면에 다른 텍스처 지정하기</h2>
							 | 
						||
| 
								 | 
							
								<p>이번에는 육면체의 각 면에 다른 텍스처를 넣어볼까요?</p>
							 | 
						||
| 
								 | 
							
								<div class="threejs_center">
							 | 
						||
| 
								 | 
							
								  <div>
							 | 
						||
| 
								 | 
							
								    <img src="../examples/resources/images/flower-1.jpg" style="width: 100px;" class="border">
							 | 
						||
| 
								 | 
							
								    <img src="../examples/resources/images/flower-2.jpg" style="width: 100px;" class="border">
							 | 
						||
| 
								 | 
							
								    <img src="../examples/resources/images/flower-3.jpg" style="width: 100px;" class="border">
							 | 
						||
| 
								 | 
							
								  </div>
							 | 
						||
| 
								 | 
							
								  <div>
							 | 
						||
| 
								 | 
							
								    <img src="../examples/resources/images/flower-4.jpg" style="width: 100px;" class="border">
							 | 
						||
| 
								 | 
							
								    <img src="../examples/resources/images/flower-5.jpg" style="width: 100px;" class="border">
							 | 
						||
| 
								 | 
							
								    <img src="../examples/resources/images/flower-6.jpg" style="width: 100px;" class="border">
							 | 
						||
| 
								 | 
							
								  </div>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p>단순히 재질을 6개 만들어 <a href="/docs/#api/ko/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>를 생성할 때 배열로 넘겨주기만 하면 됩니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const loader = new THREE.TextureLoader();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								-const material = new THREE.MeshBasicMaterial({
							 | 
						||
| 
								 | 
							
								-  map: loader.load('resources/images/wall.jpg'),
							 | 
						||
| 
								 | 
							
								-});
							 | 
						||
| 
								 | 
							
								+const materials = [
							 | 
						||
| 
								 | 
							
								+  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-1.jpg')}),
							 | 
						||
| 
								 | 
							
								+  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-2.jpg')}),
							 | 
						||
| 
								 | 
							
								+  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-3.jpg')}),
							 | 
						||
| 
								 | 
							
								+  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-4.jpg')}),
							 | 
						||
| 
								 | 
							
								+  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-5.jpg')}),
							 | 
						||
| 
								 | 
							
								+  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-6.jpg')}),
							 | 
						||
| 
								 | 
							
								+];
							 | 
						||
| 
								 | 
							
								-const cube = new THREE.Mesh(geometry, material);
							 | 
						||
| 
								 | 
							
								+const cube = new THREE.Mesh(geometry, materials);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>껌이네요.</p>
							 | 
						||
| 
								 | 
							
								<p></p><div translate="no" class="threejs_example_container notranslate">
							 | 
						||
| 
								 | 
							
								  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube-6-textures.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/textured-cube-6-textures.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<p>주의해야할 점은 모든 <code class="notranslate" translate="no">geometry</code>가 재질을 배열로 받진 않는다는 점입니다.
							 | 
						||
| 
								 | 
							
								<a href="/docs/#api/ko/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a>나 <a href="/docs/#api/ko/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a>는 최대 6개, <a href="/docs/#api/ko/geometries/ConeGeometry"><code class="notranslate" translate="no">ConeGeometry</code></a>와
							 | 
						||
| 
								 | 
							
								<a href="/docs/#api/ko/geometries/ConeGeometry"><code class="notranslate" translate="no">ConeGeometry</code></a>는 밑면과 뿔 부분에 하나씩 최대 2개, <a href="/docs/#api/ko/geometries/CylinderGeometry"><code class="notranslate" translate="no">CylinderGeometry</code></a>와
							 | 
						||
| 
								 | 
							
								<a href="/docs/#api/ko/geometries/CylinderGeometry"><code class="notranslate" translate="no">CylinderGeometry</code></a>는 아래, 위, 옆면 하나씩 최대 3개를 지정할 수 있죠.
							 | 
						||
| 
								 | 
							
								다른 경우에는 <code class="notranslate" translate="no">geometry</code>를 따로 만들거나, 텍스처의 좌표를 직접 수정해야 합니다.</p>
							 | 
						||
| 
								 | 
							
								<p>다른 3D 엔진에서나 Three.js에서나, 하나의 <code class="notranslate" translate="no">geometry</code>에서 여러 텍스처를 쓰고 싶을 때는
							 | 
						||
| 
								 | 
							
								보통 <a href="https://en.wikipedia.org/wiki/Texture_atlas">텍스처 아틀라스</a>를 사용합니다.
							 | 
						||
| 
								 | 
							
								텍스처 아틀라스란 여러 이미지로 구성된 하나의 텍스처로, <code class="notranslate" translate="no">geometry</code>의 정점에 따라 텍스처의
							 | 
						||
| 
								 | 
							
								좌표를 조절해 <code class="notranslate" translate="no">geometry</code>의 각 삼각형이 텍스처의 일정 부분을 표현하도록 할 수 있습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>그렇다면 텍스처의 좌표란 무엇일까요? 이는 <code class="notranslate" translate="no">geometry</code>의 각 정점에 추가되는 데이터로, 특정
							 | 
						||
| 
								 | 
							
								정점에 텍스처의 어느 부분을 써야하는지를 나타냅니다. 자세한 사용법은 나중에
							 | 
						||
| 
								 | 
							
								<a href="custom-buffergeometry.html">사용자 지정 geometry 만들기</a>에서 살펴보겠습니다.</p>
							 | 
						||
| 
								 | 
							
								<h2 id="-a-name-loading-a-"><a name="loading"></a> 텍스처 불러오기</h2>
							 | 
						||
| 
								 | 
							
								<h3 id="-a-name-easy-a-"><a name="easy"></a> 간단한 방법</h3>
							 | 
						||
| 
								 | 
							
								<p>이 사이트의 예제는 대부분 텍스처를 로딩할 때 간단한 메서드를 사용했습니다.
							 | 
						||
| 
								 | 
							
								<a href="/docs/#api/ko/loaders/TextureLoader"><code class="notranslate" translate="no">TextureLoader</code></a>를 생성하고, 인스턴스의 <a href="/docs/#api/ko/loaders/TextureLoader#load"><code class="notranslate" translate="no">load</code></a> 메서드를
							 | 
						||
| 
								 | 
							
								호출하는 거죠. 이 <code class="notranslate" translate="no">load</code> 메서드는 <a href="/docs/#api/ko/textures/Texture"><code class="notranslate" translate="no">Texture</code></a> 객체를 반환합니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const texture = loader.load('resources/images/flower-1.jpg');
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>알아둬야 할 건 이 메서드는 비동기로 작동한다는 점입니다. 이미지를 완전히
							 | 
						||
| 
								 | 
							
								불러온 후 이미지로 텍스처를 업데이트하기 전까지, 텍스처는 투명하게 보일 겁니다.</p>
							 | 
						||
| 
								 | 
							
								<p>텍스처를 전부 불러오지 않아도 브라우저가 페이지 렌더링을 시작할 것이므로 이는
							 | 
						||
| 
								 | 
							
								속도면에서 꽤 큰 장점입니다. 텍스처를 언제 다 불러왔는지 알아야 하는 경우가
							 | 
						||
| 
								 | 
							
								아니라면, 대부분 큰 문제가 되지 않겠죠.</p>
							 | 
						||
| 
								 | 
							
								<h3 id="-a-name-wait1-a-"><a name="wait1"></a> 텍스처를 불러온 후 처리하기</h3>
							 | 
						||
| 
								 | 
							
								<p>텍스처를 불러온 후 후처리를 위해 <code class="notranslate" translate="no">load</code> 메서드는 두 번째 인자로 콜백(callback)
							 | 
						||
| 
								 | 
							
								함수를 받습니다. 이 함수는 텍스처를 전부 불러온 후 호출되죠. 글의 첫 번째 예제를
							 | 
						||
| 
								 | 
							
								조금 수정해보겠습니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const loader = new THREE.TextureLoader();
							 | 
						||
| 
								 | 
							
								loader.load('resources/images/wall.jpg', (texture) => {
							 | 
						||
| 
								 | 
							
								  const material = new THREE.MeshBasicMaterial({
							 | 
						||
| 
								 | 
							
								    map: texture,
							 | 
						||
| 
								 | 
							
								  });
							 | 
						||
| 
								 | 
							
								  const cube = new THREE.Mesh(geometry, material);
							 | 
						||
| 
								 | 
							
								  scene.add(cube);
							 | 
						||
| 
								 | 
							
								  cubes.push(cube);  // 회전 애니메이션을 위해 배열에 추가
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>브라우저의 캐시를 비우거나 인터넷 연결 속도가 느리지 않는 한 차이를 느끼기
							 | 
						||
| 
								 | 
							
								어렵긴 하지만, 텍스처를 불러온 뒤 화면을 렌더링합니다.</p>
							 | 
						||
| 
								 | 
							
								<p></p><div translate="no" class="threejs_example_container notranslate">
							 | 
						||
| 
								 | 
							
								  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube-wait-for-texture.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/textured-cube-wait-for-texture.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<h3 id="-a-name-waitmany-a-"><a name="waitmany"></a> 다수의 텍스처를 불러온 후 처리하기</h3>
							 | 
						||
| 
								 | 
							
								<p>다수의 텍스처를 한 번에 불러와야 할 경우 <a href="/docs/#api/ko/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a>를 사용할 수 있습니다.
							 | 
						||
| 
								 | 
							
								<a href="/docs/#api/ko/loaders/TextureLoader"><code class="notranslate" translate="no">TextureLoader</code></a>를 생성할 때 미리 생성한 <a href="/docs/#api/ko/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a>의 인스턴스를 인자로
							 | 
						||
| 
								 | 
							
								넘겨주고, <a href="/docs/#api/ko/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a> 인스턴스의 <a href="/docs/#api/ko/loaders/managers/LoadingManager#onLoad"><code class="notranslate" translate="no">onLoad</code></a> 속성에
							 | 
						||
| 
								 | 
							
								콜백 함수를 설정해주는 거죠.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loadManager = new THREE.LoadingManager();
							 | 
						||
| 
								 | 
							
								*const loader = new THREE.TextureLoader(loadManager);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const materials = [
							 | 
						||
| 
								 | 
							
								  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-1.jpg')}),
							 | 
						||
| 
								 | 
							
								  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-2.jpg')}),
							 | 
						||
| 
								 | 
							
								  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-3.jpg')}),
							 | 
						||
| 
								 | 
							
								  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-4.jpg')}),
							 | 
						||
| 
								 | 
							
								  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-5.jpg')}),
							 | 
						||
| 
								 | 
							
								  new THREE.MeshBasicMaterial({map: loader.load('resources/images/flower-6.jpg')}),
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+loadManager.onLoad = () => {
							 | 
						||
| 
								 | 
							
								+  const cube = new THREE.Mesh(geometry, materials);
							 | 
						||
| 
								 | 
							
								+  scene.add(cube);
							 | 
						||
| 
								 | 
							
								+  cubes.push(cube);  // 회전 애니메이션을 위해 배열에 추가
							 | 
						||
| 
								 | 
							
								+};
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p><a href="/docs/#api/ko/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a>의 <a href="/docs/#api/ko/loaders/managers/LoadingManager#onProgress"><code class="notranslate" translate="no">onProgress</code></a>에 콜백 함수를 지정하면
							 | 
						||
| 
								 | 
							
								현재 진행 상태를 추적할 수 있습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>일단 HTML로 프로그래스 바(progress bar)를 만들겠습니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-html" translate="no"><body>
							 | 
						||
| 
								 | 
							
								  <canvas id="c"></canvas>
							 | 
						||
| 
								 | 
							
								+  <div id="loading">
							 | 
						||
| 
								 | 
							
								+    <div class="progress"><div class="progressbar"></div></div>
							 | 
						||
| 
								 | 
							
								+  </div>
							 | 
						||
| 
								 | 
							
								</body>
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>스타일도 추가하죠.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-css" translate="no">#loading {
							 | 
						||
| 
								 | 
							
								    position: fixed;
							 | 
						||
| 
								 | 
							
								    top: 0;
							 | 
						||
| 
								 | 
							
								    left: 0;
							 | 
						||
| 
								 | 
							
								    width: 100%;
							 | 
						||
| 
								 | 
							
								    height: 100%;
							 | 
						||
| 
								 | 
							
								    display: flex;
							 | 
						||
| 
								 | 
							
								    justify-content: center;
							 | 
						||
| 
								 | 
							
								    align-items: center;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								#loading .progress {
							 | 
						||
| 
								 | 
							
								    margin: 1.5em;
							 | 
						||
| 
								 | 
							
								    border: 1px solid white;
							 | 
						||
| 
								 | 
							
								    width: 50vw;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								#loading .progressbar {
							 | 
						||
| 
								 | 
							
								    margin: 2px;
							 | 
						||
| 
								 | 
							
								    background: white;
							 | 
						||
| 
								 | 
							
								    height: 1em;
							 | 
						||
| 
								 | 
							
								    transform-origin: top left;
							 | 
						||
| 
								 | 
							
								    transform: scaleX(0);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>다음으로 <code class="notranslate" translate="no">onProgress</code> 콜백에서 <code class="notranslate" translate="no">.progressbar</code>의 X축 크기를 조정하겠습니다.
							 | 
						||
| 
								 | 
							
								콜백 함수는 마지막으로 불러온 자원의 URL, 현재까지 불러온 자원의 수, 총 지원의
							 | 
						||
| 
								 | 
							
								수를 매개변수로 받습니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loadingElem = document.querySelector('#loading');
							 | 
						||
| 
								 | 
							
								+const progressBarElem = loadingElem.querySelector('.progressbar');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								loadManager.onLoad = () => {
							 | 
						||
| 
								 | 
							
								+  loadingElem.style.display = 'none';
							 | 
						||
| 
								 | 
							
								  const cube = new THREE.Mesh(geometry, materials);
							 | 
						||
| 
								 | 
							
								  scene.add(cube);
							 | 
						||
| 
								 | 
							
								  cubes.push(cube);  // 회전 애니메이션을 위해 배열에 추가
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+loadManager.onProgress = (urlOfLastItemLoaded, itemsLoaded, itemsTotal) => { // 마지막으로 불러온 자원의 URL, 현재까지 불러온 자원의 수, 총 지원의 수
							 | 
						||
| 
								 | 
							
								+  const progress = itemsLoaded / itemsTotal;
							 | 
						||
| 
								 | 
							
								+  progressBarElem.style.transform = `scaleX(${progress})`;
							 | 
						||
| 
								 | 
							
								+};
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>캐시를 비우거나 인터넷 속도가 느리지 않다면 프로그래스 바가 보이지 않을
							 | 
						||
| 
								 | 
							
								수도 있습니다.</p>
							 | 
						||
| 
								 | 
							
								<p></p><div translate="no" class="threejs_example_container notranslate">
							 | 
						||
| 
								 | 
							
								  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube-wait-for-all-textures.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/textured-cube-wait-for-all-textures.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<h2 id="-a-name-cors-a-origin-"><a name="cors"></a> 다른 도메인(origin)에서 텍스처 불러오기</h2>
							 | 
						||
| 
								 | 
							
								<p>다른 서버에서 이미지를 불러오려면 해당 서버가 <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">CORS 헤더</a>를
							 | 
						||
| 
								 | 
							
								보내줘야 합니다. CORS 헤더가 없다면 Three.js가 이미지를 불러오지 않을 것이고,
							 | 
						||
| 
								 | 
							
								에러가 발생할 겁니다. 만약 이미지 호스팅 서버를 운영한다면 해당 서버가 CORS 헤더를
							 | 
						||
| 
								 | 
							
								보내는지 확인해보세요.</p>
							 | 
						||
| 
								 | 
							
								<p><a href="https://imgur.com">imgur</a>, <a href="https://flickr.com">flickr</a>, <a href="https://github.com">github</a>
							 | 
						||
| 
								 | 
							
								등의 사이트는 자신이 호스팅하는 이미지를 사용해도 좋다는 헤더를 보냅니다.
							 | 
						||
| 
								 | 
							
								대부분의 웹사이트는 이를 허용하지 않죠.</p>
							 | 
						||
| 
								 | 
							
								<h2 id="-a-name-memory-a-"><a name="memory"></a> 메모리 관리</h2>
							 | 
						||
| 
								 | 
							
								<p>텍스처는 Three.js 앱에서 메모리를 가장 많이 사용하는 요소 중 하나입니다.
							 | 
						||
| 
								 | 
							
								<em>대체로</em> 텍스처는 약 <code class="notranslate" translate="no">너비 * 높이 * 4 * 1.33</code> 바이트의 메모리를 사용합니다.</p>
							 | 
						||
| 
								 | 
							
								<p>여기서 압축은 그다지 중요한 요소가 아닙니다. 예를 들어 집이 포함된 장면(scene)을
							 | 
						||
| 
								 | 
							
								만든다고 해보죠. 집 안에는 탁자가 있고, 탁자의 윗면에 나무 텍스처를 씌우려고
							 | 
						||
| 
								 | 
							
								합니다.</p>
							 | 
						||
| 
								 | 
							
								<div class="threejs_center"><img class="border" src="../resources/images/compressed-but-large-wood-texture.jpg" align="center" style="width: 300px"></div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p>이 이미지는 매우 고 배율로 압축되어 157kb 밖에 되지 않습니다. 상대적으로
							 | 
						||
| 
								 | 
							
								다운 속도는 빠를 것이나, 이 <a href="resources/images/compressed-but-large-wood-texture.jpg">이미지의 실제 크기는 3024 x 3761 픽셀입니다</a>.
							 | 
						||
| 
								 | 
							
								위 공식에 따라 이 이미지를 적용해보면,</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate notranslate" translate="no">3024 * 3761 * 4 * 1.33 = 60505764.5
							 | 
						||
| 
								 | 
							
								</pre><p>무려 <strong>약 60 메가바이트의 메모리</strong>를 사용합니다. 이런 텍스처가 몇 개만 더
							 | 
						||
| 
								 | 
							
								있어도 메모리 부족으로 앱을 사용하지 못할 수 있죠(OUT_OF_MEMORY).</p>
							 | 
						||
| 
								 | 
							
								<p>극단적인 예제이기는 하나, 이 예제는 텍스처를 사용하는데 숨겨진 비용을 고려해야
							 | 
						||
| 
								 | 
							
								한다는 것을 잘 알려줍니다. Three.js가 텍스처를 사용하려면 GPU에 텍스처를
							 | 
						||
| 
								 | 
							
								넘겨주어야 하는데, GPU는 <em>일반적으로</em> 압축하지 않은 데이터를 사용하죠.</p>
							 | 
						||
| 
								 | 
							
								<p>이 예시의 교훈은 파일의 용량이 아니라 파일의 해상도를 줄어야 한다는 것입니다.
							 | 
						||
| 
								 | 
							
								파일의 용량이 작다면 불러오는 속도가 빠를 것이고, 해상도가 낮다면 메모리를
							 | 
						||
| 
								 | 
							
								그만큼 적게 사용하겠죠. 얼마나 낮게 만들어야 할까요? 필요한 만큼 퀄리티를
							 | 
						||
| 
								 | 
							
								유지한 선에서 가능한 낮게 만드는 게 좋습니다.</p>
							 | 
						||
| 
								 | 
							
								<h2 id="-a-name-format-a-jpg-vs-png"><a name="format"></a> JPG vs PNG</h2>
							 | 
						||
| 
								 | 
							
								<p>이는 HTML과 마찬가지입니다. JPG는 손실 압축을 사용하고, PNG는 비손실 압축을
							 | 
						||
| 
								 | 
							
								사용하는 대신 보통 PNG가 더 용량이 크죠. 하지만 PNG는 투명도를 지원합니다.
							 | 
						||
| 
								 | 
							
								PNG는 비-이미지 데이터인 법선 맵(normal maps), 그리고 나중에 살펴볼 다른
							 | 
						||
| 
								 | 
							
								비-이미지 데이터를 사용하기에 현재로써는 가장 적당한 파일 형식입니다.</p>
							 | 
						||
| 
								 | 
							
								<p>위에서 말했듯, WebGL에서는 JPG가 용량이 더 작긴 해도 
							 | 
						||
| 
								 | 
							
								PNG 형식보다 메모리 점유율이 낮진 않습니다.</p>
							 | 
						||
| 
								 | 
							
								<h2 id="-a-name-filtering-and-mips-a-mips"><a name="filtering-and-mips"></a> 필터링과 Mips</h2>
							 | 
						||
| 
								 | 
							
								<p>이 16x16 텍스처를</p>
							 | 
						||
| 
								 | 
							
								<div class="threejs_center"><img src="../resources/images/mip-low-res-enlarged.png" class="nobg" align="center"></div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p>아래의 정육면체에 적용해보죠.</p>
							 | 
						||
| 
								 | 
							
								<div class="spread"><div data-diagram="filterCube"></div></div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p>그리고 정육면체를 아주 작게 렌더링합니다.</p>
							 | 
						||
| 
								 | 
							
								<div class="spread"><div data-diagram="filterCubeSmall"></div></div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p>음, 보기가 어렵네요. 확대해봅시다.</p>
							 | 
						||
| 
								 | 
							
								<div class="spread"><div data-diagram="filterCubeSmallLowRes"></div></div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p>GPU는 작은 정육면체를 표현할 때 어떻게 각 픽셀의 색상을 결정할까요? 정육면체가
							 | 
						||
| 
								 | 
							
								작아도 너무 작아서 1, 2 픽셀 정도라면요?</p>
							 | 
						||
| 
								 | 
							
								<p>이게 바로 필터링(filtering)이 있는 이유입니다.</p>
							 | 
						||
| 
								 | 
							
								<p>포토샵이라면 근처 픽셀의 평균을 내 해당 1, 2 픽셀의 형태를 결정할 겁니다.
							 | 
						||
| 
								 | 
							
								이는 매우 무거운 작업이죠. GPU는 이 문제를 해결하기 위해 <a href="https://ko.wikipedia.org/wiki/%EB%B0%89%EB%A7%B5">밉맵(mipmaps)</a>을
							 | 
						||
| 
								 | 
							
								사용합니다.</p>
							 | 
						||
| 
								 | 
							
								<p>밉(mips)은 텍스처의 복사본으로, 각 밉은 축소된 이전 밉보다 반만큼 작습니다.
							 | 
						||
| 
								 | 
							
								밉은 1x1 픽셀 밉을 생성할 때까지 계속 생성되죠. 위 이미지의 경우 밉은 다음처럼
							 | 
						||
| 
								 | 
							
								생성될 겁니다.</p>
							 | 
						||
| 
								 | 
							
								<div class="threejs_center"><img src="../resources/images/mipmap-low-res-enlarged.png" class="nobg" align="center"></div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p>이제 1, 2 픽셀 정도로 작은 정육면체를 렌더링할 때 GPU는 가장 작거나, 두 번째로
							 | 
						||
| 
								 | 
							
								작은 밉을 선택해 텍스처를 적용하기만 하면 되죠.</p>
							 | 
						||
| 
								 | 
							
								<p>Three.js에서는 텍스처의 크기가 원본보다 클 때와 작을 때 각각 어떻게 표현할지를
							 | 
						||
| 
								 | 
							
								설정할 수 있습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>텍스처의 크기가 원본보다 클 때의 필터는 <a href="/docs/#api/ko/textures/Texture#magFilter"><code class="notranslate" translate="no">texture.magFilter</code></a>
							 | 
						||
| 
								 | 
							
								속성을 <code class="notranslate" translate="no">THREE.NearestFilter</code>나 <code class="notranslate" translate="no">THREE.LinearFilter</code>로 지정해 설정합니다.</p>
							 | 
						||
| 
								 | 
							
								<p><code class="notranslate" translate="no">NearestFilter</code>는 말 그대로 텍스처에서 가장 가까운 픽셀을 고르는 것입니다.
							 | 
						||
| 
								 | 
							
								낮은 해상도라면 텍스처가 픽셀화되어 마인크래프트 같은 느낌을 주겠죠.</p>
							 | 
						||
| 
								 | 
							
								<p><code class="notranslate" translate="no">LinearFilter</code>는 가장 가까운 4개의 픽셀을 골라 각 픽셀의 실제 거리에 따라 적절한
							 | 
						||
| 
								 | 
							
								비율로 섞는 것을 말합니다.</p>
							 | 
						||
| 
								 | 
							
								<div class="spread">
							 | 
						||
| 
								 | 
							
								  <div>
							 | 
						||
| 
								 | 
							
								    <div data-diagram="filterCubeMagNearest" style="height: 250px;"></div>
							 | 
						||
| 
								 | 
							
								    <div class="code">Nearest</div>
							 | 
						||
| 
								 | 
							
								  </div>
							 | 
						||
| 
								 | 
							
								  <div>
							 | 
						||
| 
								 | 
							
								    <div data-diagram="filterCubeMagLinear" style="height: 250px;"></div>
							 | 
						||
| 
								 | 
							
								    <div class="code">Linear</div>
							 | 
						||
| 
								 | 
							
								  </div>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p>텍스처가 원본 크기보다 작을 때의 필터는 <a href="/docs/#api/ko/textures/Texture#minFilter"><code class="notranslate" translate="no">texture.minFilter</code></a>
							 | 
						||
| 
								 | 
							
								속성을 다음 6가지 값 중 하나로 지정해 사용합니다.</p>
							 | 
						||
| 
								 | 
							
								<ul>
							 | 
						||
| 
								 | 
							
								<li><p><code class="notranslate" translate="no">THREE.NearestFilter</code></p>
							 | 
						||
| 
								 | 
							
								<p> 원본보다 클 때와 마찬가지로 가장 가까운 픽셀을 선택합니다</p>
							 | 
						||
| 
								 | 
							
								</li>
							 | 
						||
| 
								 | 
							
								<li><p><code class="notranslate" translate="no">THREE.LinearFilter</code></p>
							 | 
						||
| 
								 | 
							
								<p> 원본보다 클 때와 마찬가지로 주변의 가까운 픽셀 4개를 골라 섞습니다</p>
							 | 
						||
| 
								 | 
							
								</li>
							 | 
						||
| 
								 | 
							
								<li><p><code class="notranslate" translate="no">THREE.NearestMipmapNearestFilter</code></p>
							 | 
						||
| 
								 | 
							
								<p> 적절한 밉을 고른 뒤 밉에서 픽셀 하나를 선택합니다</p>
							 | 
						||
| 
								 | 
							
								</li>
							 | 
						||
| 
								 | 
							
								<li><p><code class="notranslate" translate="no">THREE.NearestMipmapLinearFilter</code></p>
							 | 
						||
| 
								 | 
							
								<p> 두 개의 밉을 골라 픽셀을 하나씩 선택한 후, 두 픽셀을 섞습니다</p>
							 | 
						||
| 
								 | 
							
								</li>
							 | 
						||
| 
								 | 
							
								<li><p><code class="notranslate" translate="no">THREE.LinearMipmapNearestFilter</code></p>
							 | 
						||
| 
								 | 
							
								<p> 적절한 밉을 고른 뒤 픽셀 4개를 골라 섞습니다</p>
							 | 
						||
| 
								 | 
							
								</li>
							 | 
						||
| 
								 | 
							
								<li><p><code class="notranslate" translate="no">THREE.LinearMipmapLinearFilter</code></p>
							 | 
						||
| 
								 | 
							
								<p>두 개의 밉을 골라 각각 픽셀을 4개씩 선택하고, 선택한 8개의 픽셀을 하나의 픽셀로 혼합합니다</p>
							 | 
						||
| 
								 | 
							
								</li>
							 | 
						||
| 
								 | 
							
								</ul>
							 | 
						||
| 
								 | 
							
								<p>아래는 6개의 필터를 각각 적용한 예제입니다.</p>
							 | 
						||
| 
								 | 
							
								<div class="spread">
							 | 
						||
| 
								 | 
							
								  <div data-diagram="filterModes" style="
							 | 
						||
| 
								 | 
							
								    height: 450px;
							 | 
						||
| 
								 | 
							
								    position: relative;
							 | 
						||
| 
								 | 
							
								  ">
							 | 
						||
| 
								 | 
							
								    <div style="
							 | 
						||
| 
								 | 
							
								      width: 100%;
							 | 
						||
| 
								 | 
							
								      height: 100%;
							 | 
						||
| 
								 | 
							
								      display: flex;
							 | 
						||
| 
								 | 
							
								      align-items: center;
							 | 
						||
| 
								 | 
							
								      justify-content: flex-start;
							 | 
						||
| 
								 | 
							
								    ">
							 | 
						||
| 
								 | 
							
								      <div style="
							 | 
						||
| 
								 | 
							
								        background: rgba(255,0,0,.8);
							 | 
						||
| 
								 | 
							
								        color: white;
							 | 
						||
| 
								 | 
							
								        padding: .5em;
							 | 
						||
| 
								 | 
							
								        margin: 1em;
							 | 
						||
| 
								 | 
							
								        font-size: small;
							 | 
						||
| 
								 | 
							
								        border-radius: .5em;
							 | 
						||
| 
								 | 
							
								        line-height: 1.2;
							 | 
						||
| 
								 | 
							
								        user-select: none;">클릭해<br>텍스처를<br>변경</div>
							 | 
						||
| 
								 | 
							
								    </div>
							 | 
						||
| 
								 | 
							
								    <div class="filter-caption" style="left: 0.5em; top: 0.5em;">nearest</div>
							 | 
						||
| 
								 | 
							
								    <div class="filter-caption" style="width: 100%; text-align: center; top: 0.5em;">linear</div>
							 | 
						||
| 
								 | 
							
								    <div class="filter-caption" style="right: 0.5em; text-align: right; top: 0.5em;">nearest<br>mipmap<br>nearest</div>
							 | 
						||
| 
								 | 
							
								    <div class="filter-caption" style="left: 0.5em; text-align: left; bottom: 0.5em;">nearest<br>mipmap<br>linear</div>
							 | 
						||
| 
								 | 
							
								    <div class="filter-caption" style="width: 100%; text-align: center; bottom: 0.5em;">linear<br>mipmap<br>nearest</div>
							 | 
						||
| 
								 | 
							
								    <div class="filter-caption" style="right: 0.5em; text-align: right; bottom: 0.5em;">linear<br>mipmap<br>linear</div>
							 | 
						||
| 
								 | 
							
								  </div>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p>여기서 주의깊게 봐야할 건 상단 왼쪽 <code class="notranslate" translate="no">NearestFilter</code>와 상단 중앙의 <code class="notranslate" translate="no">LinearFilter</code>는
							 | 
						||
| 
								 | 
							
								밉을 사용하지 않는다는 점입니다. 두 텍스처를 보면 멀리 떨어질수록 픽셀이 깜빡이는 증상이
							 | 
						||
| 
								 | 
							
								보이죠. 이는 GPU가 픽셀을 원본 텍스처에서 선택하기 때문입니다. <code class="notranslate" translate="no">NearestFilter</code>는 하나의
							 | 
						||
| 
								 | 
							
								픽셀을 선택하고, <code class="notranslate" translate="no">LinearFilter</code>는 4개의 픽셀을 선택하기는 하나, 픽셀을 제대로 표현하진
							 | 
						||
| 
								 | 
							
								못합니다. 다른 4개 예제는 그나마 낫고, 그 중 <code class="notranslate" translate="no">LinearMipmapLinearFilter</code>가 제일 깔끔해
							 | 
						||
| 
								 | 
							
								보이네요.</p>
							 | 
						||
| 
								 | 
							
								<p>위 캔버스를 클릭해보면 텍스처를 바꿀 수 있습니다. 하나는 여태까지 사용하던 텍스처이고,
							 | 
						||
| 
								 | 
							
								또 하나는 밉의 각 단계가 다른 색으로 나타나는 텍스처이죠.</p>
							 | 
						||
| 
								 | 
							
								<div class="threejs_center">
							 | 
						||
| 
								 | 
							
								  <div data-texture-diagram="differentColoredMips"></div>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p>이 텍스처는 필터의 동작 원리를 이해하기 쉽도록 해줍니다. 위 예제에서 <code class="notranslate" translate="no">NearestFilter</code>와
							 | 
						||
| 
								 | 
							
								<code class="notranslate" translate="no">LinearFilter</code>는 아주 멀리까지도 첫 번째 밉을 사용합니다. 반면 상단 오른쪽과 하단 중앙을
							 | 
						||
| 
								 | 
							
								보면 밉의 경계가 뚜렷이 보이죠.</p>
							 | 
						||
| 
								 | 
							
								<p>다시 원래 텍스처로 바꿔보면 하단 오른쪽이 가장 매끄러운 것이 보일 겁니다. 왜 항상 이
							 | 
						||
| 
								 | 
							
								필터를 쓰지 않는 걸까요? 뭐, 레트로 감성을 표현한다든가 하는 이유로 물체들이 픽셀화된
							 | 
						||
| 
								 | 
							
								것을 원할 수도 있죠. 하지만 보다 흔한 이유는 성능입니다. 8개의 픽셀 데이터를 처리하는
							 | 
						||
| 
								 | 
							
								것보다는 당연히 1개의 픽셀 데이터를 처리하는 게 훨씬 빠르겠죠. 하나의 텍스처로 이런
							 | 
						||
| 
								 | 
							
								성능 차이를 체감하기는 어렵지만, Three.js를 사용하다보면 하나의 물체에 4, 5개의 텍스처가
							 | 
						||
| 
								 | 
							
								들어가는 경우도 빈번합니다. 4개의 텍스처에서 각각 8개의 픽셀을 처리해야 하니, 이는 한
							 | 
						||
| 
								 | 
							
								프레임당 32개의 픽셀을 처리해야 함을 의미하죠. 이는 저사양 기기를 고려할 때 특히 중요히
							 | 
						||
| 
								 | 
							
								여겨야 하는 요소입니다.</p>
							 | 
						||
| 
								 | 
							
								<h2 id="-a-name-uvmanipulation-a-repeating-offseting-rotating-wrapping-"><a name="uvmanipulation"></a> 텍스처의 반복(repeating), 위치 조절(offseting), 회전(rotating), 래핑(wrapping)</h2>
							 | 
						||
| 
								 | 
							
								<p>텍스처에는 반복, 위치, 회전 설정이 있습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>Three.js는 기본적으로 텍스처를 반복하지 않습니다. 반복 여부를 설정하는
							 | 
						||
| 
								 | 
							
								2가지 속성이 있는데, 하나는 수평 래핑을 설정하는 <a href="/docs/#api/ko/textures/Texture#wrapS"><code class="notranslate" translate="no">wrapS</code></a>이고,
							 | 
						||
| 
								 | 
							
								또 하나는 수직 래핑을 설정하는 <a href="/docs/#api/ko/textures/Texture#wrapT"><code class="notranslate" translate="no">wrapT</code></a>입니다.</p>
							 | 
						||
| 
								 | 
							
								<p>두 속성은 다음 중 하나로 지정할 수 있습니다.</p>
							 | 
						||
| 
								 | 
							
								<ul>
							 | 
						||
| 
								 | 
							
								<li><p><code class="notranslate" translate="no">THREE.ClampToEdgeWrapping</code></p>
							 | 
						||
| 
								 | 
							
								<p> 텍스처의 가장자리 픽셀을 계속해서 반복합니다</p>
							 | 
						||
| 
								 | 
							
								</li>
							 | 
						||
| 
								 | 
							
								<li><p><code class="notranslate" translate="no">THREE.RepeatWrapping</code></p>
							 | 
						||
| 
								 | 
							
								<p> 텍스처 자체를 반복합니다</p>
							 | 
						||
| 
								 | 
							
								</li>
							 | 
						||
| 
								 | 
							
								<li><p><code class="notranslate" translate="no">THREE.MirroredRepeatWrapping</code></p>
							 | 
						||
| 
								 | 
							
								<p> 텍스처 자체를 반복하되, 매번 뒤집습니다.</p>
							 | 
						||
| 
								 | 
							
								</li>
							 | 
						||
| 
								 | 
							
								</ul>
							 | 
						||
| 
								 | 
							
								<p>양 방향의 래핑을 키려면 다음과 같이 설정할 수 있습니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">someTexture.wrapS = THREE.RepeatWrapping;
							 | 
						||
| 
								 | 
							
								someTexture.wrapT = THREE.RepeatWrapping;
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>반복은 <code class="notranslate" translate="no">repeat</code> 속성으로 설정할 수 있죠.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const timesToRepeatHorizontally = 4;
							 | 
						||
| 
								 | 
							
								const timesToRepeatVertically = 2;
							 | 
						||
| 
								 | 
							
								someTexture.repeat.set(timesToRepeatHorizontally, timesToRepeatVertically);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>텍스처의 위치는 <code class="notranslate" translate="no">offset</code> 속성을 설정해 조절할 수 있습니다. 텍스처 위치의 단위는
							 | 
						||
| 
								 | 
							
								텍스처의 크기와 1:1, 즉 0은 위치가 그대로인 것이고 1은 각 축에서 텍스처 크기만큼
							 | 
						||
| 
								 | 
							
								이동한 것을 의미하죠.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const xOffset = .5;   // 텍스처 너비의 반만큼 이동
							 | 
						||
| 
								 | 
							
								const yOffset = .25;  // 텍스처 높이의 1/4만큼 이동
							 | 
						||
| 
								 | 
							
								someTexture.offset.set(xOffset, yOffset);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>텍스처의 회전은 <code class="notranslate" translate="no">rotation</code> 속성을 라디안(radians) 단위로 지정해 조절할 수 있습니다.
							 | 
						||
| 
								 | 
							
								<code class="notranslate" translate="no">center</code> 속성은 회전의 중심을 정하는 데 사용하죠. <code class="notranslate" translate="no">center</code> 속성의 기본값은 <code class="notranslate" translate="no">0, 0</code>으로
							 | 
						||
| 
								 | 
							
								왼쪽 상단을 기준으로 회전하고, <code class="notranslate" translate="no">offset</code>과 마찬가지로 텍스처의 크기를 기준으로
							 | 
						||
| 
								 | 
							
								단위가 정해지기에 <code class="notranslate" translate="no">.5, .5</code>로 설정하면 텍스처의 중앙을 기준으로 회전합니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">someTexture.center.set(.5, .5);
							 | 
						||
| 
								 | 
							
								someTexture.rotation = THREE.MathUtils.degToRad(45);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>아까 작성한 예제를 수정해 위 설정을 테스트할 예제를 만들겠습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>먼저 텍스처를 별도 변수에 담아 나중에 수정할 수 있도록 합니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const texture = loader.load('resources/images/wall.jpg');
							 | 
						||
| 
								 | 
							
								const material = new THREE.MeshBasicMaterial({
							 | 
						||
| 
								 | 
							
								-  map: loader.load('resources/images/wall.jpg');
							 | 
						||
| 
								 | 
							
								+  map: texture,
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>간단한 인터페이스를 만들어보죠.
							 | 
						||
| 
								 | 
							
								다시 한 번 <a href="https://github.com/georgealways/lil-gui">lil-gui</a>가 등장할 때입니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>이전 예제처럼 간단한 헬퍼 클래스를 만들어 각도(degrees)로 값을 조절하면
							 | 
						||
| 
								 | 
							
								알아서 호도(radians)로 변환해 지정하게끔 해줍니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class DegRadHelper {
							 | 
						||
| 
								 | 
							
								  constructor(obj, prop) {
							 | 
						||
| 
								 | 
							
								    this.obj = obj;
							 | 
						||
| 
								 | 
							
								    this.prop = prop;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  get value() {
							 | 
						||
| 
								 | 
							
								    return THREE.MathUtils.radToDeg(this.obj[this.prop]);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  set value(v) {
							 | 
						||
| 
								 | 
							
								    this.obj[this.prop] = THREE.MathUtils.degToRad(v);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>또 문자열을 숫자형으로 변환시켜줄 클래스도 만듭니다. lil-gui는 값을 문자열로
							 | 
						||
| 
								 | 
							
								넘겨주는데, Three.js는 <code class="notranslate" translate="no">wrapS</code>나 <code class="notranslate" translate="no">wrapT</code> 등 enum 값을 지정할 때 숫자형만
							 | 
						||
| 
								 | 
							
								받기 때문이죠.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">class StringToNumberHelper {
							 | 
						||
| 
								 | 
							
								  constructor(obj, prop) {
							 | 
						||
| 
								 | 
							
								    this.obj = obj;
							 | 
						||
| 
								 | 
							
								    this.prop = prop;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  get value() {
							 | 
						||
| 
								 | 
							
								    return this.obj[this.prop];
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  set value(v) {
							 | 
						||
| 
								 | 
							
								    this.obj[this.prop] = parseFloat(v);
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>위에서 만든 클래스를 이용해 설정값을 조절할 GUI를 만듭니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const wrapModes = {
							 | 
						||
| 
								 | 
							
								  'ClampToEdgeWrapping': THREE.ClampToEdgeWrapping,
							 | 
						||
| 
								 | 
							
								  'RepeatWrapping': THREE.RepeatWrapping,
							 | 
						||
| 
								 | 
							
								  'MirroredRepeatWrapping': THREE.MirroredRepeatWrapping,
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function updateTexture() {
							 | 
						||
| 
								 | 
							
								  texture.needsUpdate = true;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const gui = new GUI();
							 | 
						||
| 
								 | 
							
								gui.add(new StringToNumberHelper(texture, 'wrapS'), 'value', wrapModes)
							 | 
						||
| 
								 | 
							
								  .name('texture.wrapS')
							 | 
						||
| 
								 | 
							
								  .onChange(updateTexture);
							 | 
						||
| 
								 | 
							
								gui.add(new StringToNumberHelper(texture, 'wrapT'), 'value', wrapModes)
							 | 
						||
| 
								 | 
							
								  .name('texture.wrapT')
							 | 
						||
| 
								 | 
							
								  .onChange(updateTexture);
							 | 
						||
| 
								 | 
							
								gui.add(texture.repeat, 'x', 0, 5, .01).name('texture.repeat.x');
							 | 
						||
| 
								 | 
							
								gui.add(texture.repeat, 'y', 0, 5, .01).name('texture.repeat.y');
							 | 
						||
| 
								 | 
							
								gui.add(texture.offset, 'x', -2, 2, .01).name('texture.offset.x');
							 | 
						||
| 
								 | 
							
								gui.add(texture.offset, 'y', -2, 2, .01).name('texture.offset.y');
							 | 
						||
| 
								 | 
							
								gui.add(texture.center, 'x', -.5, 1.5, .01).name('texture.center.x');
							 | 
						||
| 
								 | 
							
								gui.add(texture.center, 'y', -.5, 1.5, .01).name('texture.center.y');
							 | 
						||
| 
								 | 
							
								gui.add(new DegRadHelper(texture, 'rotation'), 'value', -360, 360)
							 | 
						||
| 
								 | 
							
								  .name('texture.rotation');
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>텍스처의 <code class="notranslate" translate="no">wrapS</code>나 <code class="notranslate" translate="no">wrapT</code> 속성을 변경할 경우 <a href="/docs/#api/ko/textures/Texture#needsUpdate"><code class="notranslate" translate="no">texture.needsUpdate</code></a>를
							 | 
						||
| 
								 | 
							
								<code class="notranslate" translate="no">true</code>로 설정해줘야 합니다. 나머지 설정만 변경한다면 굳이 이 값을 설정할 필요는 없죠.</p>
							 | 
						||
| 
								 | 
							
								<p></p><div translate="no" class="threejs_example_container notranslate">
							 | 
						||
| 
								 | 
							
								  <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube-adjust.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/textured-cube-adjust.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<p>뭔가 많은 것을 배운 것 같지만, 이는 맛보기에 불과합니다. 글을 진행하다보면
							 | 
						||
| 
								 | 
							
								텍스처의 정렬과 재질에 적용할 수 있는 다른 9가지의 텍스처에 대해 다룰 기회가
							 | 
						||
| 
								 | 
							
								있을 거예요.</p>
							 | 
						||
| 
								 | 
							
								<p>일단 다음 장에서는 <a href="lights.html">조명(lights)</a>에 대해 알아보기로 하죠.</p>
							 | 
						||
| 
								 | 
							
								<!--
							 | 
						||
| 
								 | 
							
								alpha
							 | 
						||
| 
								 | 
							
								ao
							 | 
						||
| 
								 | 
							
								env
							 | 
						||
| 
								 | 
							
								light
							 | 
						||
| 
								 | 
							
								specular
							 | 
						||
| 
								 | 
							
								bumpmap ?
							 | 
						||
| 
								 | 
							
								normalmap ?
							 | 
						||
| 
								 | 
							
								metalness
							 | 
						||
| 
								 | 
							
								roughness
							 | 
						||
| 
								 | 
							
								-->
							 | 
						||
| 
								 | 
							
								<p><link rel="stylesheet" href="../resources/threejs-textures.css"></p>
							 | 
						||
| 
								 | 
							
								<script type="module" src="../resources/threejs-textures.js"></script>
							 | 
						||
| 
								 | 
							
								        </div>
							 | 
						||
| 
								 | 
							
								      </div>
							 | 
						||
| 
								 | 
							
								    </div>
							 | 
						||
| 
								 | 
							
								  
							 | 
						||
| 
								 | 
							
								  <script src="/manual/resources/prettify.js"></script>
							 | 
						||
| 
								 | 
							
								  <script src="/manual/resources/lesson.js"></script>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								</body></html>
							 |