fog.html 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <!DOCTYPE html><html lang="zh"><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. <!-- Import maps polyfill -->
  14. <!-- Remove this when import maps will be widely supported -->
  15. <script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
  16. <script type="importmap">
  17. {
  18. "imports": {
  19. "three": "../../build/three.module.js"
  20. }
  21. }
  22. </script>
  23. <link rel="stylesheet" href="/manual/zh/lang.css">
  24. </head>
  25. <body>
  26. <div class="container">
  27. <div class="lesson-title">
  28. <h1>雾</h1>
  29. </div>
  30. <div class="lesson">
  31. <div class="lesson-main">
  32. <p>本文是three.js系列文章的一部分。第一篇文章是<a href="fundamentals.html">three.js 基础</a>。如果你是个新手,还没读过,请从那里开始。如果你还没读过有关摄像机的章节,请从<a href="cameras.html">这篇文章</a>开始。</p>
  33. <p>在3D引擎里,雾通常是基于离摄像机的距离褪色至某种特定颜色的方式。在three.js中添加雾是通过创建 <a href="/docs/#api/zh/scenes/Fog"><code class="notranslate" translate="no">Fog</code></a> 或者 <a href="/docs/#api/zh/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> 实例并设定scene的<a href="/docs/#api/zh/scenes/Scene#fog"><code class="notranslate" translate="no">fog</code></a> 属性。</p>
  34. <p><a href="/docs/#api/zh/scenes/Fog"><code class="notranslate" translate="no">Fog</code></a> 让你设定 <code class="notranslate" translate="no">near</code> 和 <code class="notranslate" translate="no">far</code> 属性,代表距离摄像机的距离。任何物体比 <code class="notranslate" translate="no">near</code> 近不会受到影响,任何物体比 <code class="notranslate" translate="no">far</code> 远则完全是雾的颜色。在 <code class="notranslate" translate="no">near</code> 和 <code class="notranslate" translate="no">far</code> 中间的物体,会从它们自身材料的颜色褪色到雾的颜色。</p>
  35. <p><a href="/docs/#api/zh/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> 会根据离摄像机的距离呈指数增长。</p>
  36. <p>选择其中一个类型,创建雾并设定到场景中如下:</p>
  37. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
  38. {
  39. const color = 0xFFFFFF; // white
  40. const near = 10;
  41. const far = 100;
  42. scene.fog = new THREE.Fog(color, near, far);
  43. }
  44. </pre>
  45. <p>或者对于 <a href="/docs/#api/zh/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> 会是:</p>
  46. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
  47. {
  48. const color = 0xFFFFFF;
  49. const density = 0.1;
  50. scene.fog = new THREE.FogExp2(color, density);
  51. }
  52. </pre>
  53. <p><a href="/docs/#api/zh/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> 比较接近现实效果,但是 <a href="/docs/#api/zh/scenes/Fog"><code class="notranslate" translate="no">Fog</code></a> 使用的更加普遍,因为它支持设定影响区域,所以你可以设定一定距离内显示清晰的场景,过了这段距离再褪色到某种颜色。</p>
  54. <div class="spread">
  55. <div>
  56. <div data-diagram="fog" style="height: 300px;"></div>
  57. <div class="code">THREE.Fog</div>
  58. </div>
  59. <div>
  60. <div data-diagram="fogExp2" style="height: 300px;"></div>
  61. <div class="code">THREE.FogExp2</div>
  62. </div>
  63. </div>
  64. <p>需要注意的是雾是作用在 <em>渲染的物体</em> 上的,是物体颜色中每个像素计算的一部分。这意味着如果你想让你的场景褪色到某种颜色,你需要设定雾 <strong>和</strong> 场景的背景颜色为同一种颜色。背景颜色通过<a href="/docs/#api/zh/scenes/Scene#background"><code class="notranslate" translate="no">scene.background</code></a>属性设置。你可以通过 <a href="/docs/#api/zh/math/Color"><code class="notranslate" translate="no">THREE.Color</code></a> 选择背景颜色设置。例如:</p>
  65. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">scene.background = new THREE.Color('#F00'); // red
  66. </pre>
  67. <div class="spread">
  68. <div>
  69. <div data-diagram="fogBlueBackgroundRed" style="height: 300px;" class="border"></div>
  70. <div class="code">fog blue, background red</div>
  71. </div>
  72. <div>
  73. <div data-diagram="fogBlueBackgroundBlue" style="height: 300px;" class="border"></div>
  74. <div class="code">fog blue, background blue</div>
  75. </div>
  76. </div>
  77. <p>这是我们之前添加雾的例子。唯一的改动是在添加雾之后,我们设置了场景的背景颜色。</p>
  78. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
  79. +{
  80. + const near = 1;
  81. + const far = 2;
  82. + const color = 'lightblue';
  83. + scene.fog = new THREE.Fog(color, near, far);
  84. + scene.background = new THREE.Color(color);
  85. +}
  86. </pre>
  87. <p>在下面的例子,摄像机的 <code class="notranslate" translate="no">near</code> 是0.1, <code class="notranslate" translate="no">far</code> 是5,位于 <code class="notranslate" translate="no">z = 2</code>的位置。方块为单位大小,位于Z=0的位置。这意味着将雾设置为 <code class="notranslate" translate="no">near = 1</code> 和 <code class="notranslate" translate="no">far = 2</code> ,方块会在它的中间位置淡出。</p>
  88. <p></p><div translate="no" class="threejs_example_container notranslate">
  89. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/fog.html"></iframe></div>
  90. <a class="threejs_center" href="/manual/examples/fog.html" target="_blank">点击此处在新标签页中打开</a>
  91. </div>
  92. <p></p>
  93. <p>让我们添加界面来调整雾。我们将再次使用<a href="https://github.com/georgealways/lil-gui">lil-gui</a>。lil-gui接收对象和属性参数,并自动为其创建界面。我们能够简单地操纵雾的 <code class="notranslate" translate="no">near</code> 和 <code class="notranslate" translate="no">far</code> 属性,但是 <code class="notranslate" translate="no">near</code> 数值大于 <code class="notranslate" translate="no">far</code> 是无效的,所以我们创建助手(helper)来确保 <code class="notranslate" translate="no">near</code> 和 <code class="notranslate" translate="no">far</code> 属性,让 <code class="notranslate" translate="no">near</code> 小于或等于 <code class="notranslate" translate="no">far</code> , <code class="notranslate" translate="no">far</code> 大于或等于 <code class="notranslate" translate="no">near</code>。</p>
  94. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">// We use this class to pass to lil-gui
  95. // so when it manipulates near or far
  96. // near is never &gt; far and far is never &lt; near
  97. class FogGUIHelper {
  98. constructor(fog) {
  99. this.fog = fog;
  100. }
  101. get near() {
  102. return this.fog.near;
  103. }
  104. set near(v) {
  105. this.fog.near = v;
  106. this.fog.far = Math.max(this.fog.far, v);
  107. }
  108. get far() {
  109. return this.fog.far;
  110. }
  111. set far(v) {
  112. this.fog.far = v;
  113. this.fog.near = Math.min(this.fog.near, v);
  114. }
  115. }
  116. </pre>
  117. <p>之后我们可以像这样添加</p>
  118. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  119. const near = 1;
  120. const far = 2;
  121. const color = 'lightblue';
  122. scene.fog = new THREE.Fog(color, near, far);
  123. scene.background = new THREE.Color(color);
  124. +
  125. + const fogGUIHelper = new FogGUIHelper(scene.fog);
  126. + gui.add(fogGUIHelper, 'near', near, far).listen();
  127. + gui.add(fogGUIHelper, 'far', near, far).listen();
  128. }
  129. </pre>
  130. <p>当我们设置摄像机的时候,设置(助手的 <code class="notranslate" translate="no">near</code> 和 <code class="notranslate" translate="no">far</code> 以调节雾的最小值和最大值。</p>
  131. <p>最后两行调用 <code class="notranslate" translate="no">.listen()</code> 告诉lil-gui <em>监听</em> 变化。当我们编辑 <code class="notranslate" translate="no">far</code> 改变了 <code class="notranslate" translate="no">near</code> 或者编辑 <code class="notranslate" translate="no">near</code> 改变了 <code class="notranslate" translate="no">far</code> ,lil-gui将会为我们更新其他属性的UI。</p>
  132. <p>或许能够改变雾的颜色是个不错的主意,但是如上面提到的,我们需要保持雾的颜色和背景颜色一致。所以,让我们在助手上添加另一个 <em>虚拟</em> 属性,当lil-gui改变它时会设置这两个颜色。</p>
  133. <p>lil-gui能够通过4种方式设置颜色。分别是6位hex字符串 (如: <code class="notranslate" translate="no">#112233</code>),色相、饱和度、明度的对象 (如: <code class="notranslate" translate="no">{h: 60, s: 1, v: }</code>),RGB数组 (如: <code class="notranslate" translate="no">[255, 128, 64]</code>),或者RGBA数组 (如: <code class="notranslate" translate="no">[127, 200, 75, 0.3]</code>)。</p>
  134. <p>对于我们的目的而言,最简单的是用hex字符串的方式,因为lil-gui只修改单个数值。幸运的是通过 <a href="/docs/#api/zh/math/Color"><code class="notranslate" translate="no">THREE.Color</code></a> 的 <a href="/docs/#api/zh/math/Color#getHexString"><code class="notranslate" translate="no">getHexString</code></a> 方法我们能轻松地获得这个字符串,只需要在其前面添加 '#' 。</p>
  135. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">// We use this class to pass to lil-gui
  136. // so when it manipulates near or far
  137. // near is never &gt; far and far is never &lt; near
  138. +// Also when lil-gui manipulates color we'll
  139. +// update both the fog and background colors.
  140. class FogGUIHelper {
  141. * constructor(fog, backgroundColor) {
  142. this.fog = fog;
  143. + this.backgroundColor = backgroundColor;
  144. }
  145. get near() {
  146. return this.fog.near;
  147. }
  148. set near(v) {
  149. this.fog.near = v;
  150. this.fog.far = Math.max(this.fog.far, v);
  151. }
  152. get far() {
  153. return this.fog.far;
  154. }
  155. set far(v) {
  156. this.fog.far = v;
  157. this.fog.near = Math.min(this.fog.near, v);
  158. }
  159. + get color() {
  160. + return `#${this.fog.color.getHexString()}`;
  161. + }
  162. + set color(hexString) {
  163. + this.fog.color.set(hexString);
  164. + this.backgroundColor.set(hexString);
  165. + }
  166. }
  167. </pre>
  168. <p>然后我们调用 <code class="notranslate" translate="no">gui.addColor</code> 来为我们的助手虚拟属性添加颜色界面。</p>
  169. <pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
  170. const near = 1;
  171. const far = 2;
  172. const color = 'lightblue';
  173. scene.fog = new THREE.Fog(color, near, far);
  174. scene.background = new THREE.Color(color);
  175. * const fogGUIHelper = new FogGUIHelper(scene.fog, scene.background);
  176. gui.add(fogGUIHelper, 'near', near, far).listen();
  177. gui.add(fogGUIHelper, 'far', near, far).listen();
  178. + gui.addColor(fogGUIHelper, 'color');
  179. }
  180. </pre>
  181. <p></p><div translate="no" class="threejs_example_container notranslate">
  182. <div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/fog-gui.html"></iframe></div>
  183. <a class="threejs_center" href="/manual/examples/fog-gui.html" target="_blank">点击此处在新标签页中打开</a>
  184. </div>
  185. <p></p>
  186. <p>你可以观察到,设置 <code class="notranslate" translate="no">near</code> 如1.9, <code class="notranslate" translate="no">far</code> 为2.0能在未雾化和完全雾化之间获得锐利的变化效果,而设置 <code class="notranslate" translate="no">near</code> = 1.1, <code class="notranslate" translate="no">far</code> = 2.9 会让我们旋转的方块在距离摄像机2个单位距离的位置获得最平滑的变化效果。</p>
  187. <p>最后, <a href="/docs/#api/zh/materials/Material#fog"><code class="notranslate" translate="no">fog</code></a> 在材料上有个布尔属性,用来设置渲染物体的材料是否会受到雾的影响。对于大多数材料而言默认设置为 <code class="notranslate" translate="no">true</code> ,作为你可能想关掉雾生效的例子,设想下你正在制作一个3D车辆模拟器并处于驾驶员座位或座舱的视角,你很可能为了看清车内的物体将它们的是否受雾影响属性关闭。</p>
  188. <p>一个更好的例子会是一个外面弥漫浓雾的房子。让我们假设将雾设置在2米外 (near = 2) 并且在4米的地方完全进入雾中 (far = 4)。房间大于2米并且很可能大于4米,那么你需要将房子内的材质设置为不受雾的影响,否则当站在房子内尽头往墙壁外看会觉得房子是在雾里。</p>
  189. <div class="spread">
  190. <div>
  191. <div data-diagram="fogHouseAll" style="height: 300px;" class="border"></div>
  192. <div class="code">fog: true, all</div>
  193. </div>
  194. </div>
  195. <p>注意房间尽头的墙壁和天花板正受到雾的影响,当我们把房子材料上的是否受雾影响属性关闭可以解决这个问题。</p>
  196. <div class="spread">
  197. <div>
  198. <div data-diagram="fogHouseInsideNoFog" style="height: 300px;" class="border"></div>
  199. <div class="code">fog: true, only outside materials</div>
  200. </div>
  201. </div>
  202. <p><canvas id="c"></canvas></p>
  203. <script type="module" src="../resources/threejs-fog.js"></script>
  204. </div>
  205. </div>
  206. </div>
  207. <script src="../resources/prettify.js"></script>
  208. <script src="../resources/lesson.js"></script>
  209. </body></html>