-
-
Notifications
You must be signed in to change notification settings - Fork 35.7k
/
Copy pathmaterials.html
310 lines (291 loc) · 18.5 KB
/
materials.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
<!DOCTYPE html><html lang="en"><head>
<meta charset="utf-8">
<title>Materials</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 – Materials">
<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>
</head>
<body>
<div class="container">
<div class="lesson-title">
<h1>Materials</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.</p>
<p>Three.js provides several types of materials.
They define how objects will appear in the scene.
Which materials you use really depends on what you're trying to
accomplish.</p>
<p>There are 2 ways to set most material properties. One at creation time which
we've seen before.</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const material = new THREE.MeshPhongMaterial({
color: 0xFF0000, // red (can also use a CSS color string here)
flatShading: true,
});
</pre>
<p>The other is after creation</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const material = new THREE.MeshPhongMaterial();
material.color.setHSL(0, 1, .5); // red
material.flatShading = true;
</pre>
<p>note that properties of type <a href="/docs/#api/en/math/Color"><code class="notranslate" translate="no">THREE.Color</code></a> have multiple ways to be set.</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">material.color.set(0x00FFFF); // same as CSS's #RRGGBB style
material.color.set(cssString); // any CSS color, eg 'purple', '#F32',
// 'rgb(255, 127, 64)',
// 'hsl(180, 50%, 25%)'
material.color.set(someColor) // some other THREE.Color
material.color.setHSL(h, s, l) // where h, s, and l are 0 to 1
material.color.setRGB(r, g, b) // where r, g, and b are 0 to 1
</pre>
<p>And at creation time you can pass either a hex number or a CSS string</p>
<pre class="prettyprint showlinemods notranslate lang-js" translate="no">const m1 = new THREE.MeshBasicMaterial({color: 0xFF0000}); // red
const m2 = new THREE.MeshBasicMaterial({color: 'red'}); // red
const m3 = new THREE.MeshBasicMaterial({color: '#F00'}); // red
const m4 = new THREE.MeshBasicMaterial({color: 'rgb(255,0,0)'}); // red
const m5 = new THREE.MeshBasicMaterial({color: 'hsl(0,100%,50%)'}); // red
</pre>
<p>So let's go over three.js's set of materials.</p>
<p>The <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> is not affected by lights.
The <a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslate" translate="no">MeshLambertMaterial</code></a> computes lighting only at the vertices vs the <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> which computes lighting at every pixel. The <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a>
also supports specular highlights.</p>
<div class="spread">
<div>
<div data-diagram="MeshBasicMaterial"></div>
<div class="code">Basic</div>
</div>
<div>
<div data-diagram="MeshLambertMaterial"></div>
<div class="code">Lambert</div>
</div>
<div>
<div data-diagram="MeshPhongMaterial"></div>
<div class="code">Phong</div>
</div>
</div>
<div class="spread">
<div>
<div data-diagram="MeshBasicMaterialLowPoly"></div>
</div>
<div>
<div data-diagram="MeshLambertMaterialLowPoly"></div>
</div>
<div>
<div data-diagram="MeshPhongMaterialLowPoly"></div>
</div>
</div>
<div class="threejs_center code">low-poly models with same materials</div>
<p>The <code class="notranslate" translate="no">shininess</code> setting of the <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> determines the <em>shininess</em> of the specular highlight. It defaults to 30.</p>
<div class="spread">
<div>
<div data-diagram="MeshPhongMaterialShininess0"></div>
<div class="code">shininess: 0</div>
</div>
<div>
<div data-diagram="MeshPhongMaterialShininess30"></div>
<div class="code">shininess: 30</div>
</div>
<div>
<div data-diagram="MeshPhongMaterialShininess150"></div>
<div class="code">shininess: 150</div>
</div>
</div>
<p>Note that setting the <code class="notranslate" translate="no">emissive</code> property to a color on either a
<a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslate" translate="no">MeshLambertMaterial</code></a> or a <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> and setting the <code class="notranslate" translate="no">color</code> to black
(and <code class="notranslate" translate="no">shininess</code> to 0 for phong) ends up looking just like the <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>.</p>
<div class="spread">
<div>
<div data-diagram="MeshBasicMaterialCompare"></div>
<div class="code">
<div>Basic</div>
<div>color: 'purple'</div>
</div>
</div>
<div>
<div data-diagram="MeshLambertMaterialCompare"></div>
<div class="code">
<div>Lambert</div>
<div>color: 'black'</div>
<div>emissive: 'purple'</div>
</div>
</div>
<div>
<div data-diagram="MeshPhongMaterialCompare"></div>
<div class="code">
<div>Phong</div>
<div>color: 'black'</div>
<div>emissive: 'purple'</div>
<div>shininess: 0</div>
</div>
</div>
</div>
<p>Why have all 3 when <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> can do the same things as <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>
and <a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslate" translate="no">MeshLambertMaterial</code></a>? The reason is the more sophisticated material
takes more GPU power to draw. On a slower GPU like say a mobile phone
you might want to reduce the GPU power needed to draw your scene by
using one of the less complex materials. It also follows that if you
don't need the extra features then use the simplest material. If you don't
need the lighting and the specular highlight then use the <a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a>.</p>
<p>The <a href="/docs/#api/en/materials/MeshToonMaterial"><code class="notranslate" translate="no">MeshToonMaterial</code></a> is similar to the <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a>
with one big difference. Rather than shading smoothly it uses a gradient map
(an X by 1 texture) to decide how to shade. The default uses a gradient map
that is 70% brightness for the first 70% and 100% after but you can supply your
own gradient map. This ends up giving a 2 tone look that looks like a cartoon.</p>
<div class="spread">
<div data-diagram="MeshToonMaterial"></div>
</div>
<p>Next up there are 2 <em>physically based rendering</em> materials. Physically Based
Rendering is often abbreviated PBR.</p>
<p>The materials above use simple math to make materials that look 3D but they
aren't what actually happens in real world. The 2 PBR materials use much more
complex math to come close to what actually happens in the real world.</p>
<p>The first one is <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a>. The biggest difference between
<a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> and <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> is it uses different parameters.
<a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> had a <code class="notranslate" translate="no">shininess</code> setting. <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> has 2
settings <code class="notranslate" translate="no">roughness</code> and <code class="notranslate" translate="no">metalness</code>.</p>
<p>At a basic level <a href="/docs/#api/en/materials/MeshStandardMaterial#roughness"><code class="notranslate" translate="no">roughness</code></a> is the opposite
of <code class="notranslate" translate="no">shininess</code>. Something that has a high roughness, like a baseball doesn't
have hard reflections whereas something that's not rough, like a billiard ball,
is very shiny. Roughness goes from 0 to 1.</p>
<p>The other setting, <a href="/docs/#api/en/materials/MeshStandardMaterial#metalness"><code class="notranslate" translate="no">metalness</code></a>, says
how metal the material is. Metals behave differently than non-metals. 0
for non-metal and 1 for metal.</p>
<p>Here's a quick sample of <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> with <code class="notranslate" translate="no">roughness</code> from 0 to 1
across and <code class="notranslate" translate="no">metalness</code> from 0 to 1 down.</p>
<div data-diagram="MeshStandardMaterial" style="min-height: 400px"></div>
<p>The <a href="/docs/#api/en/materials/MeshPhysicalMaterial"><code class="notranslate" translate="no">MeshPhysicalMaterial</code></a> is same as the <a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> but it
adds a <code class="notranslate" translate="no">clearcoat</code> parameter that goes from 0 to 1 for how much to
apply a clearcoat gloss layer and a <code class="notranslate" translate="no">clearCoatRoughness</code> parameter
that specifies how rough the gloss layer is.</p>
<p>Here's the same grid of <code class="notranslate" translate="no">roughness</code> by <code class="notranslate" translate="no">metalness</code> as above but with
<code class="notranslate" translate="no">clearcoat</code> and <code class="notranslate" translate="no">clearCoatRoughness</code> settings.</p>
<div data-diagram="MeshPhysicalMaterial" style="min-height: 400px"></div>
<p>The various standard materials progress from fastest to slowest
<a href="/docs/#api/en/materials/MeshBasicMaterial"><code class="notranslate" translate="no">MeshBasicMaterial</code></a> ➡ <a href="/docs/#api/en/materials/MeshLambertMaterial"><code class="notranslate" translate="no">MeshLambertMaterial</code></a> ➡ <a href="/docs/#api/en/materials/MeshPhongMaterial"><code class="notranslate" translate="no">MeshPhongMaterial</code></a> ➡
<a href="/docs/#api/en/materials/MeshStandardMaterial"><code class="notranslate" translate="no">MeshStandardMaterial</code></a> ➡ <a href="/docs/#api/en/materials/MeshPhysicalMaterial"><code class="notranslate" translate="no">MeshPhysicalMaterial</code></a>. The slower materials
can make more realistic looking scenes but you might need to design
your code to use the faster materials on low powered or mobile machines.</p>
<p>There are 3 materials that have special uses. <a href="/docs/#api/en/materials/ShadowMaterial"><code class="notranslate" translate="no">ShadowMaterial</code></a>
is used to get the data created from shadows. We haven't
covered shadows yet. When we do we'll use this material
to take a peek at what's happening behind the scenes.</p>
<p>The <a href="/docs/#api/en/materials/MeshDepthMaterial"><code class="notranslate" translate="no">MeshDepthMaterial</code></a> renders the depth of each pixel where
pixels at negative <a href="/docs/#api/en/cameras/PerspectiveCamera#near"><code class="notranslate" translate="no">near</code></a> of the camera are 0 and negative <a href="/docs/#api/en/cameras/PerspectiveCamera#far"><code class="notranslate" translate="no">far</code></a> are 1. Certain special effects can use this data which we'll
get into at another time.</p>
<div class="spread">
<div>
<div data-diagram="MeshDepthMaterial"></div>
</div>
</div>
<p>The <a href="/docs/#api/en/materials/MeshNormalMaterial"><code class="notranslate" translate="no">MeshNormalMaterial</code></a> will show you the <em>normals</em> of geometry.
<em>Normals</em> are the direction a particular triangle or pixel faces.
<a href="/docs/#api/en/materials/MeshNormalMaterial"><code class="notranslate" translate="no">MeshNormalMaterial</code></a> draws the view space normals (the normals relative to the camera).
<span style="background: red;" class="color">x is red</span>,
<span style="background: green;" class="dark-color">y is green</span>, and
<span style="background: blue;" class="dark-color">z is blue</span> so things facing
to the right will be <span style="background: #FF7F7F;" class="color">pink</span>,
to the left will be <span style="background: #007F7F;" class="dark-color">aqua</span>,
up will be <span style="background: #7FFF7F;" class="color">light green</span>,
down will be <span style="background: #7F007F;" class="dark-color">purple</span>,
and toward the screen will be <span style="background: #7F7FFF;" class="color">lavender</span>.</p>
<div class="spread">
<div>
<div data-diagram="MeshNormalMaterial"></div>
</div>
</div>
<p><a href="/docs/#api/en/materials/ShaderMaterial"><code class="notranslate" translate="no">ShaderMaterial</code></a> is for making custom materials using the three.js shader
system. <a href="/docs/#api/en/materials/RawShaderMaterial"><code class="notranslate" translate="no">RawShaderMaterial</code></a> is for making entirely custom shaders with
no help from three.js. Both of these topics are large and will be
covered later.</p>
<p>Most materials share a bunch of settings all defined by <a href="/docs/#api/en/materials/Material"><code class="notranslate" translate="no">Material</code></a>.
<a href="/docs/#api/en/materials/Material">See the docs</a>
for all of them but let's go over two of the most commonly used
properties.</p>
<p><a href="/docs/#api/en/materials/Material#flatShading"><code class="notranslate" translate="no">flatShading</code></a>:
whether or not the object looks faceted or smooth. default = <code class="notranslate" translate="no">false</code>.</p>
<div class="spread">
<div>
<div data-diagram="smoothShading"></div>
<div class="code">flatShading: false</div>
</div>
<div>
<div data-diagram="flatShading"></div>
<div class="code">flatShading: true</div>
</div>
</div>
<p><a href="/docs/#api/en/materials/Material#side"><code class="notranslate" translate="no">side</code></a>: which sides of triangles to show. The default is <code class="notranslate" translate="no">THREE.FrontSide</code>.
Other options are <code class="notranslate" translate="no">THREE.BackSide</code> and <code class="notranslate" translate="no">THREE.DoubleSide</code> (both sides).
Most 3D objects drawn in three are probably opaque solids so the back sides
(the sides facing inside the solid) do not need to be drawn. The most common
reason to set <code class="notranslate" translate="no">side</code> is for planes or other non-solid objects where it is
common to see the back sides of triangles.</p>
<p>Here are 6 planes drawn with <code class="notranslate" translate="no">THREE.FrontSide</code> and <code class="notranslate" translate="no">THREE.DoubleSide</code>.</p>
<div class="spread">
<div>
<div data-diagram="sideDefault" style="height: 250px;"></div>
<div class="code">side: THREE.FrontSide</div>
</div>
<div>
<div data-diagram="sideDouble" style="height: 250px;"></div>
<div class="code">side: THREE.DoubleSide</div>
</div>
</div>
<p>There's really a lot to consider with materials and we actually still
have a bunch more to go. In particular we've mostly ignored textures
which open up a whole slew of options. Before we cover textures though
we need to take a break and cover
<a href="setup.html">setting up your development environment</a></p>
<div class="threejs_bottombar">
<h3>material.needsUpdate</h3>
<p>
This topic rarely affects most three.js apps but just as an FYI...
Three.js applies material settings when a material is used where "used"
means "something is rendered that uses the material". Some material settings are
only applied once as changing them requires lots of work by three.js.
In those cases you need to set <code class="notranslate" translate="no">material.needsUpdate = true</code> to tell
three.js to apply your material changes. The most common settings
that require you to set <code class="notranslate" translate="no">needsUpdate</code> if you change the settings after
using the material are:
</p>
<ul>
<li><code class="notranslate" translate="no">flatShading</code></li>
<li>adding or removing a texture
<p>
Changing a texture is ok, but if want to switch from using no texture
to using a texture or from using a texture to using no texture
then you need to set <code class="notranslate" translate="no">needsUpdate = true</code>.
</p>
<p>In the case of going from texture to no-texture it is often
just better to use a 1x1 pixel white texture.</p>
</li>
</ul>
<p>As mentioned above most apps never run into these issues. Most apps
do not switch between flat shaded and non flat shaded. Most apps also
either use textures or a solid color for a given material, they rarely
switch from using one to using the other.
</p>
</div>
<p><canvas id="c"></canvas></p>
<script type="module" src="../resources/threejs-materials.js"></script>
</div>
</div>
</div>
<script src="../resources/prettify.js"></script>
<script src="../resources/lesson.js"></script>
</body></html>