custom-geometry.html 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. <!DOCTYPE html><html lang="en"><head>
  2. <meta charset="utf-8">
  3. <title>Custom Geometry</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 – Custom Geometry">
  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>Custom Geometry</h1>
  28. </div>
  29. <div class="lesson">
  30. <div class="lesson-main">
  31. <div class="warning">
  32. <strong>NOTE!</strong> This article is deprecated. Three.js r125
  33. removed support for <code class="notranslate" translate="no">Geometry</code>. Please refer to
  34. the article on <a href="custom-buffergeometry.html">custom BufferGeometry</a>.
  35. </div>
  36. <p>A <a href="primitives.html">previous article</a> gave a tour of
  37. the various built in primitives included in THREE.js. In this
  38. article we'll cover making our own geometry.</p>
  39. <p>Just to be clear, if you are serious about making 3D content,
  40. the most common way is to use a 3D modeling package like
  41. <a href="https://blender.org">Blender</a>,
  42. <a href="https://www.autodesk.com/products/maya/overview">Maya</a>,
  43. <a href="https://www.autodesk.com/products/3ds-max/overview">3D Studio Max</a>,
  44. <a href="https://www.maxon.net/en-us/">Cinema4D</a>, etc...
  45. You'd build a model and then export to <a href="load-gltf.html">gLTF</a>
  46. or <a href="load-obj.html">.obj</a> and load them up.
  47. Whichever one you choose, expect to spend 2 or 3 weeks going through
  48. their respective tutorials as all of them have a learning curve
  49. to be useful.</p>
  50. <p>Still, there are times when we might want to generate our own
  51. 3D geometry in code instead of using a modeling package.</p>
  52. <p>First let's just make a cube. Even though three.js already
  53. provides us with <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> and <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> a
  54. cube is easy to understand so let's start there.</p>
  55. <p>There are 2 ways to make custom geometry in THREE.js. One
  56. is with the <code class="notranslate" translate="no">Geometry</code> class, the other is <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>.
  57. Each has their advantages. <code class="notranslate" translate="no">Geometry</code> is arguably easier to
  58. use but slower and uses more memory. For few 1000s triangles
  59. it's a great choice but for 10s of thousands of triangles
  60. it might be better to use <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>.</p>
  61. <p><a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a> is arguably harder to use but uses less
  62. memory and is faster. If quick rule of thumb might be
  63. if you're going to generate more than 10000 triangles
  64. consider using <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>.</p>
  65. <p>Note when I say <code class="notranslate" translate="no">Geometry</code> is slower I mean it is slower to
  66. start and slower to modify but it is not slower to draw so
  67. if you're not planning on modifying your geometry then
  68. as long as it's not too large there will only be slightly more
  69. delay for your program to start using <code class="notranslate" translate="no">Geometry</code> vs using
  70. <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>. We'll go over both eventually. For now
  71. though let's use geometry as it's easier to understand IMO.</p>
  72. <p>First let's make a cube with <code class="notranslate" translate="no">Geometry</code>. We'll start
  73. with an example from <a href="responsive.html">the article on responsiveness</a>.</p>
  74. <p>Let's remove the part that uses <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> and replace it with
  75. a <code class="notranslate" translate="no">Geometry</code>.</p>
  76. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const boxWidth = 1;
  77. -const boxHeight = 1;
  78. -const boxDepth = 1;
  79. -const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
  80. +const geometry = new THREE.Geometry();
  81. </pre>
  82. <p>Now let's add the 8 corners of a cube. Here are the 8 corners.</p>
  83. <div class="threejs_center"><img src="../resources/cube-vertex-positions.svg" style="width: 500px"></div>
  84. <p>Centered around the origin we can add the vertex positions like this</p>
  85. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const geometry = new THREE.Geometry();
  86. +geometry.vertices.push(
  87. + new THREE.Vector3(-1, -1, 1), // 0
  88. + new THREE.Vector3( 1, -1, 1), // 1
  89. + new THREE.Vector3(-1, 1, 1), // 2
  90. + new THREE.Vector3( 1, 1, 1), // 3
  91. + new THREE.Vector3(-1, -1, -1), // 4
  92. + new THREE.Vector3( 1, -1, -1), // 5
  93. + new THREE.Vector3(-1, 1, -1), // 6
  94. + new THREE.Vector3( 1, 1, -1), // 7
  95. +);
  96. </pre>
  97. <p>We then need to make triangles, 2 for each face of the cube</p>
  98. <div class="threejs_center"><img src="../resources/cube-triangles.svg" style="width: 500px"></div>
  99. <p>We do that by creating <a href="/docs/#api/en/core/Face3"><code class="notranslate" translate="no">Face3</code></a> objects and specifying the indices
  100. of the 3 vertices that make up that face.</p>
  101. <p>The order we specify the vertices is important. To be pointing toward the
  102. outside of the cube they must be specified in a counter clockwise direction
  103. when that triangle is facing the camera.</p>
  104. <div class="threejs_center"><img src="../resources/cube-vertex-winding-order.svg" style="width: 500px"></div>
  105. <p>Following that pattern we can specify the 12 triangles that make
  106. the cube like this</p>
  107. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faces.push(
  108. // front
  109. new THREE.Face3(0, 3, 2),
  110. new THREE.Face3(0, 1, 3),
  111. // right
  112. new THREE.Face3(1, 7, 3),
  113. new THREE.Face3(1, 5, 7),
  114. // back
  115. new THREE.Face3(5, 6, 7),
  116. new THREE.Face3(5, 4, 6),
  117. // left
  118. new THREE.Face3(4, 2, 6),
  119. new THREE.Face3(4, 0, 2),
  120. // top
  121. new THREE.Face3(2, 7, 6),
  122. new THREE.Face3(2, 3, 7),
  123. // bottom
  124. new THREE.Face3(4, 1, 0),
  125. new THREE.Face3(4, 5, 1),
  126. );
  127. </pre>
  128. <p>A few other minor changes to the original code and it should
  129. work.</p>
  130. <p>These cubes are twice as large as the <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> we were
  131. using before so let's move the camera back a little</p>
  132. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 75;
  133. const aspect = 2; // the canvas default
  134. const near = 0.1;
  135. -const far = 5;
  136. +const far = 100;
  137. const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  138. -camera.position.z = 2;
  139. +camera.position.z = 5;
  140. </pre>
  141. <p>and let's separate them a little more and I changed their colors just because</p>
  142. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cubes = [
  143. - makeInstance(geometry, 0x44aa88, 0),
  144. - makeInstance(geometry, 0x8844aa, -2),
  145. - makeInstance(geometry, 0xaa8844, 2),
  146. + makeInstance(geometry, 0x44FF44, 0),
  147. + makeInstance(geometry, 0x4444FF, -4),
  148. + makeInstance(geometry, 0xFF4444, 4),
  149. ];
  150. </pre>
  151. <p>One last thing is we haven't added normals yet so we
  152. can't do any lighting. Let's change the material
  153. to something that doesn't need lights.</p>
  154. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeInstance(geometry, color, x) {
  155. - const material = new THREE.MeshPhongMaterial({color});
  156. + const material = new THREE.MeshBasicMaterial({color});
  157. const cube = new THREE.Mesh(geometry, material);
  158. scene.add(cube);
  159. ...
  160. </pre>
  161. <p>and we get cubes we made ourselves.</p>
  162. <p></p><div translate="no" class="threejs_example_container notranslate">
  163. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube.html"></iframe></div>
  164. <a class="threejs_center" href="/manual/examples/custom-geometry-cube.html" target="_blank">click here to open in a separate window</a>
  165. </div>
  166. <p></p>
  167. <p>We can specify a color per face by setting the <code class="notranslate" translate="no">color</code> property of
  168. each face.</p>
  169. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faces[ 0].color = geometry.faces[ 1].color = new THREE.Color('red');
  170. geometry.faces[ 2].color = geometry.faces[ 3].color = new THREE.Color('yellow');
  171. geometry.faces[ 4].color = geometry.faces[ 5].color = new THREE.Color('green');
  172. geometry.faces[ 6].color = geometry.faces[ 7].color = new THREE.Color('cyan');
  173. geometry.faces[ 8].color = geometry.faces[ 9].color = new THREE.Color('blue');
  174. geometry.faces[10].color = geometry.faces[11].color = new THREE.Color('magenta');
  175. </pre>
  176. <p>note we need to tell the material we want to use vertex colors</p>
  177. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const material = new THREE.MeshBasicMaterial({color});
  178. +const material = new THREE.MeshBasicMaterial({vertexColors: true});
  179. </pre>
  180. <p></p><div translate="no" class="threejs_example_container notranslate">
  181. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-face-colors.html"></iframe></div>
  182. <a class="threejs_center" href="/manual/examples/custom-geometry-cube-face-colors.html" target="_blank">click here to open in a separate window</a>
  183. </div>
  184. <p></p>
  185. <p>We can instead set the color of each individual vertex by setting the <code class="notranslate" translate="no">vertexColors</code>
  186. property of a <code class="notranslate" translate="no">Face</code> to an array of the 3 colors for the 3 vertices.</p>
  187. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faces.forEach((face, ndx) =&gt; {
  188. face.vertexColors = [
  189. (new THREE.Color()).setHSL(ndx / 12 , 1, 0.5),
  190. (new THREE.Color()).setHSL(ndx / 12 + 0.1, 1, 0.5),
  191. (new THREE.Color()).setHSL(ndx / 12 + 0.2, 1, 0.5),
  192. ];
  193. });
  194. </pre>
  195. <p></p><div translate="no" class="threejs_example_container notranslate">
  196. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-vertex-colors.html"></iframe></div>
  197. <a class="threejs_center" href="/manual/examples/custom-geometry-cube-vertex-colors.html" target="_blank">click here to open in a separate window</a>
  198. </div>
  199. <p></p>
  200. <p>To use lighting we need normals. Normals are vectors that specify direction.
  201. Just like the colors we can specify a normal for the face by setting the <code class="notranslate" translate="no">normal</code>
  202. property on each face with</p>
  203. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">face.normal = new THREE.Vector3(...)
  204. </pre>
  205. <p>or we can specify a normal for each vertex by setting the <code class="notranslate" translate="no">vertexNormals</code>
  206. property with something like</p>
  207. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">face.vertexNormals = [
  208. new THREE.Vector3(...),
  209. new THREE.Vector3(...),
  210. new THREE.Vector3(...),
  211. ]
  212. </pre>
  213. <p>but often it's much easier to just ask THREE.js to compute normals
  214. for us based on the positions we specified.</p>
  215. <p>For face normals we'd call <code class="notranslate" translate="no">Geometry.computeFaceNormals</code> as in</p>
  216. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.computeFaceNormals();
  217. </pre>
  218. <p>Removing the vertex color stuff and changing the material back to <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a></p>
  219. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const material = new THREE.MeshBasicMaterial({vertexColors: true});
  220. +const material = new THREE.MeshPhongMaterial({color});
  221. </pre>
  222. <p>and now our cubes can be lit.</p>
  223. <p></p><div translate="no" class="threejs_example_container notranslate">
  224. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-face-normals.html"></iframe></div>
  225. <a class="threejs_center" href="/manual/examples/custom-geometry-cube-face-normals.html" target="_blank">click here to open in a separate window</a>
  226. </div>
  227. <p></p>
  228. <p>Using face normals will always give us a faceted look. We can use
  229. vertex normals for a smoother look by calling <code class="notranslate" translate="no">Geometry.computeVertexNormals</code></p>
  230. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-geometry.computeFaceNormals();
  231. +geometry.computeVertexNormals();
  232. </pre>
  233. <p>Unfortunately a cube is not a good candidate for vertex normals since it
  234. means each vertex gets its normal from the
  235. normals of all the faces it shares.</p>
  236. <p></p><div translate="no" class="threejs_example_container notranslate">
  237. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-vertex-normals.html"></iframe></div>
  238. <a class="threejs_center" href="/manual/examples/custom-geometry-cube-vertex-normals.html" target="_blank">click here to open in a separate window</a>
  239. </div>
  240. <p></p>
  241. <p>Adding texture coordinates, sometimes called UVs, is done via an array of
  242. layers of parallel arrays to the <code class="notranslate" translate="no">faces</code> array which is set via <code class="notranslate" translate="no">Geometry.faceVertexUvs</code>.
  243. For our cube we could do something like</p>
  244. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faceVertexUvs[0].push(
  245. // front
  246. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
  247. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
  248. // right
  249. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
  250. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
  251. // back
  252. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
  253. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
  254. // left
  255. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
  256. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
  257. // top
  258. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
  259. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
  260. // bottom
  261. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
  262. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
  263. );
  264. </pre>
  265. <p>It's important to notice <code class="notranslate" translate="no">faceVertexUvs</code> is an array of layers. Each layer
  266. is another set of UV coordinates. By default there is one layer of UV coordinates,
  267. layer 0, so we just add our UVs to that layer.</p>
  268. <p>Let's <a href="textures.html">add a texture</a> to our material and switch back to compute face normals</p>
  269. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-geometry.computeVertexNormals();
  270. +geometry.computeFaceNormals();
  271. +const loader = new THREE.TextureLoader();
  272. +const texture = loader.load('resources/images/star.png');
  273. function makeInstance(geometry, color, x) {
  274. - const material = new THREE.MeshPhongMaterial({color});
  275. + const material = new THREE.MeshPhongMaterial({color, map: texture});
  276. const cube = new THREE.Mesh(geometry, material);
  277. scene.add(cube);
  278. ...
  279. </pre>
  280. <p></p><div translate="no" class="threejs_example_container notranslate">
  281. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube-texcoords.html"></iframe></div>
  282. <a class="threejs_center" href="/manual/examples/custom-geometry-cube-texcoords.html" target="_blank">click here to open in a separate window</a>
  283. </div>
  284. <p></p>
  285. <p>Putting that all together, let's make a simple heightmap based
  286. terrain mesh.</p>
  287. <p>A heightmap based terrain is where you have a 2D array of heights
  288. that you apply them to a grid. An easy way to get a 2D array of heights
  289. is to draw them in an image editing program. Here's an image I drew.
  290. It's 96x64 pixels</p>
  291. <div class="threejs_center"><img src="../examples/resources/images/heightmap-96x64.png" style="width: 512px; image-rendering: pixelated;"></div>
  292. <p>We'll load that and then generate a heightmap mesh from it.
  293. We can use the <a href="/docs/#api/en/loaders/ImageLoader"><code class="notranslate" translate="no">ImageLoader</code></a> to load the image.</p>
  294. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const imgLoader = new THREE.ImageLoader();
  295. imgLoader.load('resources/images/heightmap-96x64.png', createHeightmap);
  296. function createHeightmap(image) {
  297. // extract the data from the image by drawing it to a canvas
  298. // and calling getImageData
  299. const ctx = document.createElement('canvas').getContext('2d');
  300. const {width, height} = image;
  301. ctx.canvas.width = width;
  302. ctx.canvas.height = height;
  303. ctx.drawImage(image, 0, 0);
  304. const {data} = ctx.getImageData(0, 0, width, height);
  305. const geometry = new THREE.Geometry();
  306. </pre>
  307. <p>We extracted the data from the image, now we'll make a grid of cells.
  308. The cells are the squares formed by the center points of each pixel
  309. from the image</p>
  310. <div class="threejs_center"><img src="../resources/heightmap-points.svg" style="width: 500px"></div>
  311. <p>For each cell we'll generate 5 vertices. One for each corner of the cell
  312. and one at the center point of the cell with the average height of the 4
  313. corner heights.</p>
  314. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cellsAcross = width - 1;
  315. const cellsDeep = height - 1;
  316. for (let z = 0; z &lt; cellsDeep; ++z) {
  317. for (let x = 0; x &lt; cellsAcross; ++x) {
  318. // compute row offsets into the height data
  319. // we multiply by 4 because the data is R,G,B,A but we
  320. // only care about R
  321. const base0 = (z * width + x) * 4;
  322. const base1 = base0 + (width * 4);
  323. // look up the height for the for points
  324. // around this cell
  325. const h00 = data[base0] / 32;
  326. const h01 = data[base0 + 4] / 32;
  327. const h10 = data[base1] / 32;
  328. const h11 = data[base1 + 4] / 32;
  329. // compute the average height
  330. const hm = (h00 + h01 + h10 + h11) / 4;
  331. // the corner positions
  332. const x0 = x;
  333. const x1 = x + 1;
  334. const z0 = z;
  335. const z1 = z + 1;
  336. // remember the first index of these 5 vertices
  337. const ndx = geometry.vertices.length;
  338. // add the 4 corners for this cell and the midpoint
  339. geometry.vertices.push(
  340. new THREE.Vector3(x0, h00, z0),
  341. new THREE.Vector3(x1, h01, z0),
  342. new THREE.Vector3(x0, h10, z1),
  343. new THREE.Vector3(x1, h11, z1),
  344. new THREE.Vector3((x0 + x1) / 2, hm, (z0 + z1) / 2),
  345. );
  346. </pre>
  347. <p>We'll then make 4 triangles from those 5 vertices</p>
  348. <div class="threejs_center"><img src="../resources/heightmap-triangles.svg" style="width: 500px"></div>
  349. <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> // create 4 triangles
  350. geometry.faces.push(
  351. new THREE.Face3(ndx + 0, ndx + 4, ndx + 1),
  352. new THREE.Face3(ndx + 1, ndx + 4, ndx + 3),
  353. new THREE.Face3(ndx + 3, ndx + 4, ndx + 2),
  354. new THREE.Face3(ndx + 2, ndx + 4, ndx + 0),
  355. );
  356. // add the texture coordinates for each vertex of each face
  357. const u0 = x / cellsAcross;
  358. const v0 = z / cellsDeep;
  359. const u1 = (x + 1) / cellsAcross;
  360. const v1 = (z + 1) / cellsDeep;
  361. const um = (u0 + u1) / 2;
  362. const vm = (v0 + v1) / 2;
  363. geometry.faceVertexUvs[0].push(
  364. [ new THREE.Vector2(u0, v0), new THREE.Vector2(um, vm), new THREE.Vector2(u1, v0) ],
  365. [ new THREE.Vector2(u1, v0), new THREE.Vector2(um, vm), new THREE.Vector2(u1, v1) ],
  366. [ new THREE.Vector2(u1, v1), new THREE.Vector2(um, vm), new THREE.Vector2(u0, v1) ],
  367. [ new THREE.Vector2(u0, v1), new THREE.Vector2(um, vm), new THREE.Vector2(u0, v0) ],
  368. );
  369. }
  370. }
  371. </pre>
  372. <p>and finish it up</p>
  373. <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> geometry.computeFaceNormals();
  374. // center the geometry
  375. geometry.translate(width / -2, 0, height / -2);
  376. const loader = new THREE.TextureLoader();
  377. const texture = loader.load('resources/images/star.png');
  378. const material = new THREE.MeshPhongMaterial({color: 'green', map: texture});
  379. const cube = new THREE.Mesh(geometry, material);
  380. scene.add(cube);
  381. }
  382. </pre>
  383. <p>A few minor changes to make it easier to view.</p>
  384. <ul>
  385. <li>include the <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a></li>
  386. </ul>
  387. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from '/build/three.module.js';
  388. +import {OrbitControls} from '/examples/jsm/controls/OrbitControls.js';
  389. </pre>
  390. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 75;
  391. const aspect = 2; // the canvas default
  392. const near = 0.1;
  393. -const far = 100;
  394. +const far = 200;
  395. const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  396. -camera.position.z = 5;
  397. +camera.position.set(20, 20, 20);
  398. +const controls = new OrbitControls(camera, canvas);
  399. +controls.target.set(0, 0, 0);
  400. +controls.update();
  401. </pre>
  402. <p>add 2 lights</p>
  403. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-{
  404. +function addLight(...pos) {
  405. const color = 0xFFFFFF;
  406. const intensity = 1;
  407. const light = new THREE.DirectionalLight(color, intensity);
  408. - light.position.set(-1, 2, 4\);
  409. + light.position.set(...pos);
  410. scene.add(light);
  411. }
  412. +addLight(-1, 2, 4);
  413. +addLight(1, 2, -2);
  414. </pre>
  415. <p>and we deleted the code related to spinning the cubes.</p>
  416. <p></p><div translate="no" class="threejs_example_container notranslate">
  417. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-heightmap.html"></iframe></div>
  418. <a class="threejs_center" href="/manual/examples/custom-geometry-heightmap.html" target="_blank">click here to open in a separate window</a>
  419. </div>
  420. <p></p>
  421. <p>I hope that was a useful instruction to making your own
  422. geometry using <code class="notranslate" translate="no">Geometry</code>.</p>
  423. <p>In <a href="custom-buffergeometry.html">another article</a> we'll go over <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>.</p>
  424. </div>
  425. </div>
  426. </div>
  427. <script src="/manual/resources/prettify.js"></script>
  428. <script src="/manual/resources/lesson.js"></script>
  429. </body></html>