cameras.html 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. <!DOCTYPE html><html lang="fr"><head>
  2. <meta charset="utf-8">
  3. <title>Caméras</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 – Caméras">
  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>Caméras</h1>
  25. </div>
  26. <div class="lesson">
  27. <div class="lesson-main">
  28. <p>Cet article fait partie d'une série d'articles sur three.js.
  29. Le premier article traitait <a href="fundamentals.html">des bases</a>.
  30. Si vous ne l'avez pas encore lu, vous pourriez vouloir commencer par là.</p>
  31. <p>Parlons des caméras dans three.js. Nous avons abordé une partie de cela dans le <a href="fundamentals.html">premier article</a>, mais nous allons le couvrir plus en détail ici.</p>
  32. <p>La caméra la plus courante dans three.js, et celle que nous avons utilisée jusqu'à présent, est
  33. la <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a>. Elle donne une vue 3D où les objets au loin apparaissent
  34. plus petits que les objets proches.</p>
  35. <p>La <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a> définit un <em>frustum</em>. <a href="https://en.wikipedia.org/wiki/Frustum">Un <em>frustum</em> est une forme pyramidale solide dont l'extrémité est coupée</a>.
  36. Par nom de solide, j'entends par exemple qu'un cube, un cône, une sphère, un cylindre
  37. et un frustum sont tous des noms de différents types de solides.</p>
  38. <div class="spread">
  39. <div><div data-diagram="shapeCube"></div><div>cube</div></div>
  40. <div><div data-diagram="shapeCone"></div><div>cône</div></div>
  41. <div><div data-diagram="shapeSphere"></div><div>sphère</div></div>
  42. <div><div data-diagram="shapeCylinder"></div><div>cylindre</div></div>
  43. <div><div data-diagram="shapeFrustum"></div><div>frustum</div></div>
  44. </div>
  45. <p>Je ne le signale que parce que je ne le savais pas pendant des années. Un livre ou une page mentionnait
  46. <em>frustum</em> et mes yeux se voilaient. Comprendre que c'est le nom d'un type de forme solide
  47. a rendu ces descriptions soudainement plus logiques 😅</p>
  48. <p>Une <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a> définit son frustum en fonction de 4 propriétés. <code class="notranslate" translate="no">near</code> définit où
  49. commence l'avant du frustum. <code class="notranslate" translate="no">far</code> définit où il se termine. <code class="notranslate" translate="no">fov</code>, le champ de vision, définit
  50. la hauteur de l'avant et de l'arrière du frustum en calculant la hauteur correcte pour obtenir
  51. le champ de vision spécifié à <code class="notranslate" translate="no">near</code> unités de la caméra. L'<code class="notranslate" translate="no">aspect</code> définit
  52. la largeur de l'avant et de l'arrière du frustum. La largeur du frustum est simplement la hauteur
  53. multipliée par l'aspect.</p>
  54. <p><img src="../resources/frustum-3d.svg" width="500" class="threejs_center"></p>
  55. <p>Utilisons la scène de <a href="lights.html">l'article précédent</a> qui contient un plan au sol,
  56. une sphère et un cube, et faisons en sorte de pouvoir ajuster les paramètres de la caméra.</p>
  57. <p>Pour ce faire, nous allons créer un <code class="notranslate" translate="no">MinMaxGUIHelper</code> pour les paramètres <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> afin que <code class="notranslate" translate="no">far</code>
  58. soit toujours supérieur à <code class="notranslate" translate="no">near</code>. Il aura des propriétés <code class="notranslate" translate="no">min</code> et <code class="notranslate" translate="no">max</code> que lil-gui
  59. ajustera. Lorsqu'elles seront ajustées, elles définiront les 2 propriétés que nous spécifions.</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; // ceci appellera le setter de min
  80. }
  81. }
  82. </pre>
  83. <p>Maintenant, nous pouvons configurer notre interface graphique comme ceci</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>Chaque fois que les paramètres de la caméra changent, nous devons appeler la fonction
  94. <a href="/docs/#api/en/cameras/PerspectiveCamera#updateProjectionMatrix"><code class="notranslate" translate="no">updateProjectionMatrix</code></a> de la caméra.
  95. Nous avons donc créé une fonction nommée <code class="notranslate" translate="no">updateCamera</code> et l'avons passée à lil-gui pour qu'elle soit appelée lorsque les choses changent.</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">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  99. </div>
  100. <p></p>
  101. <p>Vous pouvez ajuster les valeurs et voir comment elles fonctionnent. Notez que nous n'avons pas rendu l'<code class="notranslate" translate="no">aspect</code> modifiable car
  102. il est tiré de la taille de la fenêtre. Donc, si vous voulez ajuster l'aspect, ouvrez l'exemple
  103. dans une nouvelle fenêtre et redimensionnez-la.</p>
  104. <p>Néanmoins, je pense que c'est un peu difficile à voir, alors changeons l'exemple pour qu'il comporte 2 caméras.
  105. L'une montrera notre scène telle que nous la voyons ci-dessus, l'autre montrera une autre caméra regardant la
  106. scène que la première caméra dessine et affichant le frustum de cette caméra.</p>
  107. <p>Pour ce faire, nous pouvons utiliser la fonction scissor de three.js.
  108. Changeons-le pour dessiner 2 scènes avec 2 caméras côte à côte en utilisant la fonction scissor.</p>
  109. <p>Tout d'abord, utilisons du HTML et du CSS pour définir 2 éléments côte à côte. Cela nous
  110. aidera également avec les événements afin que les deux caméras puissent facilement avoir leurs propres <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>Et le CSS qui fera apparaître ces 2 vues côte à côte superposées sur
  120. le canvas</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>Puis dans notre code, nous ajouterons un <a href="/docs/#api/en/helpers/CameraHelper"><code class="notranslate" translate="no">CameraHelper</code></a>. Un <a href="/docs/#api/en/helpers/CameraHelper"><code class="notranslate" translate="no">CameraHelper</code></a> dessine le frustum d'une <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>Maintenant, cherchons les 2 éléments de vue.</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>Et nous configurerons nos <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> existants pour qu'ils ne répondent qu'au premier
  144. élément de vue.</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>Créons une deuxième <a href="/docs/#api/en/cameras/PerspectiveCamera"><code class="notranslate" translate="no">PerspectiveCamera</code></a> et un deuxième <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>.
  149. Le deuxième <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> est lié à la deuxième caméra et reçoit l'entrée
  150. du deuxième élément de vue.</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>Enfin, nous devons rendre la scène du point de vue de chaque
  164. caméra en utilisant la fonction scissor pour ne rendre qu'une partie du canvas.</p>
  165. <p>Voici une fonction qui, étant donné un élément, calculera le rectangle
  166. de cet élément qui chevauche le canvas. Elle définira ensuite le scissor
  167. et le viewport sur ce rectangle et renverra l'aspect pour cette taille.</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. // calculer un rectangle relatif au canvas
  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. // configurer le scissor pour ne rendre que cette partie du canvas
  179. const positiveYUpBottom = canvasRect.height - bottom;
  180. renderer.setScissor(left, positiveYUpBottom, width, height);
  181. renderer.setViewport(left, positiveYUpBottom, width, height);
  182. // retourner l'aspect
  183. return width / height;
  184. }
  185. </pre>
  186. <p>Et maintenant, nous pouvons utiliser cette fonction pour dessiner la scène deux fois dans notre fonction <code class="notranslate" translate="no">render</code>.</p>
  187. <pre class="prettyprint showlinemods notranslate lang-js" translate="no"> function render() {
  188. - if (resizeRendererToDisplaySize(renderer)) {
  189. - const canvas = renderer.domElement;
  190. - camera.aspect = canvas.clientWidth / canvas.clientHeight;
  191. - camera.updateProjectionMatrix();
  192. - }
  193. + resizeRendererToDisplaySize(renderer);
  194. +
  195. + // activer le scissor
  196. + renderer.setScissorTest(true);
  197. +
  198. + // rendre la vue originale
  199. + {
  200. + const aspect = setScissorForElement(view1Elem);
  201. +
  202. + // ajuster la caméra pour cet aspect
  203. + camera.aspect = aspect;
  204. + camera.updateProjectionMatrix();
  205. + cameraHelper.update();
  206. +
  207. + // ne pas dessiner l'helper de caméra dans la vue originale
  208. + cameraHelper.visible = false;
  209. +
  210. + scene.background.set(0x000000);
  211. +
  212. + // rendre
  213. + renderer.render(scene, camera);
  214. + }
  215. +
  216. + // rendre depuis la 2ème caméra
  217. + {
  218. + const aspect = setScissorForElement(view2Elem);
  219. +
  220. + // ajuster la caméra pour cet aspect
  221. + camera2.aspect = aspect;
  222. + camera2.updateProjectionMatrix();
  223. +
  224. + // dessiner l'helper de caméra dans la 2ème vue
  225. + cameraHelper.visible = true;
  226. +
  227. + scene.background.set(0x000040);
  228. +
  229. + renderer.render(scene, camera2);
  230. + }
  231. - renderer.render(scene, camera);
  232. requestAnimationFrame(render);
  233. }
  234. requestAnimationFrame(render);
  235. }
  236. </pre>
  237. <p>Le code ci-dessus définit la couleur de fond de la scène lors du rendu de la
  238. deuxième vue sur bleu foncé juste pour faciliter la distinction entre les deux vues.</p>
  239. <p>Nous pouvons également supprimer notre code <code class="notranslate" translate="no">updateCamera</code> puisque nous mettons tout à jour
  240. dans la fonction <code class="notranslate" translate="no">render</code>.</p>
  241. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-function updateCamera() {
  242. - camera.updateProjectionMatrix();
  243. -}
  244. const gui = new GUI();
  245. -gui.add(camera, 'fov', 1, 180).onChange(updateCamera);
  246. +gui.add(camera, 'fov', 1, 180);
  247. const minMaxGUIHelper = new MinMaxGUIHelper(camera, 'near', 'far', 0.1);
  248. -gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near').onChange(updateCamera);
  249. -gui.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far').onChange(updateCamera);
  250. +gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near');
  251. +gui.add(minMaxGUIHelper, 'max', 0.1, 50, 0.1).name('far');
  252. </pre>
  253. <p>Et maintenant, vous pouvez utiliser une vue pour voir le frustum de l'autre.</p>
  254. <p></p><div translate="no" class="threejs_example_container notranslate">
  255. <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>
  256. <a class="threejs_center" href="/manual/examples/cameras-perspective-2-scenes.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  257. </div>
  258. <p></p>
  259. <p>À gauche, vous pouvez voir la vue originale et à droite, vous pouvez
  260. voir une vue montrant le frustum de la caméra de gauche. En ajustant
  261. <code class="notranslate" translate="no">near</code>, <code class="notranslate" translate="no">far</code>, <code class="notranslate" translate="no">fov</code> et en déplaçant la caméra avec la souris, vous pouvez voir que
  262. seul ce qui se trouve à l'intérieur du frustum affiché à droite apparaît dans la scène de
  263. gauche.</p>
  264. <p>Ajustez <code class="notranslate" translate="no">near</code> jusqu'à environ 20 et vous verrez facilement l'avant des objets
  265. disparaître car ils ne sont plus dans le frustum. Ajustez <code class="notranslate" translate="no">far</code> en dessous d'environ 35
  266. et vous commencerez à voir le plan au sol disparaître car il n'est plus dans
  267. le frustum.</p>
  268. <p>Cela soulève la question : pourquoi ne pas simplement régler <code class="notranslate" translate="no">near</code> sur 0.0000000001 et <code class="notranslate" translate="no">far</code>
  269. sur 10000000000000 ou quelque chose de similaire afin de tout voir ?
  270. La raison est que votre GPU n'a qu'une précision limitée pour décider si quelque chose
  271. est devant ou derrière autre chose. Cette précision est répartie entre
  272. <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code>. Pire encore, par défaut, la précision près de la caméra est détaillée
  273. et la précision loin de la caméra est grossière. Les unités commencent à <code class="notranslate" translate="no">near</code>
  274. et s'étendent lentement à mesure qu'elles s'approchent de <code class="notranslate" translate="no">far</code>.</p>
  275. <p>En partant de l'exemple du haut, changeons le code pour insérer 20 sphères d'affilée.</p>
  276. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  277. const sphereRadius = 3;
  278. const sphereWidthDivisions = 32;
  279. const sphereHeightDivisions = 16;
  280. const sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);
  281. const numSpheres = 20;
  282. for (let i = 0; i &lt; numSpheres; ++i) {
  283. const sphereMat = new THREE.MeshPhongMaterial();
  284. sphereMat.color.setHSL(i * .73, 1, 0.5);
  285. const mesh = new THREE.Mesh(sphereGeo, sphereMat);
  286. mesh.position.set(-sphereRadius - 1, sphereRadius + 2, i * sphereRadius * -2.2);
  287. scene.add(mesh);
  288. }
  289. }
  290. </pre>
  291. <p>et réglons <code class="notranslate" translate="no">near</code> sur 0.00001</p>
  292. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const fov = 45;
  293. const aspect = 2; // the canvas default
  294. -const near = 0.1;
  295. +const near = 0.00001;
  296. const far = 100;
  297. const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  298. </pre>
  299. <p>Nous devons également ajuster un peu le code de l'interface graphique pour permettre 0.00001 si la valeur est éditée.</p>
  300. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-gui.add(minMaxGUIHelper, 'min', 0.1, 50, 0.1).name('near').onChange(updateCamera);
  301. +gui.add(minMaxGUIHelper, 'min', 0.00001, 50, 0.00001).name('near').onChange(updateCamera);
  302. </pre>
  303. <p>Que pensez-vous qu'il va se passer ?</p>
  304. <p></p><div translate="no" class="threejs_example_container notranslate">
  305. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/cameras-z-fighting.html"></iframe></div>
  306. <a class="threejs_center" href="/manual/examples/cameras-z-fighting.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  307. </div>
  308. <p></p>
  309. <p>C'est un exemple de <em>z-fighting</em> (chevauchement en Z) où le GPU de votre ordinateur n'a pas
  310. assez de précision pour décider quels pixels sont devant et quels pixels sont derrière.</p>
  311. <p>Juste au cas où le problème n'apparaîtrait pas sur votre machine, voici ce que je vois sur la mienne</p>
  312. <div class="threejs_center"><img src="../resources/images/z-fighting.png" style="width: 570px;"></div>
  313. <p>Une solution consiste à indiquer à three.js d'utiliser une méthode différente pour calculer quels
  314. pixels sont devant et quels pixels sont derrière. Nous pouvons le faire en activant
  315. <code class="notranslate" translate="no">logarithmicDepthBuffer</code> lorsque nous créons le <a href="/docs/#api/en/renderers/WebGLRenderer"><code class="notranslate" translate="no">WebGLRenderer</code></a></p>
  316. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">-const renderer = new THREE.WebGLRenderer({antialias: true, canvas});
  317. +const renderer = new THREE.WebGLRenderer({
  318. + antialias: true,
  319. + canvas,
  320. + logarithmicDepthBuffer: true,
  321. +});
  322. </pre>
  323. <p>et avec cela, cela pourrait fonctionner</p>
  324. <p></p><div translate="no" class="threejs_example_container notranslate">
  325. <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>
  326. <a class="threejs_center" href="/manual/examples/cameras-logarithmic-depth-buffer.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  327. </div>
  328. <p></p>
  329. <p>Si cela n'a pas résolu le problème pour vous, alors vous avez rencontré l'une des raisons pour lesquelles
  330. vous ne pouvez pas toujours utiliser cette solution. Cette raison est que seuls certains GPU
  331. la supportent. En septembre 2018, presque aucun appareil mobile ne supportait cette
  332. solution, alors que la plupart des ordinateurs de bureau le faisaient.</p>
  333. <p>Une autre raison de ne pas choisir cette solution est qu'elle peut être significativement plus lente
  334. que la solution standard.</p>
  335. <p>Même avec cette solution, la résolution reste limitée. Rendez <code class="notranslate" translate="no">near</code> encore
  336. plus petit ou <code class="notranslate" translate="no">far</code> encore plus grand et vous rencontrerez finalement les mêmes problèmes.</p>
  337. <p>Ce que cela signifie, c'est que vous devriez toujours faire un effort pour choisir un paramètre <code class="notranslate" translate="no">near</code>
  338. et <code class="notranslate" translate="no">far</code> qui convient à votre cas d'utilisation. Réglez <code class="notranslate" translate="no">near</code> aussi loin de la caméra
  339. que possible sans que les objets ne disparaissent. Réglez <code class="notranslate" translate="no">far</code> aussi près de la caméra
  340. que possible sans que les objets ne disparaissent. Si vous essayez de dessiner une scène géante
  341. et de montrer un gros plan du visage de quelqu'un afin que vous puissiez voir ses cils
  342. tout en voyant à l'arrière-plan des montagnes à 50 kilomètres
  343. de distance, eh bien, vous devrez alors trouver d'autres solutions créatives que
  344. nous aborderons peut-être plus tard. Pour l'instant, sachez simplement que vous devez faire attention
  345. à choisir des valeurs <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code> appropriées à vos besoins.</p>
  346. <p>La deuxième caméra la plus courante est l'<a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>. Au lieu
  347. de spécifier un frustum, elle spécifie une boîte avec les paramètres <code class="notranslate" translate="no">left</code>, <code class="notranslate" translate="no">right</code>
  348. <code class="notranslate" translate="no">top</code>, <code class="notranslate" translate="no">bottom</code>, <code class="notranslate" translate="no">near</code> et <code class="notranslate" translate="no">far</code>. Comme elle projette une boîte,
  349. il n'y a pas de perspective.</p>
  350. <p>Changeons l'exemple à 2 vues ci-dessus pour utiliser une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a>
  351. dans la première vue.</p>
  352. <p>Tout d'abord, configurons une <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>Nous réglons <code class="notranslate" translate="no">left</code> et <code class="notranslate" translate="no">bottom</code> à -1 et <code class="notranslate" translate="no">right</code> et <code class="notranslate" translate="no">top</code> à 1. Cela créerait
  363. une boîte de 2 unités de large et 2 unités de haut, mais nous allons ajuster <code class="notranslate" translate="no">left</code> et <code class="notranslate" translate="no">top</code>
  364. en fonction de l'aspect du rectangle dans lequel nous dessinons. Nous utiliserons la propriété
  365. <code class="notranslate" translate="no">zoom</code> pour faciliter l'ajustement du nombre d'unités réellement affichées par la caméra.</p>
  366. <p>Ajoutons un paramètre GUI pour <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>L'appel à <code class="notranslate" translate="no">listen</code> indique à lil-gui de surveiller les changements. Ceci est ici car
  371. les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a> peuvent également contrôler le zoom. Par exemple, la molette de la souris effectuera
  372. un zoom via les <a href="/docs/#examples/controls/OrbitControls"><code class="notranslate" translate="no">OrbitControls</code></a>.</p>
  373. <p>Enfin, il nous suffit de modifier la partie qui rend le côté
  374. gauche pour mettre à jour l'<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. // mettre à jour la caméra pour cet aspect
  378. - camera.aspect = aspect;
  379. + camera.left = -aspect;
  380. + camera.right = aspect;
  381. camera.updateProjectionMatrix();
  382. cameraHelper.update();
  383. // ne pas dessiner l'helper de caméra dans la vue originale
  384. cameraHelper.visible = false;
  385. scene.background.set(0x000000);
  386. renderer.render(scene, camera);
  387. }
  388. </pre>
  389. <p>et maintenant vous pouvez voir une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> en action.</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">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  393. </div>
  394. <p></p>
  395. <p>Une autre utilisation courante pour une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> est de dessiner les
  396. vues du dessus, du dessous, de gauche, de droite, de face, d'arrière d'un programme de modélisation
  397. 3D ou de l'éditeur d'un moteur de jeu.</p>
  398. <div class="threejs_center"><img src="../resources/images/quad-viewport.png" style="width: 574px;"></div>
  399. <p>Dans la capture d'écran ci-dessus, vous pouvez voir qu'une vue est une vue en perspective et 3 vues sont
  400. des vues orthographiques.</p>
  401. <p>Une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> est le plus souvent utilisée si vous utilisez three.js
  402. pour dessiner des éléments 2D. Vous décidez combien d'unités vous voulez que la caméra
  403. affiche. Par exemple, si vous voulez qu'un pixel du canvas corresponde
  404. à une unité dans la caméra, vous pourriez faire quelque chose comme</p>
  405. <p>Pour placer l'origine au centre et avoir 1 pixel = 1 unité three.js, quelque chose comme</p>
  406. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">camera.left = -canvas.width / 2;
  407. camera.right = canvas.width / 2;
  408. camera.top = canvas.height / 2;
  409. camera.bottom = -canvas.height / 2;
  410. camera.near = -1;
  411. camera.far = 1;
  412. camera.zoom = 1;
  413. </pre>
  414. <p>Ou si nous voulions que l'origine soit en haut à gauche, comme sur un
  415. canvas 2D, nous pourrions utiliser ceci</p>
  416. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">camera.left = 0;
  417. camera.right = canvas.width;
  418. camera.top = 0;
  419. camera.bottom = canvas.height;
  420. camera.near = -1;
  421. camera.far = 1;
  422. camera.zoom = 1;
  423. </pre>
  424. <p>Dans ce cas, le coin supérieur gauche serait 0,0, comme sur un canvas 2D.</p>
  425. <p>Essayons ! Tout d'abord, configurons la caméra.</p>
  426. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const left = 0;
  427. const right = 300; // taille par défaut du canvas
  428. const top = 0;
  429. const bottom = 150; // taille par défaut du canvas
  430. const near = -1;
  431. const far = 1;
  432. const camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);
  433. camera.zoom = 1;
  434. </pre>
  435. <p>Puis chargeons 6 textures et créons 6 plans, un pour chaque texture.
  436. Nous associerons chaque plan à un <a href="/docs/#api/en/core/Object3D"><code class="notranslate" translate="no">THREE.Object3D</code></a> pour faciliter le décalage
  437. du plan afin que son centre apparaisse à son coin supérieur gauche.</p>
  438. <p>Si vous l'exécutez localement, vous devrez également avoir effectué la <a href="setup.html">configuration</a>.
  439. Vous pourriez également vouloir lire l'article sur l'<a href="textures.html">utilisation des textures</a>.</p>
  440. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const loader = new THREE.TextureLoader();
  441. const textures = [
  442. loader.load('resources/images/flower-1.jpg'),
  443. loader.load('resources/images/flower-2.jpg'),
  444. loader.load('resources/images/flower-3.jpg'),
  445. loader.load('resources/images/flower-4.jpg'),
  446. loader.load('resources/images/flower-5.jpg'),
  447. loader.load('resources/images/flower-6.jpg'),
  448. ];
  449. const planeSize = 256;
  450. const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);
  451. const planes = textures.map((texture) =&gt; {
  452. const planePivot = new THREE.Object3D();
  453. scene.add(planePivot);
  454. texture.magFilter = THREE.NearestFilter;
  455. const planeMat = new THREE.MeshBasicMaterial({
  456. map: texture,
  457. side: THREE.DoubleSide,
  458. });
  459. const mesh = new THREE.Mesh(planeGeo, planeMat);
  460. planePivot.add(mesh);
  461. // déplacer le plan pour que le coin supérieur gauche soit l'origine
  462. mesh.position.set(planeSize / 2, planeSize / 2, 0);
  463. return planePivot;
  464. });
  465. </pre>
  466. <p>et nous devons mettre à jour la caméra si la taille du canvas
  467. change.</p>
  468. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render() {
  469. if (resizeRendererToDisplaySize(renderer)) {
  470. camera.right = canvas.width;
  471. camera.bottom = canvas.height;
  472. camera.updateProjectionMatrix();
  473. }
  474. ...
  475. </pre>
  476. <p><code class="notranslate" translate="no">planes</code> est un tableau de <a href="/docs/#api/en/objects/Mesh"><code class="notranslate" translate="no">THREE.Mesh</code></a>, un pour chaque plan.
  477. Déplaçons-les en fonction du temps.</p>
  478. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">function render(time) {
  479. time *= 0.001; // convertir en secondes ;
  480. ...
  481. const distAcross = Math.max(20, canvas.width - planeSize);
  482. const distDown = Math.max(20, canvas.height - planeSize);
  483. // distance totale pour se déplacer en travers et en arrière
  484. const xRange = distAcross * 2;
  485. const yRange = distDown * 2;
  486. const speed = 180;
  487. planes.forEach((plane, ndx) =&gt; {
  488. // calculer un temps unique pour chaque plan
  489. const t = time * speed + ndx * 300;
  490. // obtenir une valeur entre 0 et la plage
  491. const xt = t % xRange;
  492. const yt = t % yRange;
  493. // définir notre position en avant si 0 à la moitié de la plage
  494. // et en arrière si la moitié de la plage à la plage
  495. const x = xt &lt; distAcross ? xt : xRange - xt;
  496. const y = yt &lt; distDown ? yt : yRange - yt;
  497. plane.position.set(x, y, 0);
  498. });
  499. renderer.render(scene, camera);
  500. </pre>
  501. <p>Et vous pouvez voir les images rebondir parfaitement au pixel près sur les bords du
  502. canvas en utilisant des calculs de pixels, tout comme un canvas 2D.</p>
  503. <p></p><div translate="no" class="threejs_example_container notranslate">
  504. <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>
  505. <a class="threejs_center" href="/manual/examples/cameras-orthographic-canvas-top-left-origin.html" target="_blank">cliquez ici pour ouvrir dans une fenêtre séparée</a>
  506. </div>
  507. <p></p>
  508. <p>Une autre utilisation courante pour une <a href="/docs/#api/en/cameras/OrthographicCamera"><code class="notranslate" translate="no">OrthographicCamera</code></a> est de dessiner les
  509. vues du dessus, du dessous, de gauche, de droite, de face, d'arrière d'un programme de modélisation
  510. 3D ou de l'éditeur d'un moteur de jeu.</p>
  511. <div class="threejs_center"><img src="../resources/images/quad-viewport.png" style="width: 574px;"></div>
  512. <p>Dans la capture d'écran ci-dessus, vous pouvez voir qu'une vue est une vue en perspective et 3 vues sont
  513. des vues orthographiques.</p>
  514. <p>Voilà les bases des caméras. Nous aborderons quelques méthodes courantes pour déplacer les caméras
  515. dans d'autres articles. Pour l'instant, passons aux <a href="shadows.html">ombres</a>.</p>
  516. <p><canvas id="c"></canvas></p>
  517. <script type="module" src="../resources/threejs-cameras.js"></script>
  518. </div>
  519. </div>
  520. </div>
  521. <script src="../resources/prettify.js"></script>
  522. <script src="../resources/lesson.js"></script>
  523. </body></html>
粤ICP备19079148号