You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
285 lines
14 KiB
285 lines
14 KiB
<!DOCTYPE html><html lang="en"><head>
|
|
<meta charset="utf-8">
|
|
<title>Fog</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 – Fog">
|
|
<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="/manual/resources/lesson.css">
|
|
<link rel="stylesheet" href="/manual/resources/lang.css">
|
|
<!-- Import maps polyfill -->
|
|
<!-- Remove this when import maps will be widely supported -->
|
|
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
|
|
|
|
<script type="importmap">
|
|
{
|
|
"imports": {
|
|
"three": "../../build/three.module.js"
|
|
}
|
|
}
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="lesson-title">
|
|
<h1>Fog</h1>
|
|
</div>
|
|
<div class="lesson">
|
|
<div class="lesson-main">
|
|
<p>This article is part of a series of articles about three.js. The
|
|
first article is <a href="fundamentals.html">three.js fundamentals</a>. If
|
|
you haven't read that yet and you're new to three.js you might want to
|
|
consider starting there. If you haven't read about cameras you might
|
|
want to start with <a href="cameras.html">this article</a>.</p>
|
|
<p>Fog in a 3D engine is generally a way of fading to a specific color
|
|
based on the distance from the camera. In three.js you add fog by
|
|
creating <a href="/docs/#api/en/scenes/Fog"><code class="notranslate" translate="no">Fog</code></a> or <a href="/docs/#api/en/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> object and setting it on the scene's
|
|
<a href="/docs/#api/en/scenes/Scene#fog"><code class="notranslate" translate="no">fog</code></a> property.</p>
|
|
<p><a href="/docs/#api/en/scenes/Fog"><code class="notranslate" translate="no">Fog</code></a> lets you choose <code class="notranslate" translate="no">near</code> and <code class="notranslate" translate="no">far</code> settings which are distances
|
|
from the camera. Anything closer than <code class="notranslate" translate="no">near</code> is unaffected by fog.
|
|
Anything further than <code class="notranslate" translate="no">far</code> is completely the fog color. Parts between
|
|
<code class="notranslate" translate="no">near</code> and <code class="notranslate" translate="no">far</code> fade from their material color to the fog color.</p>
|
|
<p>There's also <a href="/docs/#api/en/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> which grows exponentially with distance from the camera.</p>
|
|
<p>To use either type of fog you create one and and assign it to the scene as in</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>or for <a href="/docs/#api/en/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> it would be</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/en/scenes/FogExp2"><code class="notranslate" translate="no">FogExp2</code></a> is closer to reality but <a href="/docs/#api/en/scenes/Fog"><code class="notranslate" translate="no">Fog</code></a> is used
|
|
more commonly since it lets you choose a place to apply
|
|
the fog so you can decide to show a clear scene
|
|
up to a certain distance and then fade out to some color
|
|
past that distance.</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>It's important to note that the fog is applied to <em>things that are rendered</em>.
|
|
It is part of the calculation of each pixel of the color of the object.
|
|
What that means is if you want your scene to fade to a certain color you
|
|
need to set the fog <strong>and</strong> the background color to the same color.
|
|
The background color is set using the
|
|
<a href="/docs/#api/en/scenes/Scene#background"><code class="notranslate" translate="no">scene.background</code></a>
|
|
property. To pick a background color you attach a <a href="/docs/#api/en/math/Color"><code class="notranslate" translate="no">THREE.Color</code></a> to it. For example</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>Here is one of our previous examples with fog added. The only addition
|
|
is right after setting up the scene we add the fog and set the scene's
|
|
background color</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>In the example below the camera's <code class="notranslate" translate="no">near</code> is 0.1 and its <code class="notranslate" translate="no">far</code> is 5.
|
|
The camera is at <code class="notranslate" translate="no">z = 2</code>. The cubes are 1 unit large and at Z = 0.
|
|
This means with a fog setting of <code class="notranslate" translate="no">near = 1</code> and <code class="notranslate" translate="no">far = 2</code> the cubes
|
|
will fade out right around their center.</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">click here to open in a separate window</a>
|
|
</div>
|
|
|
|
<p></p>
|
|
<p>Let's add an interface so we can adjust the fog. Again we'll use
|
|
<a href="https://github.com/georgealways/lil-gui">lil-gui</a>. lil-gui takes
|
|
an object and a property and automagically makes an interface
|
|
for that type of property. We could just simply let it manipulate
|
|
the fog's <code class="notranslate" translate="no">near</code> and <code class="notranslate" translate="no">far</code> properties but it's invalid to have
|
|
<code class="notranslate" translate="no">near</code> be greater than <code class="notranslate" translate="no">far</code> so let's make a helper so lil-gui
|
|
can manipulate a <code class="notranslate" translate="no">near</code> and <code class="notranslate" translate="no">far</code> property but we'll make sure <code class="notranslate" translate="no">near</code>
|
|
is less than or equal to <code class="notranslate" translate="no">far</code> and <code class="notranslate" translate="no">far</code> is greater than or equal <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 > far and far is never < 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>We can then add it like this</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>The <code class="notranslate" translate="no">near</code> and <code class="notranslate" translate="no">far</code> parameters set the minimum and maximum values
|
|
for adjusting the fog. They are set when we setup the camera.</p>
|
|
<p>The <code class="notranslate" translate="no">.listen()</code> at the end of the last 2 lines tells lil-gui to <em>listen</em>
|
|
for changes. That way when we change <code class="notranslate" translate="no">near</code> because of an edit to <code class="notranslate" translate="no">far</code>
|
|
or we change <code class="notranslate" translate="no">far</code> in response to an edit to <code class="notranslate" translate="no">near</code> lil-gui will update
|
|
the other property's UI for us.</p>
|
|
<p>It might also be nice to be able to change the fog color but like was
|
|
mentioned above we need to keep both the fog color and the background
|
|
color in sync. So, let's add another <em>virtual</em> property to our helper
|
|
that will set both colors when lil-gui manipulates it.</p>
|
|
<p>lil-gui can manipulate colors in 4 ways, as a CSS 6 digit hex string (eg: <code class="notranslate" translate="no">#112233</code>). As an hue, saturation, value, object (eg: <code class="notranslate" translate="no">{h: 60, s: 1, v: }</code>).
|
|
As an RGB array (eg: <code class="notranslate" translate="no">[255, 128, 64]</code>). Or, as an RGBA array (eg: <code class="notranslate" translate="no">[127, 200, 75, 0.3]</code>).</p>
|
|
<p>It's easiest for our purpose to use the hex string version since that way
|
|
lil-gui is only manipulating a single value. Fortunately <a href="/docs/#api/en/math/Color"><code class="notranslate" translate="no">THREE.Color</code></a>
|
|
as a <a href="/docs/#api/en/math/Color#getHexString"><code class="notranslate" translate="no">getHexString</code></a> method
|
|
we get use to easily get such a string, we just have to prepend a '#' to the front.</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 > far and far is never < 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>We then call <code class="notranslate" translate="no">gui.addColor</code> to add a color UI for our helper's virtual property.</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">click here to open in a separate window</a>
|
|
</div>
|
|
|
|
<p></p>
|
|
<p>You can see setting <code class="notranslate" translate="no">near</code> to like 1.9 and <code class="notranslate" translate="no">far</code> to 2.0 gives
|
|
a very sharp transition between un-fogged and completely fogged.
|
|
where as <code class="notranslate" translate="no">near</code> = 1.1 and <code class="notranslate" translate="no">far</code> = 2.9 should just about be
|
|
the smoothest given our cubes are spinning 2 units away from the camera.</p>
|
|
<p>One last thing, there is a boolean <a href="/docs/#api/en/materials/Material#fog"><code class="notranslate" translate="no">fog</code></a>
|
|
property on a material for whether or not objects rendered
|
|
with that material are affected by fog. It defaults to <code class="notranslate" translate="no">true</code>
|
|
for most materials. As an example of why you might want
|
|
to turn the fog off, imagine you're making a 3D vehicle
|
|
simulator with a view from the driver's seat or cockpit.
|
|
You probably want the fog off for everything inside the vehicle when
|
|
viewing from inside the vehicle.</p>
|
|
<p>A better example might be a house
|
|
and thick fog outside house. Let's say the fog is set to start
|
|
2 meters away (near = 2) and completely fogged out at 4 meters (far = 4).
|
|
Rooms are longer than 2 meters and the house is probably longer
|
|
than 4 meters so you need to set the materials for the inside
|
|
of the house to not apply fog otherwise when standing inside the
|
|
house looking outside the wall at the far end of the room will look
|
|
like it's in the fog.</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>Notice the walls and ceiling at the far end of the room are getting fog applied.
|
|
By turning fog off on the materials for the house we can fix that issue.</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="/manual/resources/prettify.js"></script>
|
|
<script src="/manual/resources/lesson.js"></script>
|
|
|
|
|
|
|
|
|
|
</body></html>
|