scenegraph.html 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. <!DOCTYPE html><html lang="en"><head>
  2. <meta charset="utf-8">
  3. <title>Scene Graph</title>
  4. <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
  5. <meta name="twitter:card" content="summary_large_image">
  6. <meta name="twitter:site" content="@threejs">
  7. <meta name="twitter:title" content="Three.js – Scene Graph">
  8. <meta property="og:image" content="https://threejs.org/files/share.png">
  9. <link rel="shortcut icon" href="/files/favicon_white.ico" media="(prefers-color-scheme: dark)">
  10. <link rel="shortcut icon" href="/files/favicon.ico" media="(prefers-color-scheme: light)">
  11. <link rel="stylesheet" href="/manual/resources/lesson.css">
  12. <link rel="stylesheet" href="/manual/resources/lang.css">
  13. <!-- Import maps polyfill -->
  14. <!-- Remove this when import maps will be widely supported -->
  15. <script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>
  16. <script type="importmap">
  17. {
  18. "imports": {
  19. "three": "../../build/three.module.js"
  20. }
  21. }
  22. </script>
  23. </head>
  24. <body>
  25. <div class="container">
  26. <div class="lesson-title">
  27. <h1>Scene Graph</h1>
  28. </div>
  29. <div class="lesson">
  30. <div class="lesson-main">
  31. <p>This article is part of a series of articles about three.js. The
  32. first article is <a href="fundamentals.html">three.js fundamentals</a>. If
  33. you haven't read that yet you might want to consider starting there.</p>
  34. <p>Three.js's core is arguably its scene graph. A scene graph in a 3D
  35. engine is a hierarchy of nodes in a graph where each node represents
  36. a local space.</p>
  37. <p><img src="../resources/images/scenegraph-generic.svg" align="center"></p>
  38. <p>That's kind of abstract so let's try to give some examples.</p>
  39. <p>One example might be solar system, sun, earth, moon.</p>
  40. <p><img src="../resources/images/scenegraph-solarsystem.svg" align="center"></p>
  41. <p>The Earth orbits the Sun. The Moon orbits the Earth. The Moon
  42. moves in a circle around the Earth. From the Moon's point of
  43. view it's rotating in the "local space" of the Earth. Even though
  44. its motion relative to the Sun is some crazy spirograph like
  45. curve from the Moon's point of view it just has to concern itself with rotating
  46. around the Earth's local space.</p>
  47. <p></p><div class="threejs_diagram_container">
  48. <iframe class="threejs_diagram " style="width: 400px; height: 300px;" src="/manual/foo/../resources/moon-orbit.html"></iframe>
  49. </div>
  50. <p></p>
  51. <p>To think of it another way, you living on the Earth do not have to think
  52. about the Earth's rotation on its axis nor its rotation around the
  53. Sun. You just walk or drive or swim or run as though the Earth is
  54. not moving or rotating at all. You walk, drive, swim, run, and live
  55. in the Earth's "local space" even though relative to the sun you are
  56. spinning around the earth at around 1000 miles per hour and around
  57. the sun at around 67,000 miles per hour. Your position in the solar
  58. system is similar to that of the moon above but you don't have to concern
  59. yourself. You just worry about your position relative to the earth in its
  60. "local space".</p>
  61. <p>Let's take it one step at a time. Imagine we want to make
  62. a diagram of the sun, earth, and moon. We'll start with the sun by
  63. just making a sphere and putting it at the origin. Note: We're using
  64. sun, earth, moon as a demonstration of how to use a scene graph. Of course
  65. the real sun, earth, and moon use physics but for our purposes we'll
  66. fake it with a scene graph.</p>
  67. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">// an array of objects whose rotation to update
  68. const objects = [];
  69. // use just one sphere for everything
  70. const radius = 1;
  71. const widthSegments = 6;
  72. const heightSegments = 6;
  73. const sphereGeometry = new THREE.SphereGeometry(
  74. radius, widthSegments, heightSegments);
  75. const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
  76. const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
  77. sunMesh.scale.set(5, 5, 5); // make the sun large
  78. scene.add(sunMesh);
  79. objects.push(sunMesh);
  80. </pre>
  81. <p>We're using a really low-polygon sphere. Only 6 subdivisions around its equator.
  82. This is so it's easy to see the rotation.</p>
  83. <p>We're going to reuse the same sphere for everything so we'll set a scale
  84. for the sun mesh of 5x.</p>
  85. <p>We also set the phong material's <code class="notranslate" translate="no">emissive</code> property to yellow. A phong material's
  86. emissive property is basically the color that will be drawn with no light hitting
  87. the surface. Light is added to that color.</p>
  88. <p>Let's also put a single point light in the center of the scene. We'll go into more
  89. details about point lights later but for now the simple version is a point light
  90. represents light that emanates from a single point.</p>
  91. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  92. const color = 0xFFFFFF;
  93. const intensity = 3;
  94. const light = new THREE.PointLight(color, intensity);
  95. scene.add(light);
  96. }
  97. </pre>
  98. <p>To make it easy to see we're going to put the camera directly above the origin
  99. looking down. The easiest way to do that is to use the <code class="notranslate" translate="no">lookAt</code> function. The <code class="notranslate" translate="no">lookAt</code>
  100. function will orient the camera from its position to "look at" the position
  101. we pass to <code class="notranslate" translate="no">lookAt</code>. Before we do that though we need to tell the camera
  102. which way the top of the camera is facing or rather which way is "up" for the
  103. camera. For most situations positive Y being up is good enough but since
  104. we are looking straight down we need to tell the camera that positive Z is up.</p>
  105. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  106. camera.position.set(0, 50, 0);
  107. camera.up.set(0, 0, 1);
  108. camera.lookAt(0, 0, 0);
  109. </pre>
  110. <p>In the render loop, adapted from previous examples, we're rotating all
  111. objects in our <code class="notranslate" translate="no">objects</code> array with this code.</p>
  112. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">objects.forEach((obj) =&gt; {
  113. obj.rotation.y = time;
  114. });
  115. </pre>
  116. <p>Since we added the <code class="notranslate" translate="no">sunMesh</code> to the <code class="notranslate" translate="no">objects</code> array it will rotate.</p>
  117. <p></p><div translate="no" class="threejs_example_container notranslate">
  118. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/scenegraph-sun.html"></iframe></div>
  119. <a class="threejs_center" href="/manual/examples/scenegraph-sun.html" target="_blank">click here to open in a separate window</a>
  120. </div>
  121. <p></p>
  122. <p>Now let's add in the earth.</p>
  123. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
  124. const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
  125. earthMesh.position.x = 10;
  126. scene.add(earthMesh);
  127. objects.push(earthMesh);
  128. </pre>
  129. <p>We make a material that is blue but we gave it a small amount of <em>emissive</em> blue
  130. so that it will show up against our black background.</p>
  131. <p>We use the same <code class="notranslate" translate="no">sphereGeometry</code> with our new blue <code class="notranslate" translate="no">earthMaterial</code> to make
  132. an <code class="notranslate" translate="no">earthMesh</code>. We position that 10 units to the left of the sun
  133. and add it to the scene. Since we added it to our <code class="notranslate" translate="no">objects</code> array it will
  134. rotate too.</p>
  135. <p></p><div translate="no" class="threejs_example_container notranslate">
  136. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/scenegraph-sun-earth.html"></iframe></div>
  137. <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth.html" target="_blank">click here to open in a separate window</a>
  138. </div>
  139. <p></p>
  140. <p>You can see both the sun and the earth are rotating but the earth is not
  141. going around the sun. Let's make the earth a child of the sun</p>
  142. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-scene.add(earthMesh);
  143. +sunMesh.add(earthMesh);
  144. </pre>
  145. <p>and...</p>
  146. <p></p><div translate="no" class="threejs_example_container notranslate">
  147. <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>
  148. <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-orbit.html" target="_blank">click here to open in a separate window</a>
  149. </div>
  150. <p></p>
  151. <p>What happened? Why is the earth the same size as the sun and why is it so far away?
  152. I actually had to move the camera from 50 units above to 150 units above to see the earth.</p>
  153. <p>We made the <code class="notranslate" translate="no">earthMesh</code> a child of the <code class="notranslate" translate="no">sunMesh</code>. The <code class="notranslate" translate="no">sunMesh</code> has
  154. its scale set to 5x with <code class="notranslate" translate="no">sunMesh.scale.set(5, 5, 5)</code>. That means the
  155. <code class="notranslate" translate="no">sunMesh</code>s local space is 5 times as big. Anything put in that space
  156. will be multiplied by 5. That means the earth is now 5x larger and
  157. its distance from the sun (<code class="notranslate" translate="no">earthMesh.position.x = 10</code>) is also
  158. 5x as well.</p>
  159. <p> Our scene graph currently looks like this</p>
  160. <p><img src="../resources/images/scenegraph-sun-earth.svg" align="center"></p>
  161. <p>To fix it let's add an empty scene graph node. We'll parent both the sun and the earth
  162. to that node.</p>
  163. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const solarSystem = new THREE.Object3D();
  164. +scene.add(solarSystem);
  165. +objects.push(solarSystem);
  166. const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xFFFF00});
  167. const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
  168. sunMesh.scale.set(5, 5, 5);
  169. -scene.add(sunMesh);
  170. +solarSystem.add(sunMesh);
  171. objects.push(sunMesh);
  172. const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
  173. const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
  174. earthMesh.position.x = 10;
  175. -sunMesh.add(earthMesh);
  176. +solarSystem.add(earthMesh);
  177. objects.push(earthMesh);
  178. </pre>
  179. <p>Here we made an <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>. Like a <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> it is also a node in the scene graph
  180. but unlike a <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> it has no material or geometry. It just represents a local space.</p>
  181. <p>Our new scene graph looks like this</p>
  182. <p><img src="../resources/images/scenegraph-sun-earth-fixed.svg" align="center"></p>
  183. <p>Both the <code class="notranslate" translate="no">sunMesh</code> and the <code class="notranslate" translate="no">earthMesh</code> are children of the <code class="notranslate" translate="no">solarSystem</code>. All 3
  184. are being rotated and now because the <code class="notranslate" translate="no">earthMesh</code> is not a child of the <code class="notranslate" translate="no">sunMesh</code>
  185. it is no longer scaled by 5x.</p>
  186. <p></p><div translate="no" class="threejs_example_container notranslate">
  187. <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>
  188. <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-orbit-fixed.html" target="_blank">click here to open in a separate window</a>
  189. </div>
  190. <p></p>
  191. <p>Much better. The earth is smaller than the sun and it's rotating around the sun
  192. and rotating itself.</p>
  193. <p>Continuing that same pattern let's add a moon.</p>
  194. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const earthOrbit = new THREE.Object3D();
  195. +earthOrbit.position.x = 10;
  196. +solarSystem.add(earthOrbit);
  197. +objects.push(earthOrbit);
  198. const earthMaterial = new THREE.MeshPhongMaterial({color: 0x2233FF, emissive: 0x112244});
  199. const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
  200. -earthMesh.position.x = 10; // note that this offset is already set in its parent's THREE.Object3D object "earthOrbit"
  201. -solarSystem.add(earthMesh);
  202. +earthOrbit.add(earthMesh);
  203. objects.push(earthMesh);
  204. +const moonOrbit = new THREE.Object3D();
  205. +moonOrbit.position.x = 2;
  206. +earthOrbit.add(moonOrbit);
  207. +const moonMaterial = new THREE.MeshPhongMaterial({color: 0x888888, emissive: 0x222222});
  208. +const moonMesh = new THREE.Mesh(sphereGeometry, moonMaterial);
  209. +moonMesh.scale.set(.5, .5, .5);
  210. +moonOrbit.add(moonMesh);
  211. +objects.push(moonMesh);
  212. </pre>
  213. <p>Again we added more invisible scene graph nodes. The first, an <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> called <code class="notranslate" translate="no">earthOrbit</code>
  214. and added both the <code class="notranslate" translate="no">earthMesh</code> and the <code class="notranslate" translate="no">moonOrbit</code> to it, also new. We then added the <code class="notranslate" translate="no">moonMesh</code>
  215. to the <code class="notranslate" translate="no">moonOrbit</code>. The new scene graph looks like this.</p>
  216. <p><img src="../resources/images/scenegraph-sun-earth-moon.svg" align="center"></p>
  217. <p>and here's that</p>
  218. <p></p><div translate="no" class="threejs_example_container notranslate">
  219. <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>
  220. <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-moon.html" target="_blank">click here to open in a separate window</a>
  221. </div>
  222. <p></p>
  223. <p>You can see the moon follows the spirograph pattern shown at the top
  224. of this article but we didn't have to manually compute it. We just
  225. setup our scene graph to do it for us.</p>
  226. <p>It is often useful to draw something to visualize the nodes in the scene graph.
  227. Three.js has some helpful ummmm, helpers to ummm, ... help with this.</p>
  228. <p>One is called an <a href="/docs/#api/en/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a>. It draws 3 lines representing the local
  229. <span style="color:red">X</span>,
  230. <span style="color:green">Y</span>, and
  231. <span style="color:blue">Z</span> axes. Let's add one to every node we
  232. created.</p>
  233. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">// add an AxesHelper to each node
  234. objects.forEach((node) =&gt; {
  235. const axes = new THREE.AxesHelper();
  236. axes.material.depthTest = false;
  237. axes.renderOrder = 1;
  238. node.add(axes);
  239. });
  240. </pre>
  241. <p>On our case we want the axes to appear even though they are inside the spheres.
  242. To do this we set their material's <code class="notranslate" translate="no">depthTest</code> to false which means they will
  243. not check to see if they are drawing behind something else. We also
  244. set their <code class="notranslate" translate="no">renderOrder</code> to 1 (the default is 0) so that they get drawn after
  245. all the spheres. Otherwise a sphere might draw over them and cover them up.</p>
  246. <p></p><div translate="no" class="threejs_example_container notranslate">
  247. <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>
  248. <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-moon-axes.html" target="_blank">click here to open in a separate window</a>
  249. </div>
  250. <p></p>
  251. <p>We can see the
  252. <span style="color:red">x (red)</span> and
  253. <span style="color:blue">z (blue)</span> axes. Since we are looking
  254. straight down and each of our objects is only rotating around its
  255. y axis we don't see much of the <span style="color:green">y (green)</span> axes.</p>
  256. <p>It might be hard to see some of them as there are 2 pairs of overlapping axes. Both the <code class="notranslate" translate="no">sunMesh</code>
  257. and the <code class="notranslate" translate="no">solarSystem</code> are at the same position. Similarly the <code class="notranslate" translate="no">earthMesh</code> and
  258. <code class="notranslate" translate="no">earthOrbit</code> are at the same position. Let's add some simple controls to allow us
  259. to turn them on/off for each node.
  260. While we're at it let's also add another helper called the <a href="/docs/#api/en/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a>. It
  261. makes a 2D grid on the X,Z plane. By default the grid is 10x10 units.</p>
  262. <p>We're also going to use <a href="https://github.com/georgealways/lil-gui">lil-gui</a> which is
  263. a UI library that is very popular with three.js projects. lil-gui takes an
  264. object and a property name on that object and based on the type of the property
  265. automatically makes a UI to manipulate that property.</p>
  266. <p>We want to make both a <a href="/docs/#api/en/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a> and an <a href="/docs/#api/en/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a> for each node. We need
  267. a label for each node so we'll get rid of the old loop and switch to calling
  268. some function to add the helpers for each node</p>
  269. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-// add an AxesHelper to each node
  270. -objects.forEach((node) =&gt; {
  271. - const axes = new THREE.AxesHelper();
  272. - axes.material.depthTest = false;
  273. - axes.renderOrder = 1;
  274. - node.add(axes);
  275. -});
  276. +function makeAxisGrid(node, label, units) {
  277. + const helper = new AxisGridHelper(node, units);
  278. + gui.add(helper, 'visible').name(label);
  279. +}
  280. +
  281. +makeAxisGrid(solarSystem, 'solarSystem', 25);
  282. +makeAxisGrid(sunMesh, 'sunMesh');
  283. +makeAxisGrid(earthOrbit, 'earthOrbit');
  284. +makeAxisGrid(earthMesh, 'earthMesh');
  285. +makeAxisGrid(moonOrbit, 'moonOrbit');
  286. +makeAxisGrid(moonMesh, 'moonMesh');
  287. </pre>
  288. <p><code class="notranslate" translate="no">makeAxisGrid</code> makes an <code class="notranslate" translate="no">AxisGridHelper</code> which is a class we'll create
  289. to make lil-gui happy. Like it says above lil-gui
  290. will automagically make a UI that manipulates the named property
  291. of some object. It will create a different UI depending on the type
  292. of property. We want it to create a checkbox so we need to specify
  293. a <code class="notranslate" translate="no">bool</code> property. But, we want both the axes and the grid
  294. to appear/disappear based on a single property so we'll make a class
  295. that has a getter and setter for a property. That way we can let lil-gui
  296. think it's manipulating a single property but internally we can set
  297. the visible property of both the <a href="/docs/#api/en/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a> and <a href="/docs/#api/en/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a> for a node.</p>
  298. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">// Turns both axes and grid visible on/off
  299. // lil-gui requires a property that returns a bool
  300. // to decide to make a checkbox so we make a setter
  301. // and getter for `visible` which we can tell lil-gui
  302. // to look at.
  303. class AxisGridHelper {
  304. constructor(node, units = 10) {
  305. const axes = new THREE.AxesHelper();
  306. axes.material.depthTest = false;
  307. axes.renderOrder = 2; // after the grid
  308. node.add(axes);
  309. const grid = new THREE.GridHelper(units, units);
  310. grid.material.depthTest = false;
  311. grid.renderOrder = 1;
  312. node.add(grid);
  313. this.grid = grid;
  314. this.axes = axes;
  315. this.visible = false;
  316. }
  317. get visible() {
  318. return this._visible;
  319. }
  320. set visible(v) {
  321. this._visible = v;
  322. this.grid.visible = v;
  323. this.axes.visible = v;
  324. }
  325. }
  326. </pre>
  327. <p>One thing to notice is we set the <code class="notranslate" translate="no">renderOrder</code> of the <a href="/docs/#api/en/helpers/AxesHelper"><code class="notranslate" translate="no">AxesHelper</code></a>
  328. to 2 and for the <a href="/docs/#api/en/helpers/GridHelper"><code class="notranslate" translate="no">GridHelper</code></a> to 1 so that the axes get drawn after the grid.
  329. Otherwise the grid might overwrite the axes.</p>
  330. <p></p><div translate="no" class="threejs_example_container notranslate">
  331. <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>
  332. <a class="threejs_center" href="/manual/examples/scenegraph-sun-earth-moon-axes-grids.html" target="_blank">click here to open in a separate window</a>
  333. </div>
  334. <p></p>
  335. <p>Turn on the <code class="notranslate" translate="no">solarSystem</code> and you'll see how the earth is exactly 10
  336. units out from the center just like we set above. You can see how the
  337. earth is in the <em>local space</em> of the <code class="notranslate" translate="no">solarSystem</code>. Similarly if you
  338. turn on the <code class="notranslate" translate="no">earthOrbit</code> you'll see how the moon is exactly 2 units
  339. from the center of the <em>local space</em> of the <code class="notranslate" translate="no">earthOrbit</code>.</p>
  340. <p>A few more examples of scene graphs. An automobile in a simple game world might have a scene graph like this</p>
  341. <p><img src="../resources/images/scenegraph-car.svg" align="center"></p>
  342. <p>If you move the car's body all the wheels will move with it. If you wanted the body
  343. to bounce separate from the wheels you might parent the body and the wheels to a "frame" node
  344. that represents the car's frame.</p>
  345. <p>Another example is a human in a game world.</p>
  346. <p><img src="../resources/images/scenegraph-human.svg" align="center"></p>
  347. <p>You can see the scene graph gets pretty complex for a human. In fact
  348. that scene graph above is simplified. For example you might extend it
  349. to cover every finger (at least another 28 nodes) and every toe
  350. (yet another 28 nodes) plus ones for the face and jaw, the eyes and maybe more.</p>
  351. <p>Let's make one semi-complex scene graph. We'll make a tank. The tank will have
  352. 6 wheels and a turret. The tank will follow a path. There will be a sphere that
  353. moves around and the tank will target the sphere.</p>
  354. <p>Here's the scene graph. The meshes are colored in green, the <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>s in blue,
  355. the lights in gold, and the cameras in purple. One camera has not been added
  356. to the scene graph.</p>
  357. <div class="threejs_center"><img src="../resources/images/scenegraph-tank.svg" style="width: 800px;"></div>
  358. <p>Look in the code to see the setup of all of these nodes.</p>
  359. <p>For the target, the thing the tank is aiming at, there is a <code class="notranslate" translate="no">targetOrbit</code>
  360. (<a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>) which just rotates similar to the <code class="notranslate" translate="no">earthOrbit</code> above. A
  361. <code class="notranslate" translate="no">targetElevation</code> (<a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a>) which is a child of the <code class="notranslate" translate="no">targetOrbit</code> provides an
  362. offset from the <code class="notranslate" translate="no">targetOrbit</code> and a base elevation. Childed to that is another
  363. <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> called <code class="notranslate" translate="no">targetBob</code> which just bobs up and down relative to the
  364. <code class="notranslate" translate="no">targetElevation</code>. Finally there's the <code class="notranslate" translate="no">targetMesh</code> which is just a cube we
  365. rotate and change its colors</p>
  366. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">// move target
  367. targetOrbit.rotation.y = time * .27;
  368. targetBob.position.y = Math.sin(time * 2) * 4;
  369. targetMesh.rotation.x = time * 7;
  370. targetMesh.rotation.y = time * 13;
  371. targetMaterial.emissive.setHSL(time * 10 % 1, 1, .25);
  372. targetMaterial.color.setHSL(time * 10 % 1, 1, .25);
  373. </pre>
  374. <p>For the tank there's an <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> called <code class="notranslate" translate="no">tank</code> which is used to move everything
  375. below it around. The code uses a <a href="/docs/#api/en/extras/curves/SplineCurve"><code class="notranslate" translate="no">SplineCurve</code></a> which it can ask for positions
  376. along that curve. 0.0 is the start of the curve. 1.0 is the end of the curve. It
  377. asks for the current position where it puts the tank. It then asks for a
  378. position slightly further down the curve and uses that to point the tank in that
  379. direction using <a href="/docs/#api/en/core/Object3D.lookAt"><code class="notranslate" translate="no">Object3D.lookAt</code></a>.</p>
  380. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const tankPosition = new THREE.Vector2();
  381. const tankTarget = new THREE.Vector2();
  382. ...
  383. // move tank
  384. const tankTime = time * .05;
  385. curve.getPointAt(tankTime % 1, tankPosition);
  386. curve.getPointAt((tankTime + 0.01) % 1, tankTarget);
  387. tank.position.set(tankPosition.x, 0, tankPosition.y);
  388. tank.lookAt(tankTarget.x, 0, tankTarget.y);
  389. </pre>
  390. <p>The turret on top of the tank is moved automatically by being a child
  391. of the tank. To point it at the target we just ask for the target's world position
  392. and then again use <a href="/docs/#api/en/core/Object3D.lookAt"><code class="notranslate" translate="no">Object3D.lookAt</code></a></p>
  393. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const targetPosition = new THREE.Vector3();
  394. ...
  395. // face turret at target
  396. targetMesh.getWorldPosition(targetPosition);
  397. turretPivot.lookAt(targetPosition);
  398. </pre>
  399. <p>There's a <code class="notranslate" translate="no">turretCamera</code> which is a child of the <code class="notranslate" translate="no">turretMesh</code> so
  400. it will move up and down and rotate with the turret. We make that
  401. aim at the target.</p>
  402. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">// make the turretCamera look at target
  403. turretCamera.lookAt(targetPosition);
  404. </pre>
  405. <p>There is also a <code class="notranslate" translate="no">targetCameraPivot</code> which is a child of <code class="notranslate" translate="no">targetBob</code> so it floats
  406. around with the target. We aim that back at the tank. Its purpose is to allow the
  407. <code class="notranslate" translate="no">targetCamera</code> to be offset from the target itself. If we instead made the camera
  408. a child of <code class="notranslate" translate="no">targetBob</code> and just aimed the camera itself it would be inside the
  409. target.</p>
  410. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">// make the targetCameraPivot look at the tank
  411. tank.getWorldPosition(targetPosition);
  412. targetCameraPivot.lookAt(targetPosition);
  413. </pre>
  414. <p>Finally we rotate all the wheels</p>
  415. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">wheelMeshes.forEach((obj) =&gt; {
  416. obj.rotation.x = time * 3;
  417. });
  418. </pre>
  419. <p>For the cameras we setup an array of all 4 cameras at init time with descriptions.</p>
  420. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cameras = [
  421. { cam: camera, desc: 'detached camera', },
  422. { cam: turretCamera, desc: 'on turret looking at target', },
  423. { cam: targetCamera, desc: 'near target looking at tank', },
  424. { cam: tankCamera, desc: 'above back of tank', },
  425. ];
  426. const infoElem = document.querySelector('#info');
  427. </pre>
  428. <p>and cycle through our cameras at render time.</p>
  429. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const camera = cameras[time * .25 % cameras.length | 0];
  430. infoElem.textContent = camera.desc;
  431. </pre>
  432. <p></p><div translate="no" class="threejs_example_container notranslate">
  433. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/scenegraph-tank.html"></iframe></div>
  434. <a class="threejs_center" href="/manual/examples/scenegraph-tank.html" target="_blank">click here to open in a separate window</a>
  435. </div>
  436. <p></p>
  437. <p>I hope this gives some idea of how scene graphs work and how you might use them.
  438. Making <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">Object3D</code></a> nodes and parenting things to them is an important step to using
  439. a 3D engine like three.js well. Often it might seem like some complex math is necessary
  440. to make something move and rotate the way you want. For example without a scene graph
  441. computing the motion of the moon or where to put the wheels of the car relative to its
  442. body would be very complicated but using a scene graph it becomes much easier.</p>
  443. <p><a href="materials.html">Next up we'll go over materials</a>.</p>
  444. </div>
  445. </div>
  446. </div>
  447. <script src="/manual/resources/prettify.js"></script>
  448. <script src="/manual/resources/lesson.js"></script>
  449. </body></html>