textures.html 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. <!DOCTYPE html><html lang="ru"><head>
  2. <meta charset="utf-8">
  3. <title>Текстуры</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 – Текстуры">
  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="../resources/lesson.css">
  12. <link rel="stylesheet" href="../resources/lang.css">
  13. <script type="importmap">
  14. {
  15. "imports": {
  16. "three": "../../build/three.module.js"
  17. }
  18. }
  19. </script>
  20. </head>
  21. <body>
  22. <div class="container">
  23. <div class="lesson-title">
  24. <h1>Текстуры</h1>
  25. </div>
  26. <div class="lesson">
  27. <div class="lesson-main">
  28. <p>Эта статья является частью серии статей о three.js.
  29. Первая статья - <a href="fundamentals.html">основы Three.js</a>.
  30. <a href="setup.html">Предыдущая статья</a> была о настройках окружения для этой статьи.
  31. Если вы их еще не читали, советую вам сделать это.</p>
  32. <p>Текстуры - это своего рода большая тема в Three.js, и я не уверен на 100%, на каком
  33. уровне их объяснить, но я постараюсь. По ним есть много тем, и многие из них взаимосвязаны,
  34. поэтому трудно объяснить все сразу. Вот краткое содержание этой статьи.</p>
  35. <ul>
  36. <li><a href="#hello">Hello Texture</a></li>
  37. <li><a href="#six">6 текстур, разные на каждой грани куба</a></li>
  38. <li><a href="#loading">Загрузка текстур</a></li>
  39. <ul>
  40. <li><a href="#easy">Легкий путь</a></li>
  41. <li><a href="#wait1">Ожидание загрузки текстуры</a></li>
  42. <li><a href="#waitmany">Ожидание загрузки нескольких текстур</a></li>
  43. <li><a href="#cors">Загрузка текстур из других доменов Cross-origin</a></li>
  44. </ul>
  45. <li><a href="#memory">Использование памяти</a></li>
  46. <li><a href="#format">JPG против PNG</a></li>
  47. <li><a href="#filtering-and-mips">Фильтрация и Mips</a></li>
  48. <li><a href="#uvmanipulation">Повторение, сдвиг, вращение, наложение</a></li>
  49. </ul>
  50. <h2 id="-a-name-hello-a-hello-texture"><a name="hello"></a> Hello Texture</h2>
  51. <p>Текстуры, <em>как правило</em> представляют собой изображения, которые чаще всего создаются в
  52. какой-либо сторонней программе, такой как Photoshop или GIMP. Например,
  53. давайте поместим это изображение на куб.</p>
  54. <div class="threejs_center">
  55. <img src="../examples/resources/images/wall.jpg" style="width: 600px;" class="border">
  56. </div>
  57. <p>Мы изменим один из наших первых примеров. Все, что нам нужно сделать, это создать <a href="/docs/#api/en/loaders/TextureLoader"><code class="notranslate" translate="no">TextureLoader</code></a>. Вызовите
  58. <a href="/docs/#api/en/loaders/TextureLoader#load"><code class="notranslate" translate="no">load</code></a> метод с URL-адресом изображения и установите для
  59. изображения и установите его возвращаемое значение для <code class="notranslate" translate="no">map</code> свойства материала, вместо установки <code class="notranslate" translate="no">color</code>.</p>
  60. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loader = new THREE.TextureLoader();
  61. +const texture = loader.load( 'resources/images/wall.jpg' );
  62. +texture.colorSpace = THREE.SRGBColorSpace;
  63. const material = new THREE.MeshBasicMaterial({
  64. - color: 0xFF8844,
  65. + map: texture,
  66. });
  67. </pre>
  68. <p>Обратите внимание, что мы используем <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> поэтому не нужно никаких источников света.</p>
  69. <p></p><div translate="no" class="threejs_example_container notranslate">
  70. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube.html"></iframe></div>
  71. <a class="threejs_center" href="/manual/examples/textured-cube.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  72. </div>
  73. <p></p>
  74. <h2 id="-a-name-six-a-6-"><a name="six"></a> 6 текстур, разные для каждой грани куба</h2>
  75. <p>Как насчет 6 текстур, по одной на каждой грани куба?</p>
  76. <div class="threejs_center">
  77. <div>
  78. <img src="../examples/resources/images/flower-1.jpg" style="width: 100px;" class="border">
  79. <img src="../examples/resources/images/flower-2.jpg" style="width: 100px;" class="border">
  80. <img src="../examples/resources/images/flower-3.jpg" style="width: 100px;" class="border">
  81. </div>
  82. <div>
  83. <img src="../examples/resources/images/flower-4.jpg" style="width: 100px;" class="border">
  84. <img src="../examples/resources/images/flower-5.jpg" style="width: 100px;" class="border">
  85. <img src="../examples/resources/images/flower-6.jpg" style="width: 100px;" class="border">
  86. </div>
  87. </div>
  88. <p>Мы просто создадим 6 материалов и передаем их в виде массива при создании <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a></p>
  89. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const loader = new THREE.TextureLoader();
  90. -const texture = loader.load( 'resources/images/wall.jpg' );
  91. -texture.colorSpace = THREE.SRGBColorSpace;
  92. -const material = new THREE.MeshBasicMaterial({
  93. - map: texture,
  94. -});
  95. +const materials = [
  96. + new THREE.MeshBasicMaterial({map: loadColorTexture('resources/images/flower-1.jpg')}),
  97. + new THREE.MeshBasicMaterial({map: loadColorTexture('resources/images/flower-2.jpg')}),
  98. + new THREE.MeshBasicMaterial({map: loadColorTexture('resources/images/flower-3.jpg')}),
  99. + new THREE.MeshBasicMaterial({map: loadColorTexture('resources/images/flower-4.jpg')}),
  100. + new THREE.MeshBasicMaterial({map: loadColorTexture('resources/images/flower-5.jpg')}),
  101. + new THREE.MeshBasicMaterial({map: loadColorTexture('resources/images/flower-6.jpg')}),
  102. +];
  103. -const cube = new THREE.Mesh(geometry, material);
  104. +const cube = new THREE.Mesh(geometry, materials);
  105. +function loadColorTexture( path ) {
  106. + const texture = loader.load( path );
  107. + texture.colorSpace = THREE.SRGBColorSpace;
  108. + return texture;
  109. +}
  110. </pre>
  111. <p>Оно работает!</p>
  112. <p></p><div translate="no" class="threejs_example_container notranslate">
  113. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube-6-textures.html"></iframe></div>
  114. <a class="threejs_center" href="/manual/examples/textured-cube-6-textures.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  115. </div>
  116. <p></p>
  117. <p>Однако следует отметить, что по умолчанию единственной геометрией, которая поддерживает
  118. несколько материалов, является <a href="/docs/#api/en/geometries/BoxGeometry"><code class="notranslate" translate="no">BoxGeometry</code></a> и <a href="/docs/#api/en/geometries/ConeGeometry"><code class="notranslate" translate="no">ConeGeometry</code></a>. В других случаях вам
  119. нужно будет создать или загрузить пользовательскую геометрию и/или изменить координаты
  120. текстуры. Гораздо более распространенным является использование
  121. <a href="https://ru.wikipedia.org/wiki/Текстурный_атлас">Текстурного атласа</a>
  122. когда вы хотите разрешить несколько изображений для одной геометрии.</p>
  123. <p>Что такое координаты текстуры? Это данные, добавленные к каждой вершине
  124. геометрического фрагмента, которые определяют, какая часть текстуры
  125. соответствует этой конкретной вершине. Мы рассмотрим их, когда
  126. начнем создавать собственную геометрию.</p>
  127. <h2 id="-a-name-loading-a-"><a name="loading"></a> Загрузка текстур</h2>
  128. <h3 id="-a-name-easy-a-"><a name="easy"></a> Легкий путь</h3>
  129. <p>Большая часть кода на этом сайте использует самый простой способ загрузки текстур.
  130. Мы создаем <a href="/docs/#api/en/loaders/TextureLoader"><code class="notranslate" translate="no">TextureLoader</code></a> и затем вызываем <a href="/docs/#api/en/loaders/TextureLoader#load"><code class="notranslate" translate="no">load</code></a> метод.
  131. Возвращающий объект <a href="/docs/#api/en/textures/Texture"><code class="notranslate" translate="no">Texture</code></a>.</p>
  132. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const texture = loader.load('../resources/images/flower-1.jpg');
  133. </pre>
  134. <p>Важно отметить, что при использовании этого метода наша текстура будет прозрачной,
  135. пока изображение не будет загружено асинхронно с помощью three.js,
  136. после чего он обновит текстуру загруженным изображением.</p>
  137. <p>Это имеет большое преимущество в том, что нам не нужно ждать загрузки текстуры,
  138. и наша страница начнет отрисовку немедленно. Это, вероятно, хорошо для очень
  139. многих случаев использования, но если мы хотим, мы можем попросить three.js
  140. сообщить нам, когда текстура закончила загрузку.</p>
  141. <h3 id="-a-name-wait1-a-"><a name="wait1"></a> Ожидание загрузки текстуры</h3>
  142. <p>Чтобы дождаться загрузки текстуры, <code class="notranslate" translate="no">load</code> метод загрузчика текстуры принимает
  143. обратный вызов, который будет вызван после завершения загрузки текстуры.
  144. Возвращаясь к нашему верхнему примеру, мы можем дождаться загрузки текстуры,
  145. прежде чем создавать нашу <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">Mesh</code></a> и добавлять ее в сцену следующим образом.</p>
  146. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const loader = new THREE.TextureLoader();
  147. loader.load('../resources/images/wall.jpg', (texture) =&gt; {
  148. const material = new THREE.MeshBasicMaterial({
  149. map: texture,
  150. });
  151. const cube = new THREE.Mesh(geometry, material);
  152. scene.add(cube);
  153. cubes.push(cube); // добавляем в наш список кубиков для вращения
  154. });
  155. </pre>
  156. <p>Если вы не очистите кеш вашего браузера и у вас не будет медленного соединения,
  157. вы вряд ли увидите разницу, но будьте уверены, что она ожидает загрузки текстуры.</p>
  158. <p></p><div translate="no" class="threejs_example_container notranslate">
  159. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube-wait-for-texture.html"></iframe></div>
  160. <a class="threejs_center" href="/manual/examples/textured-cube-wait-for-texture.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  161. </div>
  162. <p></p>
  163. <h3 id="-a-name-waitmany-a-"><a name="waitmany"></a> Ожидание загрузки нескольких текстур</h3>
  164. <p>Чтобы дождаться загрузки всех текстур, вы можете использовать <a href="/docs/#api/en/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a>.
  165. Создайте его и передайте его в <a href="/docs/#api/en/loaders/TextureLoader"><code class="notranslate" translate="no">TextureLoader</code></a>, а затем установите его <a href="/docs/#api/en/loaders/managers/LoadingManager#onLoad"><code class="notranslate" translate="no">onLoad</code></a>
  166. свойство для обратного вызова.</p>
  167. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loadManager = new THREE.LoadingManager();
  168. *const loader = new THREE.TextureLoader(loadManager);
  169. const materials = [
  170. new THREE.MeshBasicMaterial({map: loader.load('../resources/images/flower-1.jpg')}),
  171. new THREE.MeshBasicMaterial({map: loader.load('../resources/images/flower-2.jpg')}),
  172. new THREE.MeshBasicMaterial({map: loader.load('../resources/images/flower-3.jpg')}),
  173. new THREE.MeshBasicMaterial({map: loader.load('../resources/images/flower-4.jpg')}),
  174. new THREE.MeshBasicMaterial({map: loader.load('../resources/images/flower-5.jpg')}),
  175. new THREE.MeshBasicMaterial({map: loader.load('../resources/images/flower-6.jpg')}),
  176. ];
  177. +loadManager.onLoad = () =&gt; {
  178. + const cube = new THREE.Mesh(geometry, materials);
  179. + scene.add(cube);
  180. + cubes.push(cube); // add to our list of cubes to rotate
  181. +};
  182. </pre>
  183. <p><a href="/docs/#api/en/loaders/managers/LoadingManager"><code class="notranslate" translate="no">LoadingManager</code></a> также имеет <a href="/docs/#api/en/loaders/managers/LoadingManager#onProgress"><code class="notranslate" translate="no">onProgress</code></a> свойство и его
  184. можно установить в другой функции обратного вызова, чтобы показать индикатор прогресса.</p>
  185. <p>Сначала мы добавим индикатор выполнения (progress bar) в HTML</p>
  186. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
  187. &lt;canvas id="c"&gt;&lt;/canvas&gt;
  188. + &lt;div id="loading"&gt;
  189. + &lt;div class="progress"&gt;&lt;div class="progressbar"&gt;&lt;/div&gt;&lt;/div&gt;
  190. + &lt;/div&gt;
  191. &lt;/body&gt;
  192. </pre>
  193. <p>и CSS для этого</p>
  194. <pre class="prettyprint showlinemods notranslate lang-css" translate="no">#loading {
  195. position: fixed;
  196. top: 0;
  197. left: 0;
  198. width: 100%;
  199. height: 100%;
  200. display: flex;
  201. justify-content: center;
  202. align-items: center;
  203. }
  204. #loading .progress {
  205. margin: 1.5em;
  206. border: 1px solid white;
  207. width: 50vw;
  208. }
  209. #loading .progressbar {
  210. margin: 2px;
  211. background: white;
  212. height: 1em;
  213. transform-origin: top left;
  214. transform: scaleX(0);
  215. }
  216. </pre>
  217. <p>Затем в коде мы обновим масштаб (scale) <code class="notranslate" translate="no">progressbar</code> в <code class="notranslate" translate="no">onProgress</code>.
  218. Он вызывается с URL-адресом последнего загруженного элемента, количества загруженных
  219. элементов и общего количества загруженных элементов.</p>
  220. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const loadingElem = document.querySelector('#loading');
  221. +const progressBarElem = loadingElem.querySelector('.progressbar');
  222. loadManager.onLoad = () =&gt; {
  223. + loadingElem.style.display = 'none';
  224. const cube = new THREE.Mesh(geometry, materials);
  225. scene.add(cube);
  226. cubes.push(cube); // добавляем в наш список кубиков для вращения
  227. };
  228. +loadManager.onProgress = (urlOfLastItemLoaded, itemsLoaded, itemsTotal) =&gt; {
  229. + const progress = itemsLoaded / itemsTotal;
  230. + progressBarElem.style.transform = `scaleX(${progress})`;
  231. +};
  232. </pre>
  233. <p>Если вы не очистите свой кеш и у вас медленное соединение, вы можете не увидеть полосу загрузки.</p>
  234. <p></p><div translate="no" class="threejs_example_container notranslate">
  235. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube-wait-for-all-textures.html"></iframe></div>
  236. <a class="threejs_center" href="/manual/examples/textured-cube-wait-for-all-textures.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  237. </div>
  238. <p></p>
  239. <h2 id="-a-name-cors-a-cros"><a name="cors"></a> Загрузка текстур из других источников. CROS</h2>
  240. <p>Чтобы использовать изображения с других серверов, эти сервера должны отправлять правильные заголовки.
  241. Если этого не произойдет, вы не сможете использовать изображения в three.js и получите ошибку.
  242. Если вы запускаете сервер, предоставляющий изображения, убедитесь, что он
  243. <a href="https://developer.mozilla.org/ru/docs/Web/HTTP/CORS"> отправляет правильные заголовки</a>.
  244. Если вы не управляете сервером, на котором размещены изображения, и он не отправляет заголовки разрешений,
  245. вы не сможете использовать изображения с этого сервера.</p>
  246. <p>Например <a href="https://imgur.com">imgur</a>, <a href="https://flickr.com">flickr</a> и
  247. <a href="https://github.com">github</a> - все заголовки отправки, позволяющие вам использовать изображения,
  248. размещенные на их серверах в three.js. Большинство других сайтов этого не делают.</p>
  249. <h2 id="-a-name-memory-a-"><a name="memory"></a> Использование памяти</h2>
  250. <p>Текстуры часто являются частью приложения three.js, которое использует больше всего памяти.
  251. Важно понимать, что <em>обычно</em>, текстуры занимают <code class="notranslate" translate="no">width * height * 4 * 1.33</code> байт в памяти. </p>
  252. <p>Обратите внимание, что никто не говорит о сжатии. Я могу сделать изображение в формате
  253. .jpg и установить его компрессию очень высокой. Например, допустим, я делал сцену из дома.
  254. Внутри дома есть стол, и я решил положить эту текстуру дерева на верхнюю поверхность стола.</p>
  255. <div class="threejs_center"><img class="border" src="../resources/images/compressed-but-large-wood-texture.jpg" align="center" style="width: 300px"></div>
  256. <p>Это изображение всего 157 Кб, поэтому оно будет загружаться относительно быстро, но на
  257. <a href="resources/images/compressed-but-large-wood-texture.jpg">самом деле оно имеет размер 3024 x 3761 пикселей</a>.
  258. Следуя приведенному выше уравнению, это</p>
  259. <pre class="prettyprint showlinemods notranslate notranslate" translate="no">3024 * 3761 * 4 * 1.33 = 60505764.5
  260. </pre><p>Это изображение займет <strong>60 Мб ПАМЯТИ!</strong> в three.js.
  261. Несколько таких текстур, и вам не хватит памяти.</p>
  262. <p>Я поднял этот вопрос, потому что важно знать, что использование текстур имеет скрытую стоимость.
  263. Для того чтобы three.js использовал текстуру, он должен передать ее в графический процессор,
  264. а графический процессор <em>обычно</em> требует, чтобы данные текстуры были несжатыми.</p>
  265. <p>Мораль этой истории: делайте ваши текстуры небольшими по размеру, а не просто
  266. маленькими по размеру файла. Небольшой размер файла = быстрая загрузка.
  267. Маленький в размер = занимает меньше памяти.
  268. Насколько маленькими вы должны сделать их?
  269. Настолько, насколько это возможно! И при этом выглядящими так хорошо, как вам нужно.</p>
  270. <h2 id="-a-name-format-a-jpg-png"><a name="format"></a> JPG против PNG</h2>
  271. <p>Это почти то же самое, что и обычный HTML, поскольку JPG-файлы имеют сжатие с потерями,
  272. PNG-файлы имеют сжатие без потерь, поэтому PNG-файлы обычно загружаются медленнее.
  273. Но PNG поддерживают прозрачность. PNG также, вероятно, является подходящим форматом
  274. для данных без реалистичных изображений, таких как карты нормалей, и других видов карт без реалистичных изображений,
  275. которые мы рассмотрим позже.</p>
  276. <p>Важно помнить, что JPG не использует меньше памяти, чем PNG в WebGL. Смотри выше.</p>
  277. <h2 id="-a-name-filtering-and-mips-a-mips"><a name="filtering-and-mips"></a> Фильтрация и Mips</h2>
  278. <p>Давайте применим эту текстуру 16x16</p>
  279. <div class="threejs_center"><img src="../resources/images/mip-low-res-enlarged.png" class="border" align="center"></div>
  280. <p>Это куб</p>
  281. <div class="spread"><div data-diagram="filterCube"></div></div>
  282. <p>Давайте нарисуем этот кубик действительно маленьким</p>
  283. <div class="spread"><div data-diagram="filterCubeSmall"></div></div>
  284. <p>Хммм, я думаю, это трудно увидеть. Давайте увеличим этот крошечный куб</p>
  285. <div class="spread"><div data-diagram="filterCubeSmallLowRes"></div></div>
  286. <p>Как GPU узнает, какие цвета нужно сделать для каждого пикселя, который он рисует для крошечного куба?
  287. Что если куб был настолько мал, что его размер составлял всего 1 или 2 пикселя?</p>
  288. <p>Вот что такое фильтрация.</p>
  289. <p>Если бы это был Photoshop, Photoshop усреднил бы почти все пиксели вместе,
  290. чтобы выяснить, какой цвет сделать эти 1 или 2 пикселя.
  291. Это было бы очень медленной операцией.
  292. Графические процессоры решают эту проблему с помощью mipmaps.</p>
  293. <p>Mips - это копии текстуры, каждая из которых в два раза меньше ширины и в два раза меньше, чем предыдущий мип,
  294. где пиксели были смешаны, чтобы сделать следующий меньший мип.
  295. Мипы создаются до тех пор, пока мы не доберемся до 1 х 1 пикселя.
  296. Поскольку изображение выше всех мипов в конечном итоге будет что-то вроде этого</p>
  297. <div class="threejs_center"><img src="../resources/images/mipmap-low-res-enlarged.png" class="nobg" align="center"></div>
  298. <p>Теперь, когда куб нарисован настолько маленьким, что его размер составляет всего 1 или 2 пикселя,
  299. графический процессор может использовать только наименьший или почти минимальный уровень мипа,
  300. чтобы решить, какой цвет создать крошечный куб.</p>
  301. <p>В three вы можете выбрать, что будет происходить, когда текстура рисуется больше,
  302. чем ее исходный размер, и что происходит, когда она рисуется меньше, чем ее исходный размер.</p>
  303. <p>Для установки фильтра, когда текстура рисуется больше исходного размера,
  304. вы устанавливаете <a href="/docs/#api/en/textures/Texture#magFilter"><code class="notranslate" translate="no">texture.magFilter</code></a> свойство либо на <code class="notranslate" translate="no">THREE.NearestFilter</code> либо на
  305. <code class="notranslate" translate="no">THREE.LinearFilter</code>. <code class="notranslate" translate="no">NearestFilter</code> просто выбрает один пиксель из оригинальной текстуры.
  306. С текстурой низкого разрешения это дает вам очень пиксельный вид как Minecraft.</p>
  307. <p><code class="notranslate" translate="no">LinearFilter</code> выбрает 4 пикселя из текстуры, которые находятся ближе всего к тому месту,
  308. где мы должны выбирать цвет, и смешает их в соответствующих пропорциях относительно того,
  309. как далеко фактическая точка находится от каждого из 4 пикселей.</p>
  310. <div class="spread">
  311. <div>
  312. <div data-diagram="filterCubeMagNearest" style="height: 250px;"></div>
  313. <div class="code">Nearest</div>
  314. </div>
  315. <div>
  316. <div data-diagram="filterCubeMagLinear" style="height: 250px;"></div>
  317. <div class="code">Linear</div>
  318. </div>
  319. </div>
  320. <p>Для настройки фильтра, когда текстура нарисована меньше исходного размера,
  321. вы устанавливаете для свойства <a href="/docs/#api/en/textures/Texture#minFilter"><code class="notranslate" translate="no">texture.minFilter</code></a> одно из 6 значений.</p>
  322. <ul>
  323. <li><p><code class="notranslate" translate="no">THREE.NearestFilter</code></p>
  324. <p> так же, как и выше. Выбирает ближайший пиксель в текстуре</p>
  325. </li>
  326. <li><p><code class="notranslate" translate="no">THREE.LinearFilter</code></p>
  327. <p> Как и выше, выбирает 4 пикселя из текстуры и смешает их</p>
  328. </li>
  329. <li><p><code class="notranslate" translate="no">THREE.NearestMipmapNearestFilter</code></p>
  330. <p> выбирает соответствующий mip, затем выбирает один пиксель.</p>
  331. </li>
  332. <li><p><code class="notranslate" translate="no">THREE.NearestMipmapLinearFilter</code></p>
  333. <p> выбирает 2 mips, выбирает один пиксель из каждого, смешает 2 пикселя.</p>
  334. </li>
  335. <li><p><code class="notranslate" translate="no">THREE.LinearMipmapNearestFilter</code></p>
  336. <p> выбирает подходящий mip, затем выбирает 4 пикселя и смешает их.</p>
  337. </li>
  338. <li><p><code class="notranslate" translate="no">THREE.LinearMipmapLinearFilter</code></p>
  339. <p>выбирает 2 mips, выбирает 4 пикселя от каждого и смешает все 8 в 1 пиксель.</p>
  340. </li>
  341. </ul>
  342. <p>Вот пример, показывающий все 6 настроек</p>
  343. <div class="spread">
  344. <div data-diagram="filterModes" style="
  345. height: 450px;
  346. position: relative;
  347. ">
  348. <div style="
  349. width: 100%;
  350. height: 100%;
  351. display: flex;
  352. align-items: center;
  353. justify-content: flex-start;
  354. ">
  355. <div style="
  356. background: rgba(255,0,0,.8);
  357. color: white;
  358. padding: .5em;
  359. margin: 1em;
  360. font-size: small;
  361. border-radius: .5em;
  362. line-height: 1.2;
  363. user-select: none;">click to<br>change<br>texture</div>
  364. </div>
  365. <div class="filter-caption" style="left: 0.5em; top: 0.5em;">nearest</div>
  366. <div class="filter-caption" style="width: 100%; text-align: center; top: 0.5em;">linear</div>
  367. <div class="filter-caption" style="right: 0.5em; text-align: right; top: 0.5em;">nearest<br>mipmap<br>nearest</div>
  368. <div class="filter-caption" style="left: 0.5em; text-align: left; bottom: 0.5em;">nearest<br>mipmap<br>linear</div>
  369. <div class="filter-caption" style="width: 100%; text-align: center; bottom: 0.5em;">linear<br>mipmap<br>nearest</div>
  370. <div class="filter-caption" style="right: 0.5em; text-align: right; bottom: 0.5em;">linear<br>mipmap<br>linear</div>
  371. </div>
  372. </div>
  373. <p>Одна вещь, на которую нужно обратить внимание - это использование левого верхнего и верхнего среднего
  374. <code class="notranslate" translate="no">NearestFilter</code> и <code class="notranslate" translate="no">LinearFilter</code>
  375. не использует mips. Из-за этого они мерцают на расстоянии, потому что графический процессор выбирает
  376. пиксели из исходной текстуры. Слева выбран только один пиксель, а в середине 4 выбраны и смешаны,
  377. но этого недостаточно, чтобы придумать хороший представительный цвет. Другие 4 полоски лучше с нижним правым,
  378. <code class="notranslate" translate="no">LinearMipmapLinearFilter</code> лучший.</p>
  379. <p>Если вы нажмете на картинку выше, она переключится между текстурой, которую мы использовали выше,
  380. и текстурой, где каждый уровень мипа имеет свой цвет.</p>
  381. <div class="threejs_center">
  382. <div data-texture-diagram="differentColoredMips"></div>
  383. </div>
  384. <p>Это делает более понятным, что происходит.
  385. Вы можете видеть в верхнем левом и верхнем середине первый мип, используемый на всем пути.
  386. Справа вверху и внизу посередине видно, где используется другой мип.</p>
  387. <p>Возвращаясь к исходной текстуре, вы можете видеть, что нижний правый угол является самым плавным
  388. и с высочайшим качеством. Вы можете спросить, почему не всегда использовать этот режим.
  389. Самая очевидная причина - иногда вы хотите, чтобы вещи были пикселированы в стиле ретро
  390. или по какой-то другой причине. Следующая наиболее распространенная причина заключается в том,
  391. что чтение 8 пикселей и их смешивание медленнее, чем чтение 1 пикселя и смешивание.
  392. Хотя маловероятно, что для одной и той же текстуры будет видна разница в скорости по мере нашего
  393. углубления в эти статьи, в конечном итоге у нас будут материалы,
  394. которые используют 4 или 5 текстур одновременно. 4 текстуры * 8 пикселей на текстуру -
  395. это поиск 32 пикселей для каждого пикселя. Это может быть особенно важно учитывать на мобильных устройствах.</p>
  396. <h2 id="-a-name-uvmanipulation-a-"><a name="uvmanipulation"></a> Повторение, смещение, вращение, наложение текстуры</h2>
  397. <p>Текстуры имеют настройки для повторения, смещения и поворота текстуры.</p>
  398. <p>По умолчанию текстуры в three.js не повторяются. Чтобы установить, повторяется или нет текстура,
  399. есть 2 свойства: <a href="/docs/#api/en/textures/Texture#wrapS"><code class="notranslate" translate="no">wrapS</code></a> для горизонтального и
  400. и <a href="/docs/#api/en/textures/Texture#wrapT"><code class="notranslate" translate="no">wrapT</code></a> вертикального повторения.</p>
  401. <p>They can be set to one of:</p>
  402. <ul>
  403. <li><p><code class="notranslate" translate="no">THREE.ClampToEdgeWrapping</code></p>
  404. <p>Последний пиксель на каждом ребре повторяется всегда</p>
  405. </li>
  406. <li><p><code class="notranslate" translate="no">THREE.RepeatWrapping</code></p>
  407. <p> Текстура повторяется</p>
  408. </li>
  409. <li><p><code class="notranslate" translate="no">THREE.MirroredRepeatWrapping</code></p>
  410. <p> Текстура зеркально повторяется.</p>
  411. </li>
  412. </ul>
  413. <p>Например, чтобы включить повтор в обоих направлениях:</p>
  414. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">someTexture.wrapS = THREE.RepeatWrapping;
  415. someTexture.wrapT = THREE.RepeatWrapping;
  416. </pre>
  417. <p>Повторение устанавливается с помощью свойства [repeat].</p>
  418. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const timesToRepeatHorizontally = 4;
  419. const timesToRepeatVertically = 2;
  420. someTexture.repeat.set(timesToRepeatHorizontally, timesToRepeatVertically);
  421. </pre>
  422. <p>Смещение текстуры может быть сделано путем установки <code class="notranslate" translate="no">offset</code>.
  423. Текстуры смещены в единицах, где 1 единица = 1 размер текстуры.
  424. Другими словами, 0 = без смещения и 1 = смещение на одну полную величину текстуры. </p>
  425. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const xOffset = .5; // cмещение на половину текстуры
  426. const yOffset = .25;
  427. someTexture.offset.set(xOffset, yOffset);`
  428. </pre>
  429. <p>Вращение текстуры может быть установлено через свойство <code class="notranslate" translate="no">rotation</code> в радианах, а также свойство
  430. <code class="notranslate" translate="no">center</code> для выбора центра вращения. По умолчанию используется значение 0,0, которое
  431. вращается из нижнего левого угла. Подобно смещению, эти единицы имеют размер текстуры,
  432. поэтому установка их <code class="notranslate" translate="no">.5, .5</code> будет вращаться вокруг центра текстуры.</p>
  433. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">someTexture.center.set(.5, .5);
  434. someTexture.rotation = THREE.MathUtils.degToRad(45);
  435. </pre>
  436. <p>Давайте изменим верхний пример выше, чтобы играть с этими значениями</p>
  437. <p>Сначала мы сохраним ссылку на текстуру, чтобы мы могли манипулировать ею</p>
  438. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">+const texture = loader.load('../resources/images/wall.jpg');
  439. const material = new THREE.MeshBasicMaterial({
  440. - map: loader.load('../resources/images/wall.jpg');
  441. + map: texture,
  442. });
  443. </pre>
  444. <p>Затем мы снова будем использовать <a href="https://github.com/georgealways/lil-gui">lil-gui</a>
  445. для обеспечения простого интерфейса.</p>
  446. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">import {GUI} from 'three/addons/libs/lil-gui.module.min.js';
  447. </pre>
  448. <p>Как мы делали в предыдущих примерах lil-gui, мы будем использовать простой класс,
  449. чтобы дать lil-gui объект, которым он может манипулировать в градусах,
  450. но установит свойство в радианах.</p>
  451. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">class DegRadHelper {
  452. constructor(obj, prop) {
  453. this.obj = obj;
  454. this.prop = prop;
  455. }
  456. get value() {
  457. return THREE.MathUtils.radToDeg(this.obj[this.prop]);
  458. }
  459. set value(v) {
  460. this.obj[this.prop] = THREE.MathUtils.degToRad(v);
  461. }
  462. }
  463. </pre>
  464. <p>Нам также нужен класс, который будет конвертировать из строки, например, <code class="notranslate" translate="no">"123"</code>
  465. в число <code class="notranslate" translate="no">123</code>, так как для Three.js требуются числа для настроек перечисления,
  466. например, <code class="notranslate" translate="no">wrapS</code> и <code class="notranslate" translate="no">wrapT</code>, а lil-gui использует только строки для перечислений.</p>
  467. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">class StringToNumberHelper {
  468. constructor(obj, prop) {
  469. this.obj = obj;
  470. this.prop = prop;
  471. }
  472. get value() {
  473. return this.obj[this.prop];
  474. }
  475. set value(v) {
  476. this.obj[this.prop] = parseFloat(v);
  477. }
  478. }
  479. </pre>
  480. <p>Используя эти классы, мы можем настроить простой графический интерфейс для настроек выше</p>
  481. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const wrapModes = {
  482. 'ClampToEdgeWrapping': THREE.ClampToEdgeWrapping,
  483. 'RepeatWrapping': THREE.RepeatWrapping,
  484. 'MirroredRepeatWrapping': THREE.MirroredRepeatWrapping,
  485. };
  486. function updateTexture() {
  487. texture.needsUpdate = true;
  488. }
  489. const gui = new GUI();
  490. gui.add(new StringToNumberHelper(texture, 'wrapS'), 'value', wrapModes)
  491. .name('texture.wrapS')
  492. .onChange(updateTexture);
  493. gui.add(new StringToNumberHelper(texture, 'wrapT'), 'value', wrapModes)
  494. .name('texture.wrapT')
  495. .onChange(updateTexture);
  496. gui.add(texture.repeat, 'x', 0, 5, .01).name('texture.repeat.x');
  497. gui.add(texture.repeat, 'y', 0, 5, .01).name('texture.repeat.y');
  498. gui.add(texture.offset, 'x', -2, 2, .01).name('texture.offset.x');
  499. gui.add(texture.offset, 'y', -2, 2, .01).name('texture.offset.y');
  500. gui.add(texture.center, 'x', -.5, 1.5, .01).name('texture.center.x');
  501. gui.add(texture.center, 'y', -.5, 1.5, .01).name('texture.center.y');
  502. gui.add(new DegRadHelper(texture, 'rotation'), 'value', -360, 360)
  503. .name('texture.rotation');
  504. </pre>
  505. <p>Последнее, что следует отметить в этом примере, это то, что если вы измените <code class="notranslate" translate="no">wrapS</code> или
  506. <code class="notranslate" translate="no">wrapT</code> на текстуре, вы также должны установить <a href="/docs/#api/en/textures/Texture#needsUpdate"><code class="notranslate" translate="no">texture.needsUpdate</code></a>
  507. так, чтобы Three.js знал, чтобы применить эти настройки. Другие настройки применяются автоматически.</p>
  508. <p></p><div translate="no" class="threejs_example_container notranslate">
  509. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/textured-cube-adjust.html"></iframe></div>
  510. <a class="threejs_center" href="/manual/examples/textured-cube-adjust.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  511. </div>
  512. <p></p>
  513. <p>Это только один шаг в тему текстур. В какой-то момент мы рассмотрим текстурные координаты,
  514. а также 9 других типов текстур, которые можно применить к материалам.</p>
  515. <p>А пока давайте перейдем к <a href="lights.html">свету</a>.</p>
  516. <!--
  517. alpha
  518. ao
  519. env
  520. light
  521. specular
  522. bumpmap ?
  523. normalmap ?
  524. metalness
  525. roughness
  526. -->
  527. <p><script type="module" src="../resources/threejs-textures.js"></script></p>
  528. <link rel="stylesheet" href="../resources/threejs-textures.css">
  529. </div>
  530. </div>
  531. </div>
  532. <script src="../resources/prettify.js"></script>
  533. <script src="../resources/lesson.js"></script>
  534. </body></html>