custom-geometry.html 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. <!DOCTYPE html><html lang="ru"><head>
  2. <meta charset="utf-8">
  3. <title>Пользовательская 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 – Пользовательская 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. </head>
  14. <body>
  15. <div class="container">
  16. <div class="lesson-title">
  17. <h1>Пользовательская Geometry</h1>
  18. </div>
  19. <div class="lesson">
  20. <div class="lesson-main">
  21. <div class="warning">
  22. <strong>NOTE!</strong> This article is deprecated. Three.js r125
  23. removed support for <code class="notranslate" translate="no">Geometry</code>. Please refer to
  24. the article on <a href="custom-buffergeometry.html">custom BufferGeometry</a>.
  25. </div>
  26. <p>В <a href="primitives.html">предыдущей статье</a> рассказывалось о различных встроенных примитивах, включенных в THREE.js. В этой статье мы рассмотрим создание нашей собственной геометрии. </p>
  27. <p>Просто для ясности, если вы серьезно относитесь к созданию 3D-контента, наиболее распространенный способ - использовать пакет 3D-моделирования, такой как <a href="https://blender.org">Blender</a>,
  28. <a href="https://www.autodesk.com/products/maya/overview">Maya</a>,
  29. <a href="https://www.autodesk.com/products/3ds-max/overview">3D Studio Max</a>,
  30. <a href="https://www.maxon.net/en-us/">Cinema4D</a> и т. д.
  31. Вы создадите модель, а затем экспортируете в <a href="load-gltf.html">gLTF</a> или <a href="load-obj.html">.obj</a> и загрузите их.
  32. Какой бы вариант вы ни выбрали, рассчитывайте потратить 2 или 3 недели на изучение соответствующих учебных пособий, поскольку все они имеют полезную кривую обучения. </p>
  33. <p>Тем не менее, бывают случаи, когда мы можем захотеть сгенерировать нашу собственную трехмерную геометрию в коде вместо использования пакета моделирования. </p>
  34. <p>Сначала давайте просто сделаем куб. Хотя Three.js уже предоставляет нам <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> и <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a>, куб легко понять, поэтому давайте начнем с него. </p>
  35. <p>Есть два способа сделать пользовательскую геометрию в THREE.js. Один с классом <code class="notranslate" translate="no">Geometry</code>, другой - <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>. У каждого есть свои преимущества. <code class="notranslate" translate="no">Geometry</code>, возможно, проще в использовании, но медленнее и использует больше памяти. Для нескольких тысяч треугольников это отличный выбор, но для десятков тысяч треугольников может быть лучше использовать <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>. </p>
  36. <p><a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>, возможно, сложнее в использовании, но использует меньше памяти и работает быстрее. Если вы хотите сгенерировать более 10000 треугольников, подумайте об использовании <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>. </p>
  37. <p>Заметьте, когда я говорю, что <code class="notranslate" translate="no">Geometry</code> медленнее, я имею в виду, что она медленнее запускается и медленнее изменяется, но отрисовывается она не медленнее, поэтому, если вы не планируете изменять свою геометрию, тогда, пока она не слишком велика, будет только немного больше. задержка для вашей программы, чтобы начать использовать <code class="notranslate" translate="no">Geometry</code> против <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>. Мы изучим оба способа. Пока что давайте использовать геометрию, так как легче понять IMO. </p>
  38. <p>Сначала давайте сделаем куб с <code class="notranslate" translate="no">Geometry</code>. Начнем с примера из <a href="responsive.html">статьи об отзывчивости</a>. </p>
  39. <p>Давайте удалим код, который использует <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a>, и заменим ее на <code class="notranslate" translate="no">Geometry</code>. </p>
  40. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const boxWidth = 1;
  41. -const boxHeight = 1;
  42. -const boxDepth = 1;
  43. -const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
  44. +const geometry = new THREE.Geometry();
  45. </pre>
  46. <p>Теперь давайте добавим 8 углов куба. Вот 8 углов. </p>
  47. <div class="threejs_center"><img src="../resources/cube-vertex-positions.svg" style="width: 500px"></div>
  48. <p>Сосредоточив вокруг начала координат, мы можем добавить позиции вершин, как это</p>
  49. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const geometry = new THREE.Geometry();
  50. +geometry.vertices.push(
  51. + new THREE.Vector3(-1, -1, 1), // 0
  52. + new THREE.Vector3( 1, -1, 1), // 1
  53. + new THREE.Vector3(-1, 1, 1), // 2
  54. + new THREE.Vector3( 1, 1, 1), // 3
  55. + new THREE.Vector3(-1, -1, -1), // 4
  56. + new THREE.Vector3( 1, -1, -1), // 5
  57. + new THREE.Vector3(-1, 1, -1), // 6
  58. + new THREE.Vector3( 1, 1, -1), // 7
  59. +);
  60. </pre>
  61. <p>Затем нам нужно сделать треугольники, по 2 на каждую грань куба</p>
  62. <div class="threejs_center"><img src="../resources/cube-triangles.svg" style="width: 500px"></div>
  63. <p>Мы делаем это, создавая объекты <a href="/docs/#api/en/core/Face3"><code class="notranslate" translate="no">Face3</code></a> и определяя индексы 3 вершин, которые составляют эту грань.</p>
  64. <p>Порядок, в котором мы указываем вершины, важен. Чтобы указывать на внешнюю сторону куба, они должны быть указаны в направлении против часовой стрелки, когда этот треугольник направлен на камеру. </p>
  65. <div class="threejs_center"><img src="../resources/cube-vertex-winding-order.svg" style="width: 500px"></div>
  66. <p>Следуя этой схеме, мы можем указать 12 треугольников, которые делают куб таким</p>
  67. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faces.push(
  68. // front
  69. new THREE.Face3(0, 3, 2),
  70. new THREE.Face3(0, 1, 3),
  71. // right
  72. new THREE.Face3(1, 7, 3),
  73. new THREE.Face3(1, 5, 7),
  74. // back
  75. new THREE.Face3(5, 6, 7),
  76. new THREE.Face3(5, 4, 6),
  77. // left
  78. new THREE.Face3(4, 2, 6),
  79. new THREE.Face3(4, 0, 2),
  80. // top
  81. new THREE.Face3(2, 7, 6),
  82. new THREE.Face3(2, 3, 7),
  83. // bottom
  84. new THREE.Face3(4, 1, 0),
  85. new THREE.Face3(4, 5, 1),
  86. );
  87. </pre>
  88. <p>Несколько других мелких изменений в оригинальном коде, и он должен работать. </p>
  89. <p>Эти кубики в два раза больше, чем <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a>, которую мы использовали раньше, поэтому давайте немного переместим камеру назад </p>
  90. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 75;
  91. const aspect = 2; // the canvas default
  92. const near = 0.1;
  93. -const far = 5;
  94. +const far = 100;
  95. const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  96. -camera.position.z = 2;
  97. +camera.position.z = 5;
  98. </pre>
  99. <p>и давайте отделим их немного больше, и я изменил их цвета только потому, что</p>
  100. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cubes = [
  101. - makeInstance(geometry, 0x44aa88, 0),
  102. - makeInstance(geometry, 0x8844aa, -2),
  103. - makeInstance(geometry, 0xaa8844, 2),
  104. + makeInstance(geometry, 0x44FF44, 0),
  105. + makeInstance(geometry, 0x4444FF, -4),
  106. + makeInstance(geometry, 0xFF4444, 4),
  107. ];
  108. </pre>
  109. <p>И последнее, что мы еще не добавили это нормалей, поэтому мы не можем включить освещение. Давайте изменим материал на то, что не нуждается в освещении. </p>
  110. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function makeInstance(geometry, color, x) {
  111. - const material = new THREE.MeshPhongMaterial({color});
  112. + const material = new THREE.MeshBasicMaterial({color});
  113. const cube = new THREE.Mesh(geometry, material);
  114. scene.add(cube);
  115. ...
  116. </pre>
  117. <p>и мы получаем кубики, которые мы сделали сами.
  118. </p><div translate="no" class="threejs_example_container notranslate">
  119. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-cube.html"></iframe></div>
  120. <a class="threejs_center" href="/manual/examples/custom-geometry-cube.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  121. </div>
  122. <p></p>
  123. <p>Мы можем указать цвет для каждой грани, установив свойство <code class="notranslate" translate="no">color</code> каждой стороны.</p>
  124. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faces[ 0].color = geometry.faces[ 1].color = new THREE.Color('red');
  125. geometry.faces[ 2].color = geometry.faces[ 3].color = new THREE.Color('yellow');
  126. geometry.faces[ 4].color = geometry.faces[ 5].color = new THREE.Color('green');
  127. geometry.faces[ 6].color = geometry.faces[ 7].color = new THREE.Color('cyan');
  128. geometry.faces[ 8].color = geometry.faces[ 9].color = new THREE.Color('blue');
  129. geometry.faces[10].color = geometry.faces[11].color = new THREE.Color('magenta');
  130. </pre>
  131. <p>обратите внимание, что мы должны указать материал, который мы хотим использовать <code class="notranslate" translate="no">vertexColors</code></p>
  132. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const material = new THREE.MeshBasicMaterial({color});
  133. +const material = new THREE.MeshBasicMaterial({vertexColors: true});
  134. </pre>
  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/custom-geometry-cube-face-colors.html"></iframe></div>
  137. <a class="threejs_center" href="/manual/examples/custom-geometry-cube-face-colors.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  138. </div>
  139. <p></p>
  140. <p>Вместо этого мы можем установить цвет каждой отдельной вершины, установив для свойства <code class="notranslate" translate="no">vertexColors</code> массив из 3 цветов для 3 вершин. </p>
  141. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faces.forEach((face, ndx) =&gt; {
  142. face.vertexColors = [
  143. (new THREE.Color()).setHSL(ndx / 12 , 1, 0.5),
  144. (new THREE.Color()).setHSL(ndx / 12 + 0.1, 1, 0.5),
  145. (new THREE.Color()).setHSL(ndx / 12 + 0.2, 1, 0.5),
  146. ];
  147. });
  148. </pre>
  149. <p></p><div translate="no" class="threejs_example_container notranslate">
  150. <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>
  151. <a class="threejs_center" href="/manual/examples/custom-geometry-cube-vertex-colors.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  152. </div>
  153. <p></p>
  154. <p>Чтобы использовать освещение, нам нужны нормали. Нормали - это векторы, которые определяют направление. Так же, как цвета, мы можем указать нормаль для грани, установив свойство <code class="notranslate" translate="no">normal</code> для каждой стороны с помощью</p>
  155. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">face.normal = new THREE.Vector3(...)
  156. </pre>
  157. <p>или мы можем указать нормаль для каждой вершины, установив для свойства <code class="notranslate" translate="no">vertexNormals</code> что-то вроде </p>
  158. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">face.vertexNormals = [
  159. new THREE.Vector3(...),
  160. new THREE.Vector3(...),
  161. new THREE.Vector3(...),
  162. ]
  163. </pre>
  164. <p>но часто гораздо проще просто попросить THREE.js вычислить для нас нормали на основе указанных позиций. </p>
  165. <p>Для нормалей грани мы бы назвали <code class="notranslate" translate="no">Geometry.computeFaceNormals</code> как в</p>
  166. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.computeFaceNormals();
  167. </pre>
  168. <p>Удаление цвета вершин и изменение материала обратно на <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a></p>
  169. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const material = new THREE.MeshBasicMaterial({vertexColors: true});
  170. +const material = new THREE.MeshPhongMaterial({color});
  171. </pre>
  172. <p>и теперь наши кубики будут освещены.
  173. </p><div translate="no" class="threejs_example_container notranslate">
  174. <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>
  175. <a class="threejs_center" href="/manual/examples/custom-geometry-cube-face-normals.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  176. </div>
  177. <p></p>
  178. <p>Использование нормалей сторон всегда даст нам граненый взгляд. Мы можем использовать нормали вершин для более гладкого вида, вызывая <code class="notranslate" translate="no">Geometry.computeVertexNormals</code> </p>
  179. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-geometry.computeFaceNormals();
  180. +geometry.computeVertexNormals();
  181. </pre>
  182. <p>К сожалению, куб не является хорошим выбором для нормалей вершин, поскольку это означает, что каждая вершина получает свою нормаль от нормалей всех граней, которые она разделяет.</p>
  183. <p></p><div translate="no" class="threejs_example_container notranslate">
  184. <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>
  185. <a class="threejs_center" href="/manual/examples/custom-geometry-cube-vertex-normals.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  186. </div>
  187. <p></p>
  188. <p>Добавление текстурных координат, иногда называемых UV, выполняется через массив слоев параллельных массивов в массив <code class="notranslate" translate="no">faces</code> , который устанавливается через <code class="notranslate" translate="no">Geometry.faceVertexUvs</code>. Для нашего куба мы могли бы сделать что-то вроде</p>
  189. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">geometry.faceVertexUvs[0].push(
  190. // front
  191. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
  192. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
  193. // right
  194. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
  195. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
  196. // back
  197. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
  198. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
  199. // left
  200. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
  201. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
  202. // top
  203. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
  204. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
  205. // bottom
  206. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 1), new THREE.Vector2(0, 1) ],
  207. [ new THREE.Vector2(0, 0), new THREE.Vector2(1, 0), new THREE.Vector2(1, 1) ],
  208. );
  209. </pre>
  210. <p>Важно отметить, что <code class="notranslate" translate="no">faceVertexUvs</code> - это массив слоев. Каждый слой представляет собой другой набор координат UV. По умолчанию есть один слой координат UV, слой 0, поэтому мы просто добавляем наши UV в этот слой. </p>
  211. <p>Давайте добавим текстуру к нашему материалу и переключимся обратно, чтобы вычислить нормали стороны </p>
  212. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-geometry.computeVertexNormals();
  213. +geometry.computeFaceNormals();
  214. +const loader = new THREE.TextureLoader();
  215. +const texture = loader.load('resources/images/star.png');
  216. function makeInstance(geometry, color, x) {
  217. - const material = new THREE.MeshPhongMaterial({color});
  218. + const material = new THREE.MeshPhongMaterial({color, map: texture});
  219. const cube = new THREE.Mesh(geometry, material);
  220. scene.add(cube);
  221. ...
  222. </pre>
  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-texcoords.html"></iframe></div>
  225. <a class="threejs_center" href="/manual/examples/custom-geometry-cube-texcoords.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  226. </div>
  227. <p></p>
  228. <p>Собрав все это вместе, давайте создадим простую сетку ландшафта на основе карты высот. </p>
  229. <p>Ландшафт на основе карты высот - это то место, где у вас есть двумерный массив высот, который вы применяете к сетке. Простой способ получить двумерный массив высот - нарисовать их в программе для редактирования изображений. Вот изображение, которое я нарисовал. Это 96x64 пикселей </p>
  230. <div class="threejs_center"><img src="../examples/resources/images/heightmap-96x64.png" style="width: 512px; image-rendering: pixelated;"></div>
  231. <p>Мы загрузим это и затем сгенерируем из него сетку карты высот. Мы можем использовать <a href="/docs/#api/en/loaders/ImageLoader"><code class="notranslate" translate="no">ImageLoader</code></a> для загрузки изображения. </p>
  232. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const imgLoader = new THREE.ImageLoader();
  233. imgLoader.load('resources/images/heightmap-96x64.png', createHeightmap);
  234. function createHeightmap(image) {
  235. // extract the data from the image by drawing it to a canvas
  236. // and calling getImageData
  237. const ctx = document.createElement('canvas').getContext('2d');
  238. const {width, height} = image;
  239. ctx.canvas.width = width;
  240. ctx.canvas.height = height;
  241. ctx.drawImage(image, 0, 0);
  242. const {data} = ctx.getImageData(0, 0, width, height);
  243. const geometry = new THREE.Geometry();
  244. </pre>
  245. <p>Мы извлекли данные из изображения, теперь мы сделаем сетку ячеек. Ячейки - это квадраты, образованные центральными точками каждого пикселя изображения </p>
  246. <div class="threejs_center"><img src="../resources/heightmap-points.svg" style="width: 500px"></div>
  247. <p>Для каждой ячейки мы сгенерируем 5 вершин. Один для каждого угла ячейки и один в центральной точке ячейки со средней высотой 4 угловых высот. </p>
  248. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cellsAcross = width - 1;
  249. const cellsDeep = height - 1;
  250. for (let z = 0; z &lt; cellsDeep; ++z) {
  251. for (let x = 0; x &lt; cellsAcross; ++x) {
  252. // compute row offsets into the height data
  253. // we multiply by 4 because the data is R,G,B,A but we
  254. // only care about R
  255. const base0 = (z * width + x) * 4;
  256. const base1 = base0 + (width * 4);
  257. // look up the height for the for points
  258. // around this cell
  259. const h00 = data[base0] / 32;
  260. const h01 = data[base0 + 4] / 32;
  261. const h10 = data[base1] / 32;
  262. const h11 = data[base1 + 4] / 32;
  263. // compute the average height
  264. const hm = (h00 + h01 + h10 + h11) / 4;
  265. // the corner positions
  266. const x0 = x;
  267. const x1 = x + 1;
  268. const z0 = z;
  269. const z1 = z + 1;
  270. // remember the first index of these 5 vertices
  271. const ndx = geometry.vertices.length;
  272. // add the 4 corners for this cell and the midpoint
  273. geometry.vertices.push(
  274. new THREE.Vector3(x0, h00, z0),
  275. new THREE.Vector3(x1, h01, z0),
  276. new THREE.Vector3(x0, h10, z1),
  277. new THREE.Vector3(x1, h11, z1),
  278. new THREE.Vector3((x0 + x1) / 2, hm, (z0 + z1) / 2),
  279. );
  280. </pre>
  281. <p>Затем мы сделаем 4 треугольника из этих 5 вершин</p>
  282. <div class="threejs_center"><img src="../resources/heightmap-triangles.svg" style="width: 500px"></div>
  283. <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> // create 4 triangles
  284. geometry.faces.push(
  285. new THREE.Face3(ndx + 0, ndx + 4, ndx + 1),
  286. new THREE.Face3(ndx + 1, ndx + 4, ndx + 3),
  287. new THREE.Face3(ndx + 3, ndx + 4, ndx + 2),
  288. new THREE.Face3(ndx + 2, ndx + 4, ndx + 0),
  289. );
  290. // add the texture coordinates for each vertex of each face
  291. const u0 = x / cellsAcross;
  292. const v0 = z / cellsDeep;
  293. const u1 = (x + 1) / cellsAcross;
  294. const v1 = (z + 1) / cellsDeep;
  295. const um = (u0 + u1) / 2;
  296. const vm = (v0 + v1) / 2;
  297. geometry.faceVertexUvs[0].push(
  298. [ new THREE.Vector2(u0, v0), new THREE.Vector2(um, vm), new THREE.Vector2(u1, v0) ],
  299. [ new THREE.Vector2(u1, v0), new THREE.Vector2(um, vm), new THREE.Vector2(u1, v1) ],
  300. [ new THREE.Vector2(u1, v1), new THREE.Vector2(um, vm), new THREE.Vector2(u0, v1) ],
  301. [ new THREE.Vector2(u0, v1), new THREE.Vector2(um, vm), new THREE.Vector2(u0, v0) ],
  302. );
  303. }
  304. }
  305. </pre>
  306. <p>и закончим это</p>
  307. <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> geometry.computeFaceNormals();
  308. // center the geometry
  309. geometry.translate(width / -2, 0, height / -2);
  310. const loader = new THREE.TextureLoader();
  311. const texture = loader.load('resources/images/star.png');
  312. const material = new THREE.MeshPhongMaterial({color: 'green', map: texture});
  313. const cube = new THREE.Mesh(geometry, material);
  314. scene.add(cube);
  315. }
  316. </pre>
  317. <p>Несколько небольших изменений, чтобы было удобнее просматривать.
  318. включим <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> </p>
  319. <ul>
  320. <li>добавим <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a></li>
  321. </ul>
  322. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">import * as THREE from '/build/three.module.js';
  323. +import {OrbitControls} from '/examples/jsm/controls/OrbitControls.js';
  324. </pre>
  325. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 75;
  326. const aspect = 2; // the canvas default
  327. const near = 0.1;
  328. -const far = 100;
  329. +const far = 200;
  330. const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  331. -camera.position.z = 5;
  332. +camera.position.set(20, 20, 20);
  333. +const controls = new OrbitControls(camera, canvas);
  334. +controls.target.set(0, 0, 0);
  335. +controls.update();
  336. </pre>
  337. <p>добавим 2 света</p>
  338. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-{
  339. +function addLight(...pos) {
  340. const color = 0xFFFFFF;
  341. const intensity = 1;
  342. const light = new THREE.DirectionalLight(color, intensity);
  343. - light.position.set(-1, 2, 4\);
  344. + light.position.set(...pos);
  345. scene.add(light);
  346. }
  347. +addLight(-1, 2, 4);
  348. +addLight(1, 2, -2);
  349. </pre>
  350. <p>и мы удалим код, связанный с вращением кубов.
  351. </p><div translate="no" class="threejs_example_container notranslate">
  352. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/custom-geometry-heightmap.html"></iframe></div>
  353. <a class="threejs_center" href="/manual/examples/custom-geometry-heightmap.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  354. </div>
  355. <p></p>
  356. <p>Я надеюсь, что это была полезная инструкция для создания вашей собственной геометрии с использованием <code class="notranslate" translate="no">Geometry</code>.
  357. В другой статье мы рассмотрим <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>. </p>
  358. <p>В <a href="custom-buffergeometry.html">другой статье</a> мы рассмотрим <a href="/docs/#api/en/core/BufferGeometry"><code class="notranslate" translate="no">BufferGeometry</code></a>. </p>
  359. </div>
  360. </div>
  361. </div>
  362. <script src="/manual/resources/prettify.js"></script>
  363. <script src="/manual/resources/lesson.js"></script>
  364. </body></html>