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.
		
		
		
		
			
				
					471 lines
				
				32 KiB
			
		
		
			
		
	
	
					471 lines
				
				32 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								<!DOCTYPE html><html lang="ko"><head>
							 | 
						||
| 
								 | 
							
								    <meta charset="utf-8">
							 | 
						||
| 
								 | 
							
								    <title>씬 그래프(Scene graph)</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 – 씬 그래프(Scene graph)">
							 | 
						||
| 
								 | 
							
								    <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>씬 그래프(Scene graph)</h1>
							 | 
						||
| 
								 | 
							
								      </div>
							 | 
						||
| 
								 | 
							
								      <div class="lesson">
							 | 
						||
| 
								 | 
							
								        <div class="lesson-main">
							 | 
						||
| 
								 | 
							
								          <p>※ 이 글은 Three.js의 튜토리얼 시리즈로서,
							 | 
						||
| 
								 | 
							
								먼저 <a href="fundamentals.html">Three.js의 기본 구조에 관한 글</a>을
							 | 
						||
| 
								 | 
							
								읽고 오길 권장합니다.</p>
							 | 
						||
| 
								 | 
							
								<p>Three.js에서 가장 중요한 것은 무엇보다 씬 그래프(Scene graph)입니다.
							 | 
						||
| 
								 | 
							
								3D 엔진에서 씬 그래프란 요소(node)의 계층 구조를 그림으로 나타낸 것으로,
							 | 
						||
| 
								 | 
							
								여기서 각 요소는 각각의 "지역 공간(local space)"을 가리킵니다.</p>
							 | 
						||
| 
								 | 
							
								<p><img src="../resources/images/scenegraph-generic.svg" align="center"></p>
							 | 
						||
| 
								 | 
							
								<p>예시가 다소 추상적이니 좀 더 이해하기 쉬운 걸 예로 들어보겠습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>태양계, 그 중에서도 태양, 지구, 달이 적당하겠네요.</p>
							 | 
						||
| 
								 | 
							
								<p><img src="../resources/images/scenegraph-solarsystem.svg" align="center"></p>
							 | 
						||
| 
								 | 
							
								<p>지구는 태양을 중심으로 공전합니다. 달은 지구를 중심으로 공전하죠.
							 | 
						||
| 
								 | 
							
								달의 공전 궤도는 원과 유사합니다. 달의 관점에서 달은 지구의 "지역
							 | 
						||
| 
								 | 
							
								공간" 안에서 공전하는 셈이죠. 태양이 봤을 때 달은 취한 사람처럼
							 | 
						||
| 
								 | 
							
								스피로그래프(spirograph, 용수철 모양의 그래프)를 그리며 돌지만,
							 | 
						||
| 
								 | 
							
								달은 그저 지구의 "지역 공간"을 도는 것에만 집중할 뿐입니다.</p>
							 | 
						||
| 
								 | 
							
								<p></p><div class="threejs_diagram_container">
							 | 
						||
| 
								 | 
							
								  <iframe class="threejs_diagram " style="width: 400px; height: 300px;" src="/manual/foo/../resources/moon-orbit.html"></iframe>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<p>좀 더 가까운 예를 들어보죠. 우리는 지구에서 살지만 지구의 자전이나
							 | 
						||
| 
								 | 
							
								자전축, 태양을 공전하는 일은 크게 신경쓰지 않습니다. 이건 지구의
							 | 
						||
| 
								 | 
							
								일이니까요. 우리가 걷거나, 뭔가를 타고 이동하거나 수영하거나 달리거나
							 | 
						||
| 
								 | 
							
								하는 일들은 지구의 일과는 무관해 보입니다. 그래서 옛날 사람들은 지구가
							 | 
						||
| 
								 | 
							
								공전, 자전한다는 사실을 쉽게 받아들이지 못했죠. 우리가 걷든, 헤엄을
							 | 
						||
| 
								 | 
							
								치든, 우리의 삶은 지구의 "지역 공간" 안에서 이루어집니다. 태양에서
							 | 
						||
| 
								 | 
							
								봤을 때 여러분은 지구를 시속 약 1,600km로 돌고 태양의 주위를 시속 약
							 | 
						||
| 
								 | 
							
								107,800km로 도는 셈이지만, 우리는 이렇게 빨리 움직이기 위해 따로
							 | 
						||
| 
								 | 
							
								노력할 필요가 없습니다. 달과 마찬가지로 우리가 신경써야 하는 건 지구의
							 | 
						||
| 
								 | 
							
								"지역 공간" 뿐이죠.</p>
							 | 
						||
| 
								 | 
							
								<p>이제 위 예제를 Three.js로 하나씩 구현해볼 겁니다. 먼저 중점에
							 | 
						||
| 
								 | 
							
								태양의 역할을 할 구체를 하나 놓는 것으로 시작하죠.</p>
							 | 
						||
| 
								 | 
							
								<p>※ 앞으로 설명할 예제는 씬 그래프를 설명하기 위해 태양, 지구, 달을
							 | 
						||
| 
								 | 
							
								활용합니다. 실제 태양, 지구, 달의 운행을 구현하려면 물리를 사용해야
							 | 
						||
| 
								 | 
							
								하지만, 목적이 씬 그래프이니 씬 그래프로 실제 운행을 모방할 것입니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// 회전값을 업데이트할 객체들
							 | 
						||
| 
								 | 
							
								const objects = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 하나의 geometry로 모든 태양, 지구, 달을 생성
							 | 
						||
| 
								 | 
							
								const radius = 1;
							 | 
						||
| 
								 | 
							
								const widthSegments = 6;
							 | 
						||
| 
								 | 
							
								const heightSegments = 6;
							 | 
						||
| 
								 | 
							
								const sphereGeometry = new THREE.SphereGeometry(
							 | 
						||
| 
								 | 
							
								    radius, widthSegments, heightSegments);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
							 | 
						||
| 
								 | 
							
								const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
							 | 
						||
| 
								 | 
							
								sunMesh.scale.set(5, 5, 5);  // 태양의 크기를 키움
							 | 
						||
| 
								 | 
							
								scene.add(sunMesh);
							 | 
						||
| 
								 | 
							
								objects.push(sunMesh);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>예제에서는 로우-폴리(low poly) 구체를 사용할 겁니다. 적도를 중심으로
							 | 
						||
| 
								 | 
							
								딱 6분할만 한 구체이죠. 이렇게 하면 자전 운동을 쉽게 확인할 수 있습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>같은 구체를 재활용할 것이므로 태양의 <code class="notranslate" translate="no">mesh</code>를 5배로 설정해줍니다.</p>
							 | 
						||
| 
								 | 
							
								<p>다음으로 <a href="/docs/#api/ko/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a>의 <code class="notranslate" translate="no">emissive(방사성)</code> 속성(property)을
							 | 
						||
| 
								 | 
							
								노랑으로 지정합니다. 퐁-메터리얼의 <code class="notranslate" translate="no">emissive</code> 속성은 빛을 반사하지 않는
							 | 
						||
| 
								 | 
							
								표면 색상으로, 대신 광원에 해당 색상이 더해집니다.</p>
							 | 
						||
| 
								 | 
							
								<p>씬 가운데에 단방향 조명(single point light)도 하나 넣습니다. 조명에
							 | 
						||
| 
								 | 
							
								대해서는 나중에 자세히 다루기로 하고, 지금은 한 점에서 발산하는 광원
							 | 
						||
| 
								 | 
							
								정도로 알아둡시다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
							 | 
						||
| 
								 | 
							
								  const color = 0xFFFFFF;
							 | 
						||
| 
								 | 
							
								  const intensity = 3;
							 | 
						||
| 
								 | 
							
								  const light = new THREE.PointLight(color, intensity);
							 | 
						||
| 
								 | 
							
								  scene.add(light);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>예제를 쉽게 확인하기 위해 카메라를 중점 바로 위에서 아래로 내려다보게
							 | 
						||
| 
								 | 
							
								설치합니다. 카메라의 시점을 바꾸는 가장 간단한 방법은 <code class="notranslate" translate="no">lookAt</code> 메서드를
							 | 
						||
| 
								 | 
							
								활용하는 것으로, 이 메서드는 카메라가 넘겨받은 좌표를 바라보게끔 회전시켜줍니다.
							 | 
						||
| 
								 | 
							
								하지만 이전에 먼저 카메라에게 어떤 방향이 위인지 알려줘야 합니다. 대부분의
							 | 
						||
| 
								 | 
							
								경우 양의 y(positive y) 방향을 위로 설정하면 되지만, 예제의 경우 위에서
							 | 
						||
| 
								 | 
							
								아래를 내려다 볼 것이므로 양의 z 방향이 위가 됩니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
							 | 
						||
| 
								 | 
							
								camera.position.set(0, 50, 0);
							 | 
						||
| 
								 | 
							
								camera.up.set(0, 0, 1);
							 | 
						||
| 
								 | 
							
								camera.lookAt(0, 0, 0);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>이전 예제처럼 렌더링 루프에서 <code class="notranslate" translate="no">objects</code> 배열의 모든 객체를 회전시키겠습니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">objects.forEach((obj) => {
							 | 
						||
| 
								 | 
							
								  obj.rotation.y = time;
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p><code class="notranslate" translate="no">sunMesh</code>를 <code class="notranslate" translate="no">objects</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/scenegraph-sun.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/scenegraph-sun.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<p>다음으로 지구를 추가하겠습니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
							 | 
						||
| 
								 | 
							
								const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
							 | 
						||
| 
								 | 
							
								earthMesh.position.x = 10;
							 | 
						||
| 
								 | 
							
								scene.add(earthMesh);
							 | 
						||
| 
								 | 
							
								objects.push(earthMesh);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>지구는 푸른색을 사용했으나, 약간의 <em>방사성(emissive)</em> 파랑을 섞어
							 | 
						||
| 
								 | 
							
								검은 배경에서 잘 보이도록 만들었습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>그리고 이전에 썼던 <code class="notranslate" translate="no">sphereGeometry</code>와 방금 만든 <code class="notranslate" translate="no">earthMaterial</code>을
							 | 
						||
| 
								 | 
							
								이용해 <code class="notranslate" translate="no">earthMesh</code>를 만들고, 태양의 10칸 옆에 위치하도록 설정한 뒤
							 | 
						||
| 
								 | 
							
								씬에 추가했습니다. 마지막으로 <code class="notranslate" translate="no">objects</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/scenegraph-sun-earth.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<p>하지만 지구가 태양의 주위를 돌진 않습니다. 지구를 바로 씬에 추가하는
							 | 
						||
| 
								 | 
							
								대신, 태양의 자식으로 추가하면...</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-scene.add(earthMesh);
							 | 
						||
| 
								 | 
							
								+sunMesh.add(earthMesh);
							 | 
						||
| 
								 | 
							
								</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/scenegraph-sun-earth-orbit.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-orbit.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<p>뭔가 이상합니다. 왜 지구의 크기와 태양의 크기가 같고 또 왜 저렇게
							 | 
						||
| 
								 | 
							
								멀리 떨어졌을까요? 기존 카메라로는 지구가 보이지 않아 카메라의 위치도
							 | 
						||
| 
								 | 
							
								150칸 위로 옮겼습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>방금 우리는 <code class="notranslate" translate="no">earthMesh</code>를 <code class="notranslate" translate="no">sunMesh</code>의 자식으로 추가했습니다. 이전에
							 | 
						||
| 
								 | 
							
								<code class="notranslate" translate="no">sunMesh</code>를 만들 때 <code class="notranslate" translate="no">sunMesh.scale.set(5, 5, 5)</code>라는 코드로 크기를
							 | 
						||
| 
								 | 
							
								5배로 설정했죠. 이는 <code class="notranslate" translate="no">sunMesh</code>의 "지역 공간" 자체를 5배 키우겠다는
							 | 
						||
| 
								 | 
							
								의미입니다. 그래서 지구의 크기도 5배가 되었고, 거리(<code class="notranslate" translate="no">earthMesh.position.x = 10</code>)도
							 | 
						||
| 
								 | 
							
								5배로 적용된 것이죠.</p>
							 | 
						||
| 
								 | 
							
								<p>현재 예제의 씬 그래프는 다음과 같습니다.</p>
							 | 
						||
| 
								 | 
							
								<p><img src="../resources/images/scenegraph-sun-earth.svg" align="center"></p>
							 | 
						||
| 
								 | 
							
								<p>이를 해결하기 위해 빈 씬 그래프 요소를 하나 추가합니다. 그리고 태양과
							 | 
						||
| 
								 | 
							
								지구 둘 다 이 요소의 자식으로 추가할 겁니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const solarSystem = new THREE.Object3D();
							 | 
						||
| 
								 | 
							
								+scene.add(solarSystem);
							 | 
						||
| 
								 | 
							
								+objects.push(solarSystem);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
							 | 
						||
| 
								 | 
							
								const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
							 | 
						||
| 
								 | 
							
								sunMesh.scale.set(5, 5, 5);
							 | 
						||
| 
								 | 
							
								-scene.add(sunMesh);
							 | 
						||
| 
								 | 
							
								+solarSystem.add(sunMesh);
							 | 
						||
| 
								 | 
							
								objects.push(sunMesh);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
							 | 
						||
| 
								 | 
							
								const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
							 | 
						||
| 
								 | 
							
								earthMesh.position.x = 10;
							 | 
						||
| 
								 | 
							
								-sunMesh.add(earthMesh);
							 | 
						||
| 
								 | 
							
								+solarSystem.add(earthMesh);
							 | 
						||
| 
								 | 
							
								objects.push(earthMesh);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>여기서는 <a href="/docs/#api/ko/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>를 생성했습니다. <a href="/docs/#api/ko/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>는 <a href="/docs/#api/ko/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a>와 마찬가지로
							 | 
						||
| 
								 | 
							
								씬 그래프의 한 요소지만, <code class="notranslate" translate="no">material</code>이나 <code class="notranslate" translate="no">geometry</code>가 없다는 점이 다릅니다.
							 | 
						||
| 
								 | 
							
								그저 하나의 빈 "지역 공간"인 셈이죠.</p>
							 | 
						||
| 
								 | 
							
								<p>이제 씬 그래프는 다음과 같습니다.</p>
							 | 
						||
| 
								 | 
							
								<p><img src="../resources/images/scenegraph-sun-earth-fixed.svg" align="center"></p>
							 | 
						||
| 
								 | 
							
								<p><code class="notranslate" translate="no">sunMesh</code>와 <code class="notranslate" translate="no">earthMesh</code>는 <code class="notranslate" translate="no">solarSystem</code>의 자식입니다. 이 3 객체는 각각
							 | 
						||
| 
								 | 
							
								회전하죠. 이제 <code class="notranslate" translate="no">earthMesh</code>는 <code class="notranslate" translate="no">sunMesh</code>의 자식이 아니므로 5배 커지지도
							 | 
						||
| 
								 | 
							
								않았습니다.</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/scenegraph-sun-earth-orbit-fixed.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-orbit-fixed.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<p>훨씬 낫네요. 지구는 태양보다 작고 태양을 공전하는 동시에 자전까지 합니다.</p>
							 | 
						||
| 
								 | 
							
								<p>같은 패턴으로 달도 추가해봅시다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const earthOrbit = new THREE.Object3D();
							 | 
						||
| 
								 | 
							
								+earthOrbit.position.x = 10;
							 | 
						||
| 
								 | 
							
								+solarSystem.add(earthOrbit);
							 | 
						||
| 
								 | 
							
								+objects.push(earthOrbit);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
							 | 
						||
| 
								 | 
							
								const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
							 | 
						||
| 
								 | 
							
								-solarSystem.add(earthMesh);
							 | 
						||
| 
								 | 
							
								+earthOrbit.add(earthMesh);
							 | 
						||
| 
								 | 
							
								objects.push(earthMesh);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+const moonOrbit = new THREE.Object3D();
							 | 
						||
| 
								 | 
							
								+moonOrbit.position.x = 2;
							 | 
						||
| 
								 | 
							
								+earthOrbit.add(moonOrbit);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+const moonMaterial = new THREE.MeshPhongMaterial({color: 0x888888, emissive: 0x222222});
							 | 
						||
| 
								 | 
							
								+const moonMesh = new THREE.Mesh(sphereGeometry, moonMaterial);
							 | 
						||
| 
								 | 
							
								+moonMesh.scale.set(.5, .5, .5);
							 | 
						||
| 
								 | 
							
								+moonOrbit.add(moonMesh);
							 | 
						||
| 
								 | 
							
								+objects.push(moonMesh);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>이전처럼 <a href="/docs/#api/ko/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>를 이용해 <code class="notranslate" translate="no">eathOrbit</code> "지역 공간"을 만들고 거기에
							 | 
						||
| 
								 | 
							
								<code class="notranslate" translate="no">earthMesh</code>와 <code class="notranslate" translate="no">moonMesh</code>를 추가했습니다. 씬 그래프는 다음과 같죠.</p>
							 | 
						||
| 
								 | 
							
								<p><img src="../resources/images/scenegraph-sun-earth-moon.svg" align="center"></p>
							 | 
						||
| 
								 | 
							
								<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/scenegraph-sun-earth-moon.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-moon.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<p>처음에 봤던 예제처럼 달이 스피로그래프를 그리며 돌지만, 복잡한 수학적
							 | 
						||
| 
								 | 
							
								연산이 하나도 들어가지 않았습니다. 우리가 한 건 씬 그래프에게 그 연산을
							 | 
						||
| 
								 | 
							
								대신 맡긴 것 뿐이죠.</p>
							 | 
						||
| 
								 | 
							
								<p>때론 씬 그래프의 요소를 시각화하는 것이 도움이 될 때도 있습니다.
							 | 
						||
| 
								 | 
							
								Three.js는 유용한.. 음... 그러니까 이 <del>거시기</del>를 도와줄
							 | 
						||
| 
								 | 
							
								헬퍼 클래스가 있습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>그 중 하나는 <a href="/docs/#api/ko/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a>로, 이 클래스는 지역
							 | 
						||
| 
								 | 
							
								<span style="color:red">X</span>,
							 | 
						||
| 
								 | 
							
								<span style="color:green">Y</span>,
							 | 
						||
| 
								 | 
							
								<span style="color:blue">Z</span> 축을 표시해줍니다.
							 | 
						||
| 
								 | 
							
								한 번 여태까지 만든 요소에 모두 추가해보죠.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// AxesHelper 클래스를 각 요소에 지정
							 | 
						||
| 
								 | 
							
								objects.forEach((node) => {
							 | 
						||
| 
								 | 
							
								  const axes = new THREE.AxesHelper();
							 | 
						||
| 
								 | 
							
								  axes.material.depthTest = false;
							 | 
						||
| 
								 | 
							
								  axes.renderOrder = 1;
							 | 
						||
| 
								 | 
							
								  node.add(axes);
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>우리는 축이 구체 내부에 있더라도 전부 보이길 원하므로, 각 축의 <code class="notranslate" translate="no">depthTest</code>를
							 | 
						||
| 
								 | 
							
								<code class="notranslate" translate="no">false</code>로 설정합니다. 이러면 Three.js는 어떤 물체 뒤에 있는 요소를 그릴지
							 | 
						||
| 
								 | 
							
								말지 검사하는 과정을 생략하므로, 어떤 방향에서라도 축을 볼 수 있습니다. 그리고
							 | 
						||
| 
								 | 
							
								<code class="notranslate" translate="no">renderOrder</code>를 1로 설정(기본값은 0)해 구체를 전부 렌더링한 후 축을 렌더링하도록
							 | 
						||
| 
								 | 
							
								합니다. 그렇지 않으면 축을 그린 후 구체가 그려져 보이지 않을 수도 있으니까요.</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/scenegraph-sun-earth-moon-axes.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-moon-axes.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<p><span style="color:red">x축(빨강)</span> 그리고
							 | 
						||
| 
								 | 
							
								<span style="color:blue">z축(파랑)</span> 축이 보이나요? 카메라가 바로 위에서
							 | 
						||
| 
								 | 
							
								아래를 내려다 보고, 각 물체도 y축을 따라 회전하므로 <span style="color:green">y축(초록)</span>은
							 | 
						||
| 
								 | 
							
								보여도 거의 점처럼 보일 겁니다.</p>
							 | 
						||
| 
								 | 
							
								<p>몇몇 축은 2개의 축이 겹쳐져 구별이 어려울 수 있습니다. <code class="notranslate" translate="no">sunMesh</code>와 <code class="notranslate" translate="no">solarSystem</code>,
							 | 
						||
| 
								 | 
							
								<code class="notranslate" translate="no">earthMesh</code>와 <code class="notranslate" translate="no">earthOrbit</code>이 같은 위치에 있기 때문이죠. 각 노드의 축을 켜고
							 | 
						||
| 
								 | 
							
								끌 수 있는 간단한 컨트롤 패널을 한 번 만들어보죠. 동시에 다른 헬퍼 클래스인
							 | 
						||
| 
								 | 
							
								<a href="/docs/#api/ko/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a>도 추가해보겠습니다. <a href="/docs/#api/ko/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a>는 X, Z축으로 2D 격자(grid)를
							 | 
						||
| 
								 | 
							
								만다는 클래스로, 기본값은 10x10 칸입니다.</p>
							 | 
						||
| 
								 | 
							
								<p>또 Three.js와 함께 사용하기로 유명한 <a href="https://github.com/georgealways/lil-gui">lil-gui</a>도
							 | 
						||
| 
								 | 
							
								사용할 겁니다. lil-gui는 UI 라이브러리로, 객체와 속성 이름을 넘겨받고, 해당 속성의
							 | 
						||
| 
								 | 
							
								타입을 기반으로 속성값을 UI로 조정할 수 있게 해줍니다.</p>
							 | 
						||
| 
								 | 
							
								<p>각 요소에 <a href="/docs/#api/ko/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a>와 <a href="/docs/#api/ko/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a>를 추가하겠습니다. 각 노드에 헬퍼를
							 | 
						||
| 
								 | 
							
								추가하기 위해 각 노드의 이름이 필요하니, 기존 렌더링 루프를 제거하고 특정
							 | 
						||
| 
								 | 
							
								함수를 호출하게 변경하겠습니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">-// add an AxesHelper to each node
							 | 
						||
| 
								 | 
							
								-objects.forEach((node) => {
							 | 
						||
| 
								 | 
							
								-  const axes = new THREE.AxesHelper();
							 | 
						||
| 
								 | 
							
								-  axes.material.depthTest = false;
							 | 
						||
| 
								 | 
							
								-  axes.renderOrder = 1;
							 | 
						||
| 
								 | 
							
								-  node.add(axes);
							 | 
						||
| 
								 | 
							
								-});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								+function makeAxisGrid(node, label, units) {
							 | 
						||
| 
								 | 
							
								+  const helper = new AxisGridHelper(node, units);
							 | 
						||
| 
								 | 
							
								+  gui.add(helper, 'visible').name(label);
							 | 
						||
| 
								 | 
							
								+}
							 | 
						||
| 
								 | 
							
								+
							 | 
						||
| 
								 | 
							
								+makeAxisGrid(solarSystem, 'solarSystem', 25);
							 | 
						||
| 
								 | 
							
								+makeAxisGrid(sunMesh, 'sunMesh');
							 | 
						||
| 
								 | 
							
								+makeAxisGrid(earthOrbit, 'earthOrbit');
							 | 
						||
| 
								 | 
							
								+makeAxisGrid(earthMesh, 'earthMesh');
							 | 
						||
| 
								 | 
							
								+makeAxisGrid(moonMesh, 'moonMesh');
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p><code class="notranslate" translate="no">makeAxisGrid</code> 함수는 나중에 만들 <code class="notranslate" translate="no">AxisGridHelper</code>를 생성하여
							 | 
						||
| 
								 | 
							
								lil-gui에 붙이는 역할을 합니다. 예제에서는 체크박스를 만들 것이므로,
							 | 
						||
| 
								 | 
							
								<code class="notranslate" translate="no">boolean</code> 타입으로 속성을 지정해주겠습니다. 또 하나의 속성이 바뀔 때
							 | 
						||
| 
								 | 
							
								축과 격자가 동시에 나타나고 사라지게 할 것이니 getter와 setter가
							 | 
						||
| 
								 | 
							
								있는 간단한 클래스를 하나 만들겠습니다. 이러면 lil-gui가 하나의
							 | 
						||
| 
								 | 
							
								속성을 바꿀 때 요소의 <a href="/docs/#api/ko/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a>와 <a href="/docs/#api/ko/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a>의 속성을
							 | 
						||
| 
								 | 
							
								동시에 조작할 수 있죠.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">/* 
							 | 
						||
| 
								 | 
							
								 * 축과 격자를 동시에 켜고 끕니다
							 | 
						||
| 
								 | 
							
								 * lil-gui가 체크박스를 만들게 하려면 boolean 타입의
							 | 
						||
| 
								 | 
							
								 * 속성을 지정해줘야 하므로, `visible` 속성에
							 | 
						||
| 
								 | 
							
								 * getter와 setter를 지정해 lil-gui가 이 속성을
							 | 
						||
| 
								 | 
							
								 * 바라보도록 합니다
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class AxisGridHelper {
							 | 
						||
| 
								 | 
							
								  constructor(node, units = 10) {
							 | 
						||
| 
								 | 
							
								    const axes = new THREE.AxesHelper();
							 | 
						||
| 
								 | 
							
								    axes.material.depthTest = false;
							 | 
						||
| 
								 | 
							
								    axes.renderOrder = 2;  // 격자 다음에 렌더링
							 | 
						||
| 
								 | 
							
								    node.add(axes);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const grid = new THREE.GridHelper(units, units);
							 | 
						||
| 
								 | 
							
								    grid.material.depthTest = false;
							 | 
						||
| 
								 | 
							
								    grid.renderOrder = 1;
							 | 
						||
| 
								 | 
							
								    node.add(grid);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.grid = grid;
							 | 
						||
| 
								 | 
							
								    this.axes = axes;
							 | 
						||
| 
								 | 
							
								    this.visible = false;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  get visible() {
							 | 
						||
| 
								 | 
							
								    return this._visible;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  set visible(v) {
							 | 
						||
| 
								 | 
							
								    this._visible = v;
							 | 
						||
| 
								 | 
							
								    this.grid.visible = v;
							 | 
						||
| 
								 | 
							
								    this.axes.visible = v;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>격자가 축을 가릴 수 있으니, <a href="/docs/#api/ko/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a>의 <code class="notranslate" translate="no">renderOrder</code>를
							 | 
						||
| 
								 | 
							
								2로 설정하고 <a href="/docs/#api/ko/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a>를 2로 설정해 축을 격자 다음에
							 | 
						||
| 
								 | 
							
								렌더링하도록 합니다.</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/scenegraph-sun-earth-moon-axes-grids.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-moon-axes-grids.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<p><code class="notranslate" translate="no">solarSystem</code>을 체크하면 위에서 설정했듯 지구가 정확히 중앙으로부터
							 | 
						||
| 
								 | 
							
								10칸 떨어진 것을 확인할 수 있습니다. 지구가 <code class="notranslate" translate="no">solarSystem</code> "지역 공간"
							 | 
						||
| 
								 | 
							
								안에 있는 것도 확인할 수 있죠. <code class="notranslate" translate="no">earthOrbit</code>을 켜면 달도 마찬가지로
							 | 
						||
| 
								 | 
							
								<code class="notranslate" translate="no">earthOrbit</code>의 "지역 공간"의 중심으로부터 정확히 2칸 떨어진 것을
							 | 
						||
| 
								 | 
							
								확인할 수 있을 겁니다.</p>
							 | 
						||
| 
								 | 
							
								<p>씬 그래프의 다른 예시로 자동차를 들 수 있습니다.</p>
							 | 
						||
| 
								 | 
							
								<p><img src="../resources/images/scenegraph-car.svg" align="center"></p>
							 | 
						||
| 
								 | 
							
								<p>차체(Car body)를 움직이면 바퀴(wheel)도 같이 움직입니다. 차체가
							 | 
						||
| 
								 | 
							
								바퀴와는 별도로 튀게 하려면(서스펜션. 역주) 차체와 바퀴를 하나의
							 | 
						||
| 
								 | 
							
								차체의 "프레임" 요소의 자식으로 설정할 수 있죠.</p>
							 | 
						||
| 
								 | 
							
								<p>다른 예로 게임 속 인간형 캐릭터를 한 번 봅시다.</p>
							 | 
						||
| 
								 | 
							
								<p><img src="../resources/images/scenegraph-human.svg" align="center"></p>
							 | 
						||
| 
								 | 
							
								<p>인간형 캐릭터의 씬 그래프는 꽤 복잡하네요. 위 씬 그래프는 상당히 축소된
							 | 
						||
| 
								 | 
							
								버젼인데도 말이죠. 좀 더 세세하게 만든다면 손가락 하나하나(최소한 28마디)와
							 | 
						||
| 
								 | 
							
								발가락 하나하나(또 다른 28마디), 얼굴과 턱, 눈 등등으로 나눠야 합니다.</p>
							 | 
						||
| 
								 | 
							
								<p>약간 복잡한 씬 그래프를 만들어 봅시다. 탱크가 좋겠네요. 바퀴 6개와
							 | 
						||
| 
								 | 
							
								포탑으로 이루어진 간단한 탱크입니다. 또 탱크의 주위를 돌아다니는 구체를
							 | 
						||
| 
								 | 
							
								하나 만들어 탱크가 그 구체를 조준하도록 해보겠습니다.</p>
							 | 
						||
| 
								 | 
							
								<p>아래는 예제를 구현하기 위한 씬 그래프입니다. <code class="notranslate" translate="no">mesh</code>는 녹색으로 칠했고,
							 | 
						||
| 
								 | 
							
								<a href="/docs/#api/ko/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>는 청색, 광원은 갈색, 카메라는 보라색으로 칠했습니다. 하나의
							 | 
						||
| 
								 | 
							
								카메라는 씬 그래프에 포함하지 않았습니다.</p>
							 | 
						||
| 
								 | 
							
								<div class="threejs_center"><img src="../resources/images/scenegraph-tank.svg" style="width: 800px;"></div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p>모든 요소를 어떻게 설정했는지 코드를 하나씩 살펴보죠.</p>
							 | 
						||
| 
								 | 
							
								<p>탱크가 조준할 목표를 만들기 위해 먼저 위 예제의 <code class="notranslate" translate="no">earthOrbit</code>과 유사한
							 | 
						||
| 
								 | 
							
								<code class="notranslate" translate="no">targetOrbit</code>(<a href="/docs/#api/ko/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>)을 만듭니다. 그리고 <code class="notranslate" translate="no">targetOrbit</code>의 상대 좌표를 넘겨줄
							 | 
						||
| 
								 | 
							
								<code class="notranslate" translate="no">targetElevation</code>(<a href="/docs/#api/ko/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>)을 만들어 <code class="notranslate" translate="no">targetOrbit</code>의 자식으로 추가한 뒤,
							 | 
						||
| 
								 | 
							
								또 다른 <a href="/docs/#api/ko/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>, <code class="notranslate" translate="no">targetBob</code>을 만들어 <code class="notranslate" translate="no">targetElevation</code>의 자식으로 추가합니다.
							 | 
						||
| 
								 | 
							
								이 <code class="notranslate" translate="no">targetBob</code>은 위아래로 보빙(bob은 낙시찌, 권투에서 bobbing은 몸을 숙이는 동작을 말함. 역주)하는
							 | 
						||
| 
								 | 
							
								역할을 할 겁니다. 마지막으로 색이 색이 바뀌는 동시에 회전할 <code class="notranslate" translate="no">targetMesh</code> 육면체를
							 | 
						||
| 
								 | 
							
								만듭니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// 움직이는 목표
							 | 
						||
| 
								 | 
							
								targetOrbit.rotation.y = time * .27;
							 | 
						||
| 
								 | 
							
								targetBob.position.y = Math.sin(time * 2) * 4;
							 | 
						||
| 
								 | 
							
								targetMesh.rotation.x = time * 7;
							 | 
						||
| 
								 | 
							
								targetMesh.rotation.y = time * 13;
							 | 
						||
| 
								 | 
							
								targetMaterial.emissive.setHSL(time * 10 % 1, 1, .25);
							 | 
						||
| 
								 | 
							
								targetMaterial.color.setHSL(time * 10 % 1, 1, .25);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>탱크는 먼저 <code class="notranslate" translate="no">tank</code>라는 이름으로 다른 요소를 감쌀 <a href="/docs/#api/ko/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>를 하나 생성합니다.
							 | 
						||
| 
								 | 
							
								예제에서는 커브에 따라 위치값을 반환받을 수 있는 <a href="/docs/#api/ko/extras/curves/SplineCurve"><code class="notranslate" translate="no">SplineCurve</code></a>를 이용하겠습니다.
							 | 
						||
| 
								 | 
							
								0.0은 커브의 시작점이고, 1.0은 커브의 끝점으로, 먼저 탱크의 위치를 넘겨주어 탱크의
							 | 
						||
| 
								 | 
							
								다음 위치를 정한 뒤(아래 <code class="notranslate" translate="no">tankPosition</code>. 역주), 커브의 다음 값을 받아 탱크가 어디를
							 | 
						||
| 
								 | 
							
								바라봐야할지 구합니다(아래 <code class="notranslate" translate="no">tankTarget</code>. 역주). 그리고 구한 값을 <a href="/docs/#api/ko/core/Object3D.lookAt"><code class="notranslate" translate="no">Object3D.lookAt</code></a>
							 | 
						||
| 
								 | 
							
								메서드에 넘겨주어 탱크가 그 방향을 바라보도록 합니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const tankPosition = new THREE.Vector2();
							 | 
						||
| 
								 | 
							
								const tankTarget = new THREE.Vector2();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// move tank
							 | 
						||
| 
								 | 
							
								const tankTime = time * .05;
							 | 
						||
| 
								 | 
							
								curve.getPointAt(tankTime % 1, tankPosition);
							 | 
						||
| 
								 | 
							
								curve.getPointAt((tankTime + 0.01) % 1, tankTarget);
							 | 
						||
| 
								 | 
							
								tank.position.set(tankPosition.x, 0, tankPosition.y);
							 | 
						||
| 
								 | 
							
								tank.lookAt(tankTarget.x, 0, tankTarget.y);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>그 다음 탱크의 포탑을 탱크의 자식으로 지정해서 탱크를 따라 움직이게 합니다.
							 | 
						||
| 
								 | 
							
								그리고 목표물의 전역 위치값(global position)을 구한 뒤 <a href="/docs/#api/ko/core/Object3D.lookAt"><code class="notranslate" translate="no">Object3D.lookAt</code></a>
							 | 
						||
| 
								 | 
							
								메서드를 이용, 포탑이 목표물을 조준하게 합니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const targetPosition = new THREE.Vector3();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								...
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 목표를 조준하도록
							 | 
						||
| 
								 | 
							
								targetMesh.getWorldPosition(targetPosition);
							 | 
						||
| 
								 | 
							
								turretPivot.lookAt(targetPosition);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p><code class="notranslate" translate="no">turretCamera</code>를 <code class="notranslate" translate="no">turretMesh</code>의 자식으로 지정해 포탑과 함께 카메라가
							 | 
						||
| 
								 | 
							
								움직이도록 설정합니다. 또 카메라도 목표물을 바라보게 변경합니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// 포탑 카메라가 목표물을 바라보도록
							 | 
						||
| 
								 | 
							
								turretCamera.lookAt(targetPosition);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p><code class="notranslate" translate="no">targetCameraPivot</code>은 <code class="notranslate" translate="no">targetBob</code>의 자식으로 지정해 목표물과 함께
							 | 
						||
| 
								 | 
							
								돌아다니도록 하고, 탱크의 뒤쪽을 바라보도록 합니다. 이는 <code class="notranslate" translate="no">targetCamera</code>가
							 | 
						||
| 
								 | 
							
								목표물의 위치에서 살짝 벗어나게 하기 위함으로, 만약 카메라를 <code class="notranslate" translate="no">targetBob</code>의
							 | 
						||
| 
								 | 
							
								자식으로 바로 추가한다면 목표물 안에서 탱크를 보게 될 겁니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// targetCameraPivot이 탱크를 바라보도록
							 | 
						||
| 
								 | 
							
								tank.getWorldPosition(targetPosition);
							 | 
						||
| 
								 | 
							
								targetCameraPivot.lookAt(targetPosition);
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>다음으로 바퀴를 회전시킵니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">wheelMeshes.forEach((obj) => {
							 | 
						||
| 
								 | 
							
								  obj.rotation.x = time * 3;
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>그리고 카메라를 간단한 설명과 함께 배열로 묶은 뒤,</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cameras = [
							 | 
						||
| 
								 | 
							
								  { cam: camera, desc: 'detached camera', },
							 | 
						||
| 
								 | 
							
								  { cam: turretCamera, desc: 'on turret looking at target', },
							 | 
						||
| 
								 | 
							
								  { cam: targetCamera, desc: 'near target looking at tank', },
							 | 
						||
| 
								 | 
							
								  { cam: tankCamera, desc: 'above back of tank', },
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const infoElem = document.querySelector('#info');
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<p>시간에 따라 카메라를 변경하도록 합니다.</p>
							 | 
						||
| 
								 | 
							
								<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const camera = cameras[time * .25 % cameras.length | 0];
							 | 
						||
| 
								 | 
							
								infoElem.textContent = camera.desc;
							 | 
						||
| 
								 | 
							
								</pre>
							 | 
						||
| 
								 | 
							
								<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/scenegraph-tank.html"></iframe></div>
							 | 
						||
| 
								 | 
							
								  <a class="threejs_center" href="/manual/examples/scenegraph-tank.html" target="_blank">새 탭에서 보기</a>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<p></p>
							 | 
						||
| 
								 | 
							
								<p>자, 이번 장은 여기까지입니다. 이 글이 씬 그래프가 어떻게 작동하는지,
							 | 
						||
| 
								 | 
							
								어떻게 사용해야할지 감을 잡는 데 도움이 되었으면 좋겠네요. <a href="/docs/#api/ko/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>
							 | 
						||
| 
								 | 
							
								요소를 만들어 부모로 만드는 것은 Three.js 뿐만 아니라 다른 3D 엔진을
							 | 
						||
| 
								 | 
							
								쓸 때도 중요한 요소입니다. 뭔가를 만들다보면 종종 복잡한 수학이 필요한
							 | 
						||
| 
								 | 
							
								것처럼 느껴질 수 있는데, 이때 씬 그래프를 사용하지 않는다면 달의 궤도를
							 | 
						||
| 
								 | 
							
								계산하거나 자동차 바퀴의 위치를 계산하는 건 굉장히 복잡할 겁니다. 씬
							 | 
						||
| 
								 | 
							
								그래프를 적절히 활용하면 이런 복잡한 동작을 더 쉽게 구현할 수 있죠.</p>
							 | 
						||
| 
								 | 
							
								<p><a href="materials.html">다음 장에서는 <code class="notranslate" translate="no">재질(material)</code>에 대해 알아보겠습니다</a>.</p>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        </div>
							 | 
						||
| 
								 | 
							
								      </div>
							 | 
						||
| 
								 | 
							
								    </div>
							 | 
						||
| 
								 | 
							
								  
							 | 
						||
| 
								 | 
							
								  <script src="/manual/resources/prettify.js"></script>
							 | 
						||
| 
								 | 
							
								  <script src="/manual/resources/lesson.js"></script>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								</body></html>
							 |