Pārlūkot izejas kodu

add materials article

Gregg Tavares 7 gadi atpakaļ
vecāks
revīzija
29dd038c31

+ 186 - 0
threejs/lessons/resources/threejs-materials.js

@@ -0,0 +1,186 @@
+'use strict';
+
+/* global threejsLessonUtils */
+
+{
+  function smoothOrFlat(flatShading, radius = 7) {
+    const widthDivisions = 12;
+    const heightDivisions = 9;
+    const geometry = new THREE.SphereBufferGeometry(radius, widthDivisions, heightDivisions);
+    const material = new THREE.MeshPhongMaterial({
+      flatShading,
+      color: 'hsl(300,50%,50%)',
+    });
+    return new THREE.Mesh(geometry, material);
+  }
+
+  function basicLambertPhongExample(MaterialCtor, lowPoly, params = {}) {
+    const radius = 7;
+    const widthDivisions = lowPoly ? 8 : 100;
+    const heightDivisions = lowPoly ? 5 : 50;
+    const geometry = new THREE.SphereBufferGeometry(radius, widthDivisions, heightDivisions);
+    const material = new MaterialCtor(Object.assign({
+      color: 'hsl(210,50%,50%)',
+    }, params));
+    return new THREE.Mesh(geometry, material);
+  }
+
+  function sideExample(side) {
+    const base = new THREE.Object3D();
+    const size = 6;
+    const geometry = new THREE.PlaneBufferGeometry(size, size);
+    [
+      { position: [ -1, 0, 0], up: [0,  1, 0], },
+      { position: [  1, 0, 0], up: [0, -1, 0], },
+      { position: [ 0, -1, 0], up: [0, 0, -1], },
+      { position: [ 0,  1, 0], up: [0, 0,  1], },
+      { position: [ 0, 0, -1], up: [ 1, 0, 0], },
+      { position: [ 0, 0,  1], up: [-1, 0, 0], },
+    ].forEach((settings, ndx) => {
+      const material = new THREE.MeshBasicMaterial({side});
+      material.color.setHSL(ndx / 6, .5, .5);
+      const mesh = new THREE.Mesh(geometry, material);
+      mesh.up.set(...settings.up);
+      mesh.lookAt(...settings.position);
+      mesh.position.set(...settings.position).multiplyScalar(size * .75);
+      base.add(mesh);
+    });
+    return base;
+  }
+
+  threejsLessonUtils.addDiagrams({
+    smoothShading: {
+      create() {
+        return smoothOrFlat(false);
+      },
+    },
+    flatShading: {
+      create() {
+        return smoothOrFlat(true);
+      },
+    },
+    MeshBasicMaterial: {
+      create() {
+        return basicLambertPhongExample(THREE.MeshBasicMaterial);
+      },
+    },
+    MeshLambertMaterial: {
+      create() {
+        return basicLambertPhongExample(THREE.MeshLambertMaterial);
+      },
+    },
+    MeshPhongMaterial: {
+      create() {
+        return basicLambertPhongExample(THREE.MeshPhongMaterial);
+      },
+    },
+    MeshBasicMaterialLowPoly: {
+      create() {
+        return basicLambertPhongExample(THREE.MeshBasicMaterial, true);
+      },
+    },
+    MeshLambertMaterialLowPoly: {
+      create() {
+        return basicLambertPhongExample(THREE.MeshLambertMaterial, true);
+      },
+    },
+    MeshPhongMaterialLowPoly: {
+      create() {
+        return basicLambertPhongExample(THREE.MeshPhongMaterial, true);
+      },
+    },
+    MeshPhongMaterialShininess0: {
+      create() {
+        return basicLambertPhongExample(THREE.MeshPhongMaterial, false, {
+          color: 'red',
+          shininess: 0,
+        });
+      },
+    },
+    MeshPhongMaterialShininess30: {
+      create() {
+        return basicLambertPhongExample(THREE.MeshPhongMaterial, false, {
+          color: 'red',
+          shininess: 30,
+        });
+      },
+    },
+    MeshPhongMaterialShininess150: {
+      create() {
+        return basicLambertPhongExample(THREE.MeshPhongMaterial, false, {
+          color: 'red',
+          shininess: 150,
+        });
+      },
+    },
+    MeshBasicMaterialCompare: {
+      create() {
+        return basicLambertPhongExample(THREE.MeshBasicMaterial, false, {
+          color: 'purple',
+        });
+      },
+    },
+    MeshLambertMaterialCompare: {
+      create() {
+        return basicLambertPhongExample(THREE.MeshLambertMaterial, false, {
+          color: 'black',
+          emissive: 'purple',
+        });
+      },
+    },
+    MeshPhongMaterialCompare: {
+      create() {
+        return basicLambertPhongExample(THREE.MeshPhongMaterial, false, {
+          color: 'black',
+          emissive: 'purple',
+          shininess: 0,
+        });
+      },
+    },
+    MeshToonMaterial: {
+      create() {
+        return basicLambertPhongExample(THREE.MeshToonMaterial);
+      },
+    },
+    MeshDepthMaterial: {
+      create(props) {
+        const {camera} = props;
+        const radius = 4;
+        const tube = 1.5;
+        const radialSegments = 8;
+        const tubularSegments = 64;
+        const p = 2;
+        const q = 3;
+        const geometry = new THREE.TorusKnotBufferGeometry(radius, tube, tubularSegments, radialSegments, p, q);
+        const material = new THREE.MeshDepthMaterial();
+        camera.near = 7;
+        camera.far = 20;
+        return new THREE.Mesh(geometry, material);
+      },
+    },
+    MeshNormalMaterial: {
+      create() {
+        const radius = 4;
+        const tube = 1.5;
+        const radialSegments = 8;
+        const tubularSegments = 64;
+        const p = 2;
+        const q = 3;
+        const geometry = new THREE.TorusKnotBufferGeometry(radius, tube, tubularSegments, radialSegments, p, q);
+        const material = new THREE.MeshNormalMaterial();
+        return new THREE.Mesh(geometry, material);
+      },
+    },
+    sideDefault: {
+      create() {
+        return sideExample(THREE.FrontSide);
+      },
+    },
+    sideDouble: {
+      create() {
+        return sideExample(THREE.DoubleSide);
+      },
+    },
+  });
+}
+

+ 0 - 8
threejs/lessons/threejs-materials-and-lights.md

@@ -1,8 +0,0 @@
-Title: Three.js Materials and Lights
-Description: Materials and Lights
-
-This article is part of a series of articles about three.js. The
-first article is [three.js fundamentals](three-fundamentals.html). If
-you haven't read yet you might want to consider starting there.
-
-TBD

+ 276 - 0
threejs/lessons/threejs-materials.md

@@ -0,0 +1,276 @@
+Title: Three.js Materials
+Description: Materials in Three.js
+
+This article is part of a series of articles about three.js. The
+first article is [three.js fundamentals](three-fundamentals.html). If
+you haven't read that yet and you're new to three.js you might want to
+consider starting there.
+ 
+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.
+
+There are 2 ways to set most material properties. One at creation time which
+we've seen before.
+
+```
+const material = new THREE.MeshPhongMaterial({
+  color: 0xFF0000,    // red (can also use a CSS color string here)
+  flatShading: true,
+});
+```
+
+The other is after creation
+
+```
+const material = new THREE.MeshPhongMaterial();
+material.color.setHSL(0, 1, .5);  // red
+material.flatShading = true;
+```
+
+note that properties of type `THREE.Color` have multiple ways to be set.
+
+```
+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                      
+```
+
+And at creation time you and pass either a hex number or a CSS string
+
+```
+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
+```
+
+So let's go over three.js's set of materials.
+
+The `MeshBasicMaterial` is not affected by lights. 
+The `MeshLambertMaterial` computes lighting only at the vertices vs the `MeshPhongMaterial` which computes lighting at every pixel. The `MeshPhongMaterial`
+also supports specular highlights.
+
+<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>
+
+The `shininess` setting of the `MeshPhongMaterial` determines the *shininess* of the specular highlight. It defaults to 30.
+
+<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>
+
+Note that setting the `emissive` property to a color on either a 
+`MeshLambertMaterial` or a `MeshPhongMaterial` and setting the `color` to black
+(and `shininess` to 0 for phong) ends up looking just like the `MeshBasicMaterial`.
+
+<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>
+
+Why have all 3 when `MeshPhongMaterial` can do the same things as `MeshBasicMaterial`
+and `MeshLambertMaterial`? 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 `MeshBasicMaterial`.
+
+The `MeshToonMaterial` is similar to the `MeshPhongMaterial`
+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.
+
+<div class="spread">
+  <div data-diagram="MeshToonMaterial"></div>
+</div>
+
+There are 3 materials that have special uses. `ShadowMaterial`
+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 peak at what's happening behind the scenes.
+
+The `MeshDepthMaterial` renders the depth of each pixel where
+pixels at negative [`near`](https://threejs.org/docs/#api/cameras/PerspectiveCamera.near) of the camera are 0 and negative [`far`](https://threejs.org/docs/#api/cameras/PerspectiveCamera.far) are 1. Certain special effects can use this data which we'll
+get into at another time.
+
+<div class="spread">
+  <div>
+    <div data-diagram="MeshDepthMaterial"></div>
+  </div>
+</div>
+
+The `MeshNormalMaterial` will show you the *normals* of geometry.
+*Normals* are the direction a particular triangle or pixel faces. 
+`MeshNormalMaterial` draws the view space normals. (the normals relative to the camera).
+<span class="color:red;">x is red</span>,
+<span class="color:green;">y is green</span>, and
+<span class="color:blue;">z is blue</span> so things facing
+to the right will be red, up will be green, and toward the screen will be blue.
+
+<div class="spread">
+  <div>
+    <div data-diagram="MeshNormalMaterial"></div>
+  </div>
+</div>
+
+`ShaderMaterial` is for making custom materials using three.js shader
+system. `RawShaderMaterial` is for making entirely custom shaders with
+no help from three.js. Both of these topics are large and will be
+covered later.
+
+The last 2 materials we'll metion here are the `MeshStandardMaterial`
+and the `MeshPhysicsMaterial`. Both implement what's called *physically
+based rendering* or often PBR for short. This is a way of computing
+material properties and lights that comes close to the way
+lights and materials work in the real word. We'll cover these in
+more detail in another article.
+
+Most materials share a bunch of settings all defined by `Material`.
+[See the docs](https://threejs.org/docs/#api/materials/Material)
+for all of them but let's go over two of the most commonly used
+properties.
+
+[`flatShading`](https://threejs.org/docs/#api/materials/Material.flatShading): 
+whether or not the object looks faceted or smooth. default = `false`.
+
+<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>
+
+[`side`](https://threejs.org/docs/#api/materials/Material.side): which sides of triangles to show. The default is `THREE.FrontSide`.
+Other options are `THREE.BackSide` and `THREE.DoubleSide` (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 `side` is for planes or other non-solid objects where it is
+common to see the back sides of triangles.
+
+Here are 6 planes drawn with `THREE.FrontSide` and `THREE.DoubleSide`.
+
+<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>
+
+There's really a lot to consider with materials and we actually still
+have a bunch more to go. In particlar 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 
+[setting up your development environment](threejs-setup.html)
+
+<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>material.needsUpdate = true</code> to tell
+three.js to apply your material changes. The most common settings
+that require you to set <code>needsUpdate</code> if you change the settings after
+using the material are:
+</p>
+<ul>
+  <li><code>flatShading</code></li>
+  <li>adding or removing a texture.
+    <p>
+    Changing a texture is ok, but if want switch from using no texture 
+    to using a texture or from using a texture to using no texture 
+    then you need to set <code>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>
+
+<canvas id="c"></canvas>
+<script src="../resources/threejs/r94/three.min.js"></script>
+<script src="../resources/threejs/r94/js/controls/TrackballControls.js"></script>
+<script src="resources/threejs-lesson-utils.js"></script>
+<script src="resources/threejs-materials.js"></script>
+
+

+ 1 - 0
threejs/lessons/toc.html

@@ -5,6 +5,7 @@
     <li><a href="/threejs/lessons/threejs-responsive.html">Three.js Responsive Design</a></li>
     <li><a href="/threejs/lessons/threejs-primitives.html">Three.js Primitives</a></li>
     <li><a href="/threejs/lessons/threejs-scenegraph.html">Three.js Scenegraph</a></li>
+    <li><a href="/threejs/lessons/threejs-materials.html">Three.js Materials</a></li>
   </ul>
 </ul>
 <ul>