cameras.html 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  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">об основах</a>.
  30. Если вы её еще не читали, советую вам сделать это.</p>
  31. <p>Давайте поговорим о камерах в three.js. Мы рассмотрели некоторые из них в <a href="fundamentals.html">первой статье</a>
  32. , но мы расскажем здесь об этом более подробно.</p>
  33. <p>Самая распространенная камера в Three.js и та, которую мы использовали до этого момента, - <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a>.
  34. Она дает трехмерный вид, где вещи на расстоянии кажутся меньше, чем вещи рядом.</p>
  35. <p><a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a> определяет <em>frustum</em>. <a href="https://ru.wikipedia.org/wiki/Усечённая_пирамида"><em>Frustum</em> - усеченная пирамида, твердое тело</a>.
  36. Под твердым телом я подразумеваю, например, куб, конус, сферу,
  37. цилиндр и усеченный конус - все названия различных видов твердых тел.</p>
  38. <div class="spread">
  39. <div><div data-diagram="shapeCube"></div><div>cube</div></div>
  40. <div><div data-diagram="shapeCone"></div><div>cone</div></div>
  41. <div><div data-diagram="shapeSphere"></div><div>sphere</div></div>
  42. <div><div data-diagram="shapeCylinder"></div><div>cylinder</div></div>
  43. <div><div data-diagram="shapeFrustum"></div><div>frustum</div></div>
  44. </div>
  45. <p>Я только указываю на это, потому что я не знал это в течение многих лет.
  46. Если в какой-нибудь книге или на веб странице будет упоминание <em>frustum</em> я закатывал глаза.
  47. Понимание того, что это название сплошной формы, сделало эти описания внезапно более понятными 😅</p>
  48. <p>A <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a>определяет свой frustum на основе 4 свойств. <code class="notranslate" translate="no">near</code> определяет,
  49. где начинается фронт усечения. <code class="notranslate" translate="no">far</code> определяет, где он заканчивается. <code class="notranslate" translate="no">fov</code>поле обзора
  50. определяет высоту передней и задней частей усеченного конуса, вычисляя правильную высоту,
  51. чтобы получить указанное поле обзора в <code class="notranslate" translate="no">near</code> единицах измерения от камеры. <code class="notranslate" translate="no">aspect</code> определяет,
  52. насколько широким передние и задняя часть усеченного есть. Ширина усеченного конуса -
  53. это просто высота, умноженная на aspect.</p>
  54. <p><img src="../resources/frustum-3d.svg" width="500" class="threejs_center"></p>
  55. <p>Давайте используем сцену из <a href="lights.html">предыдущей статьи</a> которая имеет плоскость
  56. земли, сферу и куб, и сделаем так, чтобы мы могли регулировать настройки камеры</p>
  57. <p>Для этого мы сделаем <code class="notranslate" translate="no">MinMaxGUIHelper</code> для параметров <code class="notranslate" translate="no">near</code> и <code class="notranslate" translate="no">far</code>, так чтобы <code class="notranslate" translate="no">far</code>
  58. всегда был больше, чем <code class="notranslate" translate="no">near</code>. У него будут свойства <code class="notranslate" translate="no">min</code> и <code class="notranslate" translate="no">max</code>, которые lil-gui будет
  59. настраивать. После настройки они установят 2 свойства, которые мы указываем.</p>
  60. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">class MinMaxGUIHelper {
  61. constructor(obj, minProp, maxProp, minDif) {
  62. this.obj = obj;
  63. this.minProp = minProp;
  64. this.maxProp = maxProp;
  65. this.minDif = minDif;
  66. }
  67. get min() {
  68. return this.obj[this.minProp];
  69. }
  70. set min(v) {
  71. this.obj[this.minProp] = v;
  72. this.obj[this.maxProp] = Math.max(this.obj[this.maxProp], v + this.minDif);
  73. }
  74. get max() {
  75. return this.obj[this.maxProp];
  76. }
  77. set max(v) {
  78. this.obj[this.maxProp] = v;
  79. this.min = this.min; // это вызовет setter min
  80. }
  81. }
  82. </pre>
  83. <p>Теперь мы можем настроить наш графический интерфейс следующим образом</p>
  84. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function updateCamera() {
  85. camera.updateProjectionMatrix();
  86. }
  87. const gui = new GUI();
  88. gui.add(camera, 'fov', 1, 180).onChange(updateCamera);
  89. const minMaxGUIHelper = new MinMaxGUIHelper(camera, 'near', 'far', 0.1);
  90. gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near').onChange(updateCamera);
  91. gui.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far').onChange(updateCamera);
  92. </pre>
  93. <p>Каждый раз, когда меняются настройки камеры, нам нужно вызывать функцию камеры
  94. <a href="/docs/#api/en/cameras/PerspectiveCamera#updateProjectionMatrix"><code class="notranslate" translate="no">updateProjectionMatrix</code></a> поэтому мы сделали
  95. функцию <code class="notranslate" translate="no">updateCamera</code> передав ее в lil-gui, чтобы вызывать ее, когда что-то меняется.</p>
  96. <p></p><div translate="no" class="threejs_example_container notranslate">
  97. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cameras-perspective.html"></iframe></div>
  98. <a class="threejs_center" href="/manual/examples/cameras-perspective.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  99. </div>
  100. <p></p>
  101. <p>Вы можете просто значения и посмотреть, как они работают. Обратите внимание, что мы не делали
  102. <code class="notranslate" translate="no">aspect</code> сеттер, так как aspect взят из размера окна, поэтому, если вы хотите настроить aspect,
  103. откройте пример в новом окне, а затем измените размер окна.</p>
  104. <p>Тем не менее, я думаю, что это немного трудно увидеть, поэтому давайте изменим пример, чтобы он имел 2 камеры.
  105. Один покажет нашу сцену, как мы видим ее выше, другой покажет другую камеру, смотрящую на сцену,
  106. которую рисует первая камера, и показывает frustum камеры.</p>
  107. <p>Для этого мы можем использовать функцию ножниц (scissor) Three.js. Давайте изменим это, чтобы
  108. нарисовать 2 сцены с 2 камерами рядом, используя функцию scissor</p>
  109. <p>Для начала давайте используем HTML и CSS, чтобы определить 2 элемента рядом друг с другом.
  110. Это также поможет нам с событиями, так что обе камеры могут иметь свои собственные <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>.</p>
  111. <pre class="prettyprint showlinemods notranslate lang-html" translate="no">&lt;body&gt;
  112. &lt;canvas id="c"&gt;&lt;/canvas&gt;
  113. + &lt;div class="split"&gt;
  114. + &lt;div id="view1" tabindex="1"&gt;&lt;/div&gt;
  115. + &lt;div id="view2" tabindex="2"&gt;&lt;/div&gt;
  116. + &lt;/div&gt;
  117. &lt;/body&gt;
  118. </pre>
  119. <p>Для начала давайте используем HTML и CSS, чтобы расположить 2 элемента рядом друг с другом.
  120. Это также поможет нам с событиями, так что обе камеры могут иметь свои собственные</p>
  121. <pre class="prettyprint showlinemods notranslate lang-css" translate="no">.split {
  122. position: absolute;
  123. left: 0;
  124. top: 0;
  125. width: 100%;
  126. height: 100%;
  127. display: flex;
  128. }
  129. .split&gt;div {
  130. width: 100%;
  131. height: 100%;
  132. }
  133. </pre>
  134. <p>Затем в нашем коде мы добавим <a href="/docs/#api/en/helpers/CameraHelper"><code class="notranslate" translate="no">CameraHelper</code></a>. <a href="/docs/#api/en/helpers/CameraHelper"><code class="notranslate" translate="no">CameraHelper</code></a> рисует frustum для <a href="/docs/#api/en/cameras/Camera"><code class="notranslate" translate="no">Camera</code></a></p>
  135. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const cameraHelper = new THREE.CameraHelper(camera);
  136. ...
  137. scene.add(cameraHelper);
  138. </pre>
  139. <p>Теперь давайте посмотрим на 2 элемента view.</p>
  140. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const view1Elem = document.querySelector('#view1');
  141. const view2Elem = document.querySelector('#view2');
  142. </pre>
  143. <p>И мы установим нашу существующую <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> так, чтобы она отвечала
  144. только за первый элемент представления.</p>
  145. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const controls = new OrbitControls(camera, canvas);
  146. +const controls = new OrbitControls(camera, view1Elem);
  147. </pre>
  148. <p>Создадим вторую <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a> и вторую <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>.
  149. Вторая <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> привязана ко второй камере и получает
  150. ввод от второго элемента view.</p>
  151. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const camera2 = new THREE.PerspectiveCamera(
  152. 60, // fov
  153. 2, // aspect
  154. 0.1, // near
  155. 500, // far
  156. );
  157. camera2.position.set(40, 10, 30);
  158. camera2.lookAt(0, 5, 0);
  159. const controls2 = new OrbitControls(camera2, view2Elem);
  160. controls2.target.set(0, 5, 0);
  161. controls2.update();
  162. </pre>
  163. <p>Наконец, нам нужно визуализировать сцену с точки зрения каждой камеры, используя
  164. функцию ножниц (scissor), чтобы визуализировать только часть холста.</p>
  165. <p>Вот функция, которая для данного элемента будет вычислять прямоугольник этого
  166. элемента, который перекрывает холст. Затем он установит плоскость отсечения (scissor) и область
  167. просмотра (fov) в этот прямоугольник и вернет aspect для этого размера.</p>
  168. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function setScissorForElement(elem) {
  169. const canvasRect = canvas.getBoundingClientRect();
  170. const elemRect = elem.getBoundingClientRect();
  171. // вычисляем относительный прямоугольник холста
  172. const right = Math.min(elemRect.right, canvasRect.right) - canvasRect.left;
  173. const left = Math.max(0, elemRect.left - canvasRect.left);
  174. const bottom = Math.min(elemRect.bottom, canvasRect.bottom) - canvasRect.top;
  175. const top = Math.max(0, elemRect.top - canvasRect.top);
  176. const width = Math.min(canvasRect.width, right - left);
  177. const height = Math.min(canvasRect.height, bottom - top);
  178. // установка области отсечения для рендеринга только на эту часть холста
  179. renderer.setScissor(left, top, width, height);
  180. renderer.setViewport(left, top, width, height);
  181. // return aspect
  182. return width / height;
  183. }
  184. </pre>
  185. <p>И теперь мы можем использовать эту функцию, чтобы нарисовать сцену дважды в нашей функции <code class="notranslate" translate="no">render</code></p>
  186. <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> function render() {
  187. - if (resizeRendererToDisplaySize(renderer)) {
  188. - const canvas = renderer.domElement;
  189. - camera.aspect = canvas.clientWidth / canvas.clientHeight;
  190. - camera.updateProjectionMatrix();
  191. - }
  192. + resizeRendererToDisplaySize(renderer);
  193. +
  194. + // включить область отсечения
  195. + renderer.setScissorTest(true);
  196. +
  197. + // render the original view
  198. + {
  199. + const aspect = setScissorForElement(view1Elem);
  200. +
  201. + // настроить камеру для этого соотношения сторон
  202. + camera.aspect = aspect;
  203. + camera.updateProjectionMatrix();
  204. + cameraHelper.update();
  205. +
  206. + // не рисуем Helper камеры в исходном представлении
  207. + cameraHelper.visible = false;
  208. +
  209. + scene.background.set(0x000000);
  210. +
  211. + // отрисовка
  212. + renderer.render(scene, camera);
  213. + }
  214. +
  215. + // отрисовка со 2-й камеры
  216. + {
  217. + const aspect = setScissorForElement(view2Elem);
  218. +
  219. + // настроить камеру для этого соотношения сторон
  220. + camera2.aspect = aspect;
  221. + camera2.updateProjectionMatrix();
  222. +
  223. + // рисуем Helper камеры во втором представлении
  224. + cameraHelper.visible = true;
  225. +
  226. + scene.background.set(0x000040);
  227. +
  228. + renderer.render(scene, camera2);
  229. + }
  230. - renderer.render(scene, camera);
  231. requestAnimationFrame(render);
  232. }
  233. requestAnimationFrame(render);
  234. }
  235. </pre>
  236. <p>Приведенный выше код устанавливает цвет фона сцены при рендеринге
  237. второго представления темно-синим, чтобы было проще различать два представления.</p>
  238. <p>Мы также можем удалить наш <code class="notranslate" translate="no">updateCamera</code> код, так как мы обновляем все в функции <code class="notranslate" translate="no">render</code>.</p>
  239. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function updateCamera() {
  240. - camera.updateProjectionMatrix();
  241. -}
  242. const gui = new GUI();
  243. -gui.add(camera, 'fov', 1, 180).onChange(updateCamera);
  244. +gui.add(camera, 'fov', 1, 180);
  245. const minMaxGUIHelper = new MinMaxGUIHelper(camera, 'near', 'far', 0.1);
  246. -gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near').onChange(updateCamera);
  247. -gui.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far').onChange(updateCamera);
  248. +gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near');
  249. +gui.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far');
  250. </pre>
  251. <p>И теперь вы можете использовать один вид, чтобы увидеть frustum другого.</p>
  252. <p></p><div translate="no" class="threejs_example_container notranslate">
  253. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cameras-perspective-2-scenes.html"></iframe></div>
  254. <a class="threejs_center" href="/manual/examples/cameras-perspective-2-scenes.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  255. </div>
  256. <p></p>
  257. <p>Слева вы можете увидеть исходный вид, а справа вы можете увидеть вид,
  258. показывающий frustum камеры слева. Можно настроить
  259. <code class="notranslate" translate="no">near</code>, <code class="notranslate" translate="no">far</code>, <code class="notranslate" translate="no">fov</code> и перемещать камеру с помощью мыши. Вы можете увидеть,
  260. как то, что внутри frustum, показаное справа, появляется на сцене слева.</p>
  261. <p>Отрегулируйте <code class="notranslate" translate="no">near</code> примерно до 20, и вы легко увидите, как передние
  262. объекты исчезают, поскольку их больше нет в усеченном конусе.
  263. Отрегулируйте <code class="notranslate" translate="no">far</code> ниже примерно 35, и вы начнете видеть,
  264. что наземная плоскость исчезает, поскольку она больше не находится
  265. в не усеченной области.</p>
  266. <p>Возникает вопрос, почему бы просто не установить <code class="notranslate" translate="no">near</code> значение 0,0000000001 и <code class="notranslate" translate="no">far</code>
  267. 10000000000000 или что-то в этом роде, чтобы вы могли видеть все? Причина в том, что
  268. ваш GPU имеет столько точности, чтобы решить, находится ли что-то впереди или
  269. позади чего-то другого. Эта точность распределена между
  270. <code class="notranslate" translate="no">near</code> и <code class="notranslate" translate="no">far</code>. Хуже того, по умолчанию точность закрытия камеры детализирована (резкое отсечение),
  271. а точность далеко от камеры - конечна. <code class="notranslate" translate="no">near</code> медленно расширяется по мере приближения <code class="notranslate" translate="no">far</code>.</p>
  272. <p>Начиная с верхнего примера, давайте изменим код, вставив 20 сфер в ряд.</p>
  273. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  274. const sphereRadius = 3;
  275. const sphereWidthDivisions = 32;
  276. const sphereHeightDivisions = 16;
  277. const sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);
  278. const numSpheres = 20;
  279. for (let i = 0; i &lt; numSpheres; ++i) {
  280. const sphereMat = new THREE.MeshPhongMaterial();
  281. sphereMat.color.setHSL(i * .73, 1, 0.5);
  282. const mesh = new THREE.Mesh(sphereGeo, sphereMat);
  283. mesh.position.set(-sphereRadius - 1, sphereRadius + 2, i * sphereRadius * -2.2);
  284. scene.add(mesh);
  285. }
  286. }
  287. </pre>
  288. <p>и давайте установим <code class="notranslate" translate="no">near</code> = 0.00001</p>
  289. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 45;
  290. const aspect = 2; // the canvas default
  291. -const near = 0.1;
  292. +const near = 0.00001;
  293. const far = 100;
  294. const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  295. </pre>
  296. <p>Нам также нужно немного подправить код графического интерфейса,
  297. чтобы позволить 0.00001, если значение редактируется</p>
  298. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near').onChange(updateCamera);
  299. +gui.add(minMaxGUIHelper, 'min', 0.00001, 50, 0.00001).name('near').onChange(updateCamera);
  300. </pre>
  301. <p>Как ты думаешь, что произойдет?</p>
  302. <p></p><div translate="no" class="threejs_example_container notranslate">
  303. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cameras-z-fighting.html"></iframe></div>
  304. <a class="threejs_center" href="/manual/examples/cameras-z-fighting.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  305. </div>
  306. <p></p>
  307. <p>Это пример <em>z fighting</em> (<a href="https://en.wikipedia.org/wiki/Z-fighting">сшивание</a>), когда графический процессор на вашем компьютере не обладает
  308. достаточной точностью, чтобы определить, какие пиксели находятся спереди, а какие - сзади.</p>
  309. <p>На тот случай, если проблема не отображается на вашей машине, вот что я вижу на своей машине</p>
  310. <div class="threejs_center"><img src="../resources/images/z-fighting.png" style="width: 570px;"></div>
  311. <p>Одно из решений состоит в том, чтобы указать использование three.js другому методу для вычисления того,
  312. какие пиксели находятся спереди, а какие - сзади. Мы можем сделать это, включив,
  313. <code class="notranslate" translate="no">logarithmicDepthBuffer</code> когда мы создаем <a href="/docs/#api/en/renderers/WebGLRenderer"><code class="notranslate" translate="no">WebGLRenderer</code></a></p>
  314. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
  315. +const renderer = new THREE.WebGLRenderer({
  316. + antialias: true,
  317. + canvas,
  318. + logarithmicDepthBuffer: true,
  319. +});
  320. </pre>
  321. <p>и с этим это может работать</p>
  322. <p></p><div translate="no" class="threejs_example_container notranslate">
  323. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cameras-logarithmic-depth-buffer.html"></iframe></div>
  324. <a class="threejs_center" href="/manual/examples/cameras-logarithmic-depth-buffer.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  325. </div>
  326. <p></p>
  327. <p>Если это не помогло решить проблему, вы столкнулись с одной из причин, по которой
  328. вы не всегда можете использовать это решение. Причина в том, что это поддерживают
  329. только определенные графические процессоры. По состоянию на сентябрь 2018 года
  330. практически ни одно мобильное устройство не поддерживает это решение, как это
  331. делают большинство настольных компьютеров.</p>
  332. <p>Другая причина не выбирать это решение - оно может быть значительно медленнее,
  333. чем стандартное решение.</p>
  334. <p>Даже при таком решении разрешение все еще ограничено. Сделайте <code class="notranslate" translate="no">near</code> еще меньше или
  335. <code class="notranslate" translate="no">far</code> больше, и вы в конечном итоге столкнетесь с теми же проблемами.</p>
  336. <p>Это означает, что вы всегда должны прилагать усилия к тому, чтобы выбрать параметр <code class="notranslate" translate="no">near</code>
  337. и <code class="notranslate" translate="no">far</code>, которые соответствуют вашему варианту использования.
  338. Установите <code class="notranslate" translate="no">near</code> как можно дальше от камеры, чтобы все не исчезло.
  339. Установите <code class="notranslate" translate="no">far</code> как можно ближе к камере, чтобы все не исчезло. Если вы пытаетесь
  340. нарисовать гигантскую сцену и показать крупным планом чье-то лицо, чтобы вы
  341. могли видеть их ресницы, в то время как на заднем плане вы можете видеть весь
  342. путь в горы на расстоянии 50 километров, тогда вам нужно будет найти другое
  343. креативные решения, которые, возможно, мы рассмотрим позже. На данный момент,
  344. просто знайте, что вы должны позаботиться о том, чтобы выбрать подходящие
  345. <code class="notranslate" translate="no">near</code> и <code class="notranslate" translate="no">far</code> для ваших нужд.</p>
  346. <p>2-ая ​​самая распространенная камера - <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>. Вместо того,
  347. чтобы указать frustum он указывает прямоугольный паралелепипед (box)
  348. с параметрами <code class="notranslate" translate="no">left</code>, <code class="notranslate" translate="no">right</code>, <code class="notranslate" translate="no">top</code>, <code class="notranslate" translate="no">bottom</code>, <code class="notranslate" translate="no">near</code>, и <code class="notranslate" translate="no">far</code>.
  349. Поскольку он проецирует box, перспективы нет.</p>
  350. <p>Давайте изменим приведенный выше пример 2 для использования <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>
  351. в первом представлении.</p>
  352. <p>Сначала давайте настроим <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>.</p>
  353. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const left = -1;
  354. const right = 1;
  355. const top = 1;
  356. const bottom = -1;
  357. const near = 5;
  358. const far = 50;
  359. const camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);
  360. camera.zoom = 0.2;
  361. </pre>
  362. <p>Мы устанавливаем <code class="notranslate" translate="no">left</code> и <code class="notranslate" translate="no">bottom</code> = -1 и <code class="notranslate" translate="no">right</code> и <code class="notranslate" translate="no">top</code> = 1. Это сделало бы
  363. прямоугольник шириной 2 единицы и высотой 2 единицы, но мы собираемся отрегулировать <code class="notranslate" translate="no">left</code> и <code class="notranslate" translate="no">top</code>
  364. в соответствии со отношением сторон прямоугольника, к которому мы рисуем.
  365. Мы будем использовать свойство <code class="notranslate" translate="no">zoom</code>, чтобы упростить настройку количества единиц, отображаемых камерой.</p>
  366. <p>Давайте добавим настройки GUI для <code class="notranslate" translate="no">zoom</code></p>
  367. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const gui = new GUI();
  368. +gui.add(camera, 'zoom', 0.01, 1, 0.01).listen();
  369. </pre>
  370. <p>Вызовем <code class="notranslate" translate="no">listen</code> говорящий lil-gui следить за изменениями.
  371. Потому что <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> также может управлять масштабированием.
  372. Например, колесо прокрутки на мыши будет масштабироваться с помощью <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>.</p>
  373. <p>Наконец, нам просто нужно изменить часть, которая отображает левую сторону,
  374. чтобы обновить <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>.</p>
  375. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  376. const aspect = setScissorForElement(view1Elem);
  377. // обновить камеру для этого соотношения сторон
  378. - camera.aspect = aspect;
  379. + camera.left = -aspect;
  380. + camera.right = aspect;
  381. camera.updateProjectionMatrix();
  382. cameraHelper.update();
  383. // не рисуем Helper камеры в исходном view
  384. cameraHelper.visible = false;
  385. scene.background.set(0x000000);
  386. renderer.render(scene, camera);
  387. }
  388. </pre>
  389. <p>и теперь вы можете увидеть <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> в работе.</p>
  390. <p></p><div translate="no" class="threejs_example_container notranslate">
  391. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cameras-orthographic-2-scenes.html"></iframe></div>
  392. <a class="threejs_center" href="/manual/examples/cameras-orthographic-2-scenes.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  393. </div>
  394. <p></p>
  395. <p><a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> чаще всего используется для рисования 2D-объектов.
  396. Вы решаете, сколько единиц вы хотите, чтобы камера показывала. Например,
  397. если вы хотите, чтобы один пиксель холста соответствовал одному элементу
  398. камеры, вы можете сделать что-то вроде:</p>
  399. <p>Поместить начало координат в центр и иметь 1 пиксель = 1 единицу three.js что-то вроде:</p>
  400. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">camera.left = -canvas.width / 2;
  401. camera.right = canvas.width / 2;
  402. camera.top = canvas.heigth / 2;
  403. camera.bottom = -canvas.height / 2;
  404. camera.near = -1;
  405. camera.far = 1;
  406. camera.zoom = 1;
  407. </pre>
  408. <p>Или, если бы мы хотели, чтобы источник находился в верхнем левом углу,
  409. как 2D-холст, мы могли бы использовать это</p>
  410. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">camera.left = 0;
  411. camera.right = canvas.width;
  412. camera.top = 0;
  413. camera.bottom = canvas.height;
  414. camera.near = -1;
  415. camera.far = 1;
  416. camera.zoom = 1;
  417. </pre>
  418. <p>В этом случае верхний левый угол будет 0,0, как 2D холст</p>
  419. <p>Давай попробуем! Сначала давайте настроим камеру</p>
  420. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const left = 0;
  421. const right = 300; // default canvas size
  422. const top = 0;
  423. const bottom = 150; // default canvas size
  424. const near = -1;
  425. const far = 1;
  426. const camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);
  427. camera.zoom = 1;
  428. </pre>
  429. <p>Затем давайте загрузим 6 текстур и сделаем 6 плоскостей, по одной на каждую текстуру.
  430. Мы будем привязывать каждую плоскость к <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">THREE.Object3D</code></a> чтобы было легче сместить плоскость,
  431. чтобы ее центр находился в ее верхнем левом углу.</p>
  432. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const loader = new THREE.TextureLoader();
  433. const textures = [
  434. loader.load('../resources/images/flower-1.jpg'),
  435. loader.load('../resources/images/flower-2.jpg'),
  436. loader.load('../resources/images/flower-3.jpg'),
  437. loader.load('../resources/images/flower-4.jpg'),
  438. loader.load('../resources/images/flower-5.jpg'),
  439. loader.load('../resources/images/flower-6.jpg'),
  440. ];
  441. const planeSize = 256;
  442. const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);
  443. const planes = textures.map((texture) =&gt; {
  444. const planePivot = new THREE.Object3D();
  445. scene.add(planePivot);
  446. texture.magFilter = THREE.NearestFilter;
  447. const planeMat = new THREE.MeshBasicMaterial({
  448. map: texture,
  449. side: THREE.DoubleSide,
  450. });
  451. const mesh = new THREE.Mesh(planeGeo, planeMat);
  452. planePivot.add(mesh);
  453. // move plane so top left corner is origin
  454. mesh.position.set(planeSize / 2, planeSize / 2, 0);
  455. return planePivot;
  456. });
  457. </pre>
  458. <p>и нам нужно обновить камеру, если размер холста изменится.</p>
  459. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render() {
  460. if (resizeRendererToDisplaySize(renderer)) {
  461. camera.right = canvas.width;
  462. camera.bottom = canvas.height;
  463. camera.updateProjectionMatrix();
  464. }
  465. ...
  466. </pre>
  467. <p><code class="notranslate" translate="no">planes</code> - массив <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">THREE.Mesh</code></a>, по одному для каждой плоскости.
  468. Давайте переместим их в зависимости от времени.</p>
  469. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
  470. time *= 0.001; // конвертировать в секунды;
  471. ...
  472. const distAcross = Math.max(20, canvas.width - planeSize);
  473. const distDown = Math.max(20, canvas.height - planeSize);
  474. // total distance to move across and back
  475. const xRange = distAcross * 2;
  476. const yRange = distDown * 2;
  477. const speed = 180;
  478. planes.forEach((plane, ndx) =&gt; {
  479. // compute a unique time for each plane
  480. const t = time * speed + ndx * 300;
  481. // get a value between 0 and range
  482. const xt = t % xRange;
  483. const yt = t % yRange;
  484. // set our position going forward if 0 to half of range
  485. // and backward if half of range to range
  486. const x = xt &lt; distAcross ? xt : xRange - xt;
  487. const y = yt &lt; distDown ? yt : yRange - yt;
  488. plane.position.set(x, y, 0);
  489. });
  490. renderer.render(scene, camera);
  491. </pre>
  492. <p>И вы можете видеть, как изображения отскакивают от пикселей идеально по краям холста,
  493. используя пиксельную математику, как 2D холст</p>
  494. <p></p><div translate="no" class="threejs_example_container notranslate">
  495. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cameras-orthographic-canvas-top-left-origin.html"></iframe></div>
  496. <a class="threejs_center" href="/manual/examples/cameras-orthographic-canvas-top-left-origin.html" target="_blank">нажмите здесь, чтобы открыть в отдельном окне</a>
  497. </div>
  498. <p></p>
  499. <p>Другое распространенное использование <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> для рисования - это отображение вверх,
  500. вниз, влево, вправо, спереди, сзади программ трехмерного моделирования или редактора игрового движка.</p>
  501. <div class="threejs_center"><img src="../resources/images/quad-viewport.png" style="width: 574px;"></div>
  502. <p>На скриншоте выше вы можете видеть 1 вид в перспективе и 3 вида в ортогональном виде.</p>
  503. <p>Это основы камер. Мы рассмотрим несколько распространенных способов перемещения камер в других статьях.
  504. А пока давайте перейдем к <a href="shadows.html">теням</a>.</p>
  505. <p><canvas id="c"></canvas></p>
  506. <script type="module" src="../resources/threejs-cameras.js"></script>
  507. </div>
  508. </div>
  509. </div>
  510. <script src="../resources/prettify.js"></script>
  511. <script src="../resources/lesson.js"></script>
  512. </body></html>