You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

221 lines
14 KiB
HTML

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html><html lang="zh"><head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@threejs">
<meta name="twitter:title" content="Three.js 雾">
<meta property="og:image" content="https://threejs.org/files/share.png">
<link rel="shortcut icon" href="../../files/favicon_white.ico" media="(prefers-color-scheme: dark)">
<link rel="shortcut icon" href="../../files/favicon.ico" media="(prefers-color-scheme: light)">
<link rel="stylesheet" href="../resources/lesson.css">
<link rel="stylesheet" href="../resources/lang.css">
<script type="importmap">
{
"imports": {
"three": "../../build/three.module.js"
}
}
</script>
<link rel="stylesheet" href="/manual/zh/lang.css">
</head>
<body>
<div class="container">
<div class="lesson-title">
<h1></h1>
</div>
<div class="lesson">
<div class="lesson-main">
<p>本文是three.js系列文章的一部分。第一篇文章是<a href="fundamentals.html">three.js 基础</a>。如果你是个新手,还没读过,请从那里开始。如果你还没读过有关摄像机的章节,请从<a href="cameras.html">这篇文章</a>开始。</p>
<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>
<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>
<p><a href="/docs/#api/zh/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> 会根据离摄像机的距离呈指数增长。</p>
<p>选择其中一个类型,创建雾并设定到场景中如下:</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
{
const color = 0xFFFFFF; // white
const near = 10;
const far = 100;
scene.fog = new THREE.Fog(color, near, far);
}
</pre>
<p>或者对于 <a href="/docs/#api/zh/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> 会是:</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
{
const color = 0xFFFFFF;
const density = 0.1;
scene.fog = new THREE.FogExp2(color, density);
}
</pre>
<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>
<div class="spread">
<div>
<div data-diagram="fog" style="height: 300px;"></div>
<div class="code">THREE.Fog</div>
</div>
<div>
<div data-diagram="fogExp2" style="height: 300px;"></div>
<div class="code">THREE.FogExp2</div>
</div>
</div>
<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>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">scene.background = new THREE.Color('#F00'); // red
</pre>
<div class="spread">
<div>
<div data-diagram="fogBlueBackgroundRed" style="height: 300px;" class="border"></div>
<div class="code">fog blue, background red</div>
</div>
<div>
<div data-diagram="fogBlueBackgroundBlue" style="height: 300px;" class="border"></div>
<div class="code">fog blue, background blue</div>
</div>
</div>
<p>这是我们之前添加雾的例子。唯一的改动是在添加雾之后,我们设置了场景的背景颜色。</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const scene = new THREE.Scene();
+{
+ const near = 1;
+ const far = 2;
+ const color = 'lightblue';
+ scene.fog = new THREE.Fog(color, near, far);
+ scene.background = new THREE.Color(color);
+}
</pre>
<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>
<p></p><div translate="no" class="threejs_example_container notranslate">
<div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/fog.html"></iframe></div>
<a class="threejs_center" href="/manual/examples/fog.html" target="_blank">点击此处在新标签页中打开</a>
</div>
<p></p>
<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>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// We use this class to pass to lil-gui
// so when it manipulates near or far
// near is never &gt; far and far is never &lt; near
class FogGUIHelper {
constructor(fog) {
this.fog = fog;
}
get near() {
return this.fog.near;
}
set near(v) {
this.fog.near = v;
this.fog.far = Math.max(this.fog.far, v);
}
get far() {
return this.fog.far;
}
set far(v) {
this.fog.far = v;
this.fog.near = Math.min(this.fog.near, v);
}
}
</pre>
<p>之后我们可以像这样添加</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
const near = 1;
const far = 2;
const color = 'lightblue';
scene.fog = new THREE.Fog(color, near, far);
scene.background = new THREE.Color(color);
+
+ const fogGUIHelper = new FogGUIHelper(scene.fog);
+ gui.add(fogGUIHelper, 'near', near, far).listen();
+ gui.add(fogGUIHelper, 'far', near, far).listen();
}
</pre>
<p>当我们设置摄像机的时候,设置(助手的 <code class="notranslate" translate="no">near</code><code class="notranslate" translate="no">far</code> 以调节雾的最小值和最大值。</p>
<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>
<p>或许能够改变雾的颜色是个不错的主意,但是如上面提到的,我们需要保持雾的颜色和背景颜色一致。所以,让我们在助手上添加另一个 <em>虚拟</em> 属性当lil-gui改变它时会设置这两个颜色。</p>
<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>
<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>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">// We use this class to pass to lil-gui
// so when it manipulates near or far
// near is never &gt; far and far is never &lt; near
+// Also when lil-gui manipulates color we'll
+// update both the fog and background colors.
class FogGUIHelper {
* constructor(fog, backgroundColor) {
this.fog = fog;
+ this.backgroundColor = backgroundColor;
}
get near() {
return this.fog.near;
}
set near(v) {
this.fog.near = v;
this.fog.far = Math.max(this.fog.far, v);
}
get far() {
return this.fog.far;
}
set far(v) {
this.fog.far = v;
this.fog.near = Math.min(this.fog.near, v);
}
+ get color() {
+ return `#${this.fog.color.getHexString()}`;
+ }
+ set color(hexString) {
+ this.fog.color.set(hexString);
+ this.backgroundColor.set(hexString);
+ }
}
</pre>
<p>然后我们调用 <code class="notranslate" translate="no">gui.addColor</code> 来为我们的助手虚拟属性添加颜色界面。</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">{
const near = 1;
const far = 2;
const color = 'lightblue';
scene.fog = new THREE.Fog(color, near, far);
scene.background = new THREE.Color(color);
* const fogGUIHelper = new FogGUIHelper(scene.fog, scene.background);
gui.add(fogGUIHelper, 'near', near, far).listen();
gui.add(fogGUIHelper, 'far', near, far).listen();
+ gui.addColor(fogGUIHelper, 'color');
}
</pre>
<p></p><div translate="no" class="threejs_example_container notranslate">
<div><iframe class="threejs_example notranslate" translate="no" style=" " src="/manual/examples/resources/editor.html?url=/manual/examples/fog-gui.html"></iframe></div>
<a class="threejs_center" href="/manual/examples/fog-gui.html" target="_blank">点击此处在新标签页中打开</a>
</div>
<p></p>
<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>
<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>
<p>一个更好的例子会是一个外面弥漫浓雾的房子。让我们假设将雾设置在2米外 (near = 2) 并且在4米的地方完全进入雾中 (far = 4)。房间大于2米并且很可能大于4米那么你需要将房子内的材质设置为不受雾的影响否则当站在房子内尽头往墙壁外看会觉得房子是在雾里。</p>
<div class="spread">
<div>
<div data-diagram="fogHouseAll" style="height: 300px;" class="border"></div>
<div class="code">fog: true, all</div>
</div>
</div>
<p>注意房间尽头的墙壁和天花板正受到雾的影响,当我们把房子材料上的是否受雾影响属性关闭可以解决这个问题。</p>
<div class="spread">
<div>
<div data-diagram="fogHouseInsideNoFog" style="height: 300px;" class="border"></div>
<div class="code">fog: true, only outside materials</div>
</div>
</div>
<p><canvas id="c"></canvas></p>
<script type="module" src="../resources/threejs-fog.js"></script>
</div>
</div>
</div>
<script src="../resources/prettify.js"></script>
<script src="../resources/lesson.js"></script>
</body></html>