---
title: "Threejs材质"
description: "Theree.js中，材质和`THREE.Geometry`对象就构成了`THREE.Mesh`对象。材质就像物体的皮肤，决定了几何体的外表。"
url: "https://hicc.pro/p/three.js/threejs-material"
author: "tyler wang"
published: "2023-02-19T15:01:09.000Z"
updated: "2023-02-19T16:07:19.000Z"
category: "Threejs"
tags:
  - "three.js"
lang: "zh-CN"
site: "Hipo日志"
---

# Threejs材质

Theree.js中，材质和`THREE.Geometry`对象就构成了`THREE.Mesh`对象。材质就像物体的皮肤，决定了几何体的外表。

| Threejs材质 |  | 
| -------- | -------- | 
|  `MeshBasicMaterial` （网格基础材质）   | 基础材质，用户给几何体赋予一种简单的颜色，可用于几何体线框    | 
|  `MeshDepthMaterial`（网格深度材质） |从摄像机到网格的记录来决定如何给网格上色  |
|  `MeshNormalMaterial`（网格法线材质） | 根据法向量计算物体表面的颜色    |
|  `MeshLambertMaterial`（网格 Lambert 材质）    | 考虑光照影响的材质，用于创建暗淡的、不光亮的物体    |
|  `MeshPhongMaterial`（网格 Phong 材质）    | 考虑光照影响的材质，用于创建光亮的物体    |
|  `MeshStandardMaterial`（网格标准材质）    | 这种标准材质使用“基于物理的渲染(PBR)” 算法来绘制物体的表面。 它能够计算出表面与光线的正确互动关系，从而使渲染出的物体看起来更加真实    |
|  `MeshPhysicalMaterial`（网格物理材质）   |`MeshStandardMaterial`的扩展材质，他为光线反射计算模拟提供了更多控制     |
|  `MeshToonMaterial` （网格卡通材质）    | `MeshPhongMaterial`的扩展材质，它使得物理渲染更加卡通化     |
|  `ShadowMaterial` （阴影材质）    |  一个专门用户接受阴影图的特殊材质。在该材质中只有阴影图先，非阴影部分为完全透明的区域    |
|  `ShaderMaterial` （着色器材质）   |   这种材质容许使用自定义的着色程序， 直接控制顶点的放置方式以及像素的着色方式  |
|  `LineBasicMaterial` （直线基础材质）   | 可以用于`THREE.Line`（直线）几何体，用来创建着色的直线     |
|  `LineDashMaterial`    |  与`LineBasicMaterial`一样，但是可以创建出一种虚线的效果   |

### 材质的共有属性

#### 基础属性

#### 融合属性

#### 高级属性

### 网格材质

#### `THREE.MeshBasicMaterial`

[在线示例 / Live demo](https://hicc.pro/assets/threejs/src/chapter-04/01-basic-mesh-material.html)

```js
function init() {

  // use the defaults
  var stats = initStats();
  var camera = initCamera();

  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();
  // create a render and set the size
  var webGLRenderer = new THREE.WebGLRenderer();
  webGLRenderer.setClearColor(new THREE.Color(0x000000));
  webGLRenderer.setSize(window.innerWidth, window.innerHeight);
  webGLRenderer.shadowMapEnabled = true;
  
  var canvasRenderer = new THREE.CanvasRenderer();
  canvasRenderer.setSize(window.innerWidth, window.innerHeight);
  renderer = webGLRenderer;

  var groundGeom = new THREE.PlaneGeometry(100, 100, 4, 4);
  var groundMesh = new THREE.Mesh(groundGeom, new THREE.MeshBasicMaterial({
    color: 0x777777
  }));
  groundMesh.rotation.x = -Math.PI / 2;
  groundMesh.position.y = -20;
  scene.add(groundMesh);

  var sphereGeometry = new THREE.SphereGeometry(14, 20, 20);
  var cubeGeometry = new THREE.BoxGeometry(15, 15, 15);
  var planeGeometry = new THREE.PlaneGeometry(14, 14, 4, 4);

  var meshMaterial = new THREE.MeshBasicMaterial({
    color: 0x7777ff,
    name: 'Basic Material',
    flatShading: true
  });

  var sphere = new THREE.Mesh(sphereGeometry, meshMaterial);
  var cube = new THREE.Mesh(cubeGeometry, meshMaterial);
  var plane = new THREE.Mesh(planeGeometry, meshMaterial);

  // position the sphere
  sphere.position.x = 0;
  sphere.position.y = 3;
  sphere.position.z = 2;

  cube.position = sphere.position;
  plane.position = sphere.position;

  // add the sphere to the scene
  scene.add(cube);

  // position and point the camera to the center of the scene
  camera.position.x = -20;
  camera.position.y = 50;
  camera.position.z = 40;
  camera.lookAt(new THREE.Vector3(10, 0, 0));

  // add subtle ambient lighting
  var ambientLight = new THREE.AmbientLight(0x0c0c0c);
  scene.add(ambientLight);

  // add spotlight for the shadows
  var spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(-40, 60, -10);
  spotLight.castShadow = true;
  scene.add(spotLight);

  // add the output of the renderer to the html element
  document.getElementById("webgl-output").appendChild(renderer.domElement);

  // call the render function
  var step = 0;
  var oldContext = null;

  var controls = new function () {
    this.rotationSpeed = 0.02;
    this.bouncingSpeed = 0.03;

    this.color = meshMaterial.color.getStyle();
    this.selectedMesh = "cube";

    this.switchRenderer = function () {
      if (renderer instanceof THREE.WebGLRenderer) {
        renderer = canvasRenderer;
        document.getElementById("webgl-output").innerHTML = "";
        document.getElementById("webgl-output").appendChild(renderer.domElement);
      } else {
        renderer = webGLRenderer;
        document.getElementById("webgl-output").innerHTML = "";
        document.getElementById("webgl-output").appendChild(renderer.domElement);
      }
    }
  };

  var gui = new dat.GUI();
  var selectedMesh = cube;
  
  addBasicMaterialSettings(gui, controls, meshMaterial);

  var spGui = gui.addFolder("THREE.MeshBasicMaterial");
  spGui.addColor(controls, 'color').onChange(function (e) {
    meshMaterial.color.setStyle(e)
  });
  spGui.add(meshMaterial, 'wireframe');
  spGui.add(meshMaterial, 'wireframeLinewidth', 0, 20);
  spGui.add(meshMaterial, 'wireframeLinejoin', ['round', 'bevel', 'miter']).onChange(function (e) {
    meshMaterial.wireframeLinejoin = e
  });
  spGui.add(meshMaterial, 'wireframeLinecap', ['butt', 'round', 'square']).onChange(function (e) {
    meshMaterial.wireframeLinecap = e
  });

  loadGopher(meshMaterial).then(function(gopher) {
    gopher.scale.x = 4;
    gopher.scale.y = 4;
    gopher.scale.z = 4;
    gui.add(controls, 'selectedMesh', ["cube", "sphere", "plane", "gopher"]).onChange(function (e) {

      scene.remove(plane);
      scene.remove(cube);
      scene.remove(sphere);
      scene.remove(gopher);
  
      switch (e) {
        case "cube":
          scene.add(cube);
          selectedMesh = cube;
          break;
        case "sphere":
          scene.add(sphere);
          selectedMesh = sphere;
          break;
        case "plane":
          scene.add(plane);
          selectedMesh = plane;
          break;
        case "gopher":
          scene.add(gopher);
          selectedMesh = gopher;
          break;
      }
    });
  });

  gui.add(controls, 'switchRenderer');

  render();

  function render() {
    stats.update();

    selectedMesh.rotation.y = step += 0.01;

    // render using requestAnimationFrame
    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }
}
```

#### `THREE.MeshDepthMaterial`

[在线示例 / Live demo](https://hicc.pro/assets/threejs/src/chapter-04/02-depth-material.html)

```js
function init() {

  // use the defaults
  var stats = initStats();
  var renderer = initRenderer();

  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();
  scene.overrideMaterial = new THREE.MeshDepthMaterial();

  var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 50, 110);
  camera.position.set(-50, 40, 50);
  camera.lookAt(scene.position);

  // call the render function
  var step = 0;

  var controls = new function () {
    this.cameraNear = camera.near;
    this.cameraFar = camera.far;
    this.rotationSpeed = 0.02;
    this.numberOfObjects = scene.children.length;

    this.removeCube = function () {
      var allChildren = scene.children;
      var lastObject = allChildren[allChildren.length - 1];
      if (lastObject instanceof THREE.Mesh) {
        scene.remove(lastObject);
        this.numberOfObjects = scene.children.length;
      }
    };

    this.addCube = function () {

      var cubeSize = Math.ceil(3 + (Math.random() * 3));
      var cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
      var cubeMaterial = new THREE.MeshLambertMaterial({
        color: Math.random() * 0xffffff
      });
      var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
      cube.castShadow = true;

      // position the cube randomly in the scene
      cube.position.x = -60 + Math.round((Math.random() * 100));
      cube.position.y = Math.round((Math.random() * 10));
      cube.position.z = -100 + Math.round((Math.random() * 150));

      // add the cube to the scene
      scene.add(cube);
      this.numberOfObjects = scene.children.length;
    };

    this.outputObjects = function () {
      console.log(scene.children);
    }
  };

  var gui = new dat.GUI();
  addBasicMaterialSettings(gui, controls, scene.overrideMaterial);
  var spGui = gui.addFolder("THREE.MeshDepthMaterial");
  spGui.add(scene.overrideMaterial, 'wireframe');
  spGui.add(scene.overrideMaterial, 'wireframeLinewidth', 0, 20);

  gui.add(controls, 'rotationSpeed', 0, 0.5);
  gui.add(controls, 'addCube');
  gui.add(controls, 'removeCube');
  gui.add(controls, 'cameraNear', 0, 100).onChange(function (e) {
    camera.near = e;
    camera.updateProjectionMatrix();
  });
  gui.add(controls, 'cameraFar', 50, 200).onChange(function (e) {
    camera.far = e;
    camera.updateProjectionMatrix();
  });

  var i = 0;
  while (i < 10) {
    controls.addCube();
    i++;
  }

  render();

  function render() {
    stats.update();

    // rotate the cubes around its axes
    scene.traverse(function (e) {
      if (e instanceof THREE.Mesh) {

        e.rotation.x += controls.rotationSpeed;
        e.rotation.y += controls.rotationSpeed;
        e.rotation.z += controls.rotationSpeed;
      }
    });

    // render using requestAnimationFrame
    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }
}
```

#### 联合材质

[在线示例 / Live demo](https://hicc.pro/assets/threejs/src/chapter-04/03-combined-material.html)

```js
function init() {

  // use the defaults
  var stats = initStats();
  var renderer = initRenderer();
  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();

  // create a camera, which defines where we're looking at.
  var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 50, 110);
  camera.position.set(-50, 40, 50);
  camera.lookAt(scene.position);

  // call the render function
  var step = 0;

  var controls = new function () {
    this.cameraNear = camera.near;
    this.cameraFar = camera.far;
    this.rotationSpeed = 0.02;
    this.numberOfObjects = scene.children.length;
    this.color = 0x00ff00;

    this.removeCube = function () {
      var allChildren = scene.children;
      var lastObject = allChildren[allChildren.length - 1];
      if (lastObject instanceof THREE.Group) {
        scene.remove(lastObject);
        this.numberOfObjects = scene.children.length;
      }
    };

    this.addCube = function () {

      var cubeSize = Math.ceil(3 + (Math.random() * 3));
      var cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);

      //var cubeMaterial = new THREE.MeshLambertMaterial({color:  Math.random() * 0xffffff });
      var cubeMaterial = new THREE.MeshDepthMaterial();
      var colorMaterial = new THREE.MeshBasicMaterial({
        color: controls.color,
        transparent: true,
        blending: THREE.MultiplyBlending
      });
      var cube = new THREE.SceneUtils.createMultiMaterialObject(cubeGeometry, [colorMaterial,
        cubeMaterial
      ]);
      cube.children[1].scale.set(0.99, 0.99, 0.99);
      cube.castShadow = true;

      // position the cube randomly in the scene
      cube.position.x = -60 + Math.round((Math.random() * 100));
      cube.position.y = Math.round((Math.random() * 10));
      cube.position.z = -100 + Math.round((Math.random() * 150));

      // add the cube to the scene
      scene.add(cube);
      this.numberOfObjects = scene.children.length;
    };

    this.outputObjects = function () {
      console.log(scene.children);
    }
  };

  var gui = new dat.GUI();
  gui.addColor(controls, 'color');
  gui.add(controls, 'rotationSpeed', 0, 0.5);
  gui.add(controls, 'addCube');
  gui.add(controls, 'removeCube');
  gui.add(controls, 'cameraNear', 0, 50).onChange(function (e) {
    camera.near = e;
    camera.updateProjectionMatrix();
  });
  gui.add(controls, 'cameraFar', 50, 200).onChange(function (e) {
    camera.far = e;
    camera.updateProjectionMatrix();
  });

  var i = 0;
  while (i < 10) {
    controls.addCube();
    i++;
  }

  render();

  function render() {
    stats.update();

    // rotate the cubes around its axes
    scene.traverse(function (e) {
      if (e instanceof THREE.Group) {
        e.rotation.x += controls.rotationSpeed;
        e.rotation.y += controls.rotationSpeed;
        e.rotation.z += controls.rotationSpeed;
      }
    });

    // render using requestAnimationFrame
    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }

}
```

#### `THREE.MeshNormalMaterial`

[在线示例 / Live demo](https://hicc.pro/assets/threejs/src/chapter-04/04-mesh-normal-material.html)

```js
function init() {

  // use the defaults
  var stats = initStats();
  var camera = initCamera();

  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();

  // create a camera, which defines where we're looking at.
  var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.set(-20, 30, 40);
  camera.lookAt(new THREE.Vector3(10, 0, 0));

  // create a render and set the size
  var renderer;
  var webGLRenderer = new THREE.WebGLRenderer();
  webGLRenderer.setClearColor(new THREE.Color(0x000000));
  webGLRenderer.setSize(window.innerWidth, window.innerHeight);

  var canvasRenderer = new THREE.CanvasRenderer();
  canvasRenderer.setSize(window.innerWidth, window.innerHeight);
  renderer = webGLRenderer;

  var groundGeom = new THREE.PlaneGeometry(100, 100, 4, 4);
  var groundMesh = new THREE.Mesh(groundGeom, new THREE.MeshBasicMaterial({
    color: 0x777777
  }));
  groundMesh.rotation.x = -Math.PI / 2;
  groundMesh.position.y = -20;
  scene.add(groundMesh);

  var sphereGeometry = new THREE.SphereGeometry(14, 20, 20);
  var cubeGeometry = new THREE.BoxGeometry(15, 15, 15);
  var planeGeometry = new THREE.PlaneGeometry(14, 14, 4, 4);

  var meshMaterial = new THREE.MeshNormalMaterial();
  var sphere = new THREE.Mesh(sphereGeometry, meshMaterial);
  var cube = new THREE.Mesh(cubeGeometry, meshMaterial);
  var plane = new THREE.Mesh(planeGeometry, meshMaterial);

  var selectedMesh = cube;

  // position the sphere
  sphere.position.x = 0;
  sphere.position.y = 3;
  sphere.position.z = 2;

  for (var f = 0, fl = sphere.geometry.faces.length; f < fl; f++) {
    var face = sphere.geometry.faces[f];
    var centroid = new THREE.Vector3(0, 0, 0);
    centroid.add(sphere.geometry.vertices[face.a]);
    centroid.add(sphere.geometry.vertices[face.b]);
    centroid.add(sphere.geometry.vertices[face.c]);
    centroid.divideScalar(3);

    var arrow = new THREE.ArrowHelper(
      face.normal,
      centroid,
      2,
      0x3333FF,
      0.5,
      0.5);
    // sphere.add(arrow);
  }

  cube.position = sphere.position;
  plane.position = sphere.position;

  // add the sphere to the scene
  scene.add(cube);

  // add subtle ambient lighting
  var ambientLight = new THREE.AmbientLight(0x0c0c0c);
  scene.add(ambientLight);

  // add spotlight for the shadows
  var spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(-40, 60, -10);
  spotLight.castShadow = true;
  scene.add(spotLight);

  // add the output of the renderer to the html element
  document.getElementById("webgl-output").appendChild(renderer.domElement);

  // call the render function
  var step = 0;
  var oldContext = null;

  var controls = new function () {
    this.rotationSpeed = 0.02;
    this.bouncingSpeed = 0.03;
    this.selectedMesh = "cube";
  };

  var gui = new dat.GUI();    
  addBasicMaterialSettings(gui, controls, meshMaterial);

  loadGopher(meshMaterial).then(function(gopher) {
    gopher.scale.x = 4;
    gopher.scale.y = 4;
    gopher.scale.z = 4;
    gui.add(controls, 'selectedMesh', ["cube", "sphere", "plane", "gopher"]).onChange(function (e) {

      scene.remove(plane);
      scene.remove(cube);
      scene.remove(sphere);
      scene.remove(gopher);
  
      switch (e) {
        case "cube":
          scene.add(cube);
          selectedMesh = cube;
          break;
        case "sphere":
          scene.add(sphere);
          selectedMesh = sphere;
          break;
        case "plane":
          scene.add(plane);
          selectedMesh = plane;
          break;
        case "gopher":
          scene.add(gopher);
          selectedMesh = gopher;
          break;
      }
    });
  });

  render();

  function render() {
    stats.update();

    selectedMesh.rotation.y = step += 0.01;
    // render using requestAnimationFrame
    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }

}
```

#### 在单几何体上使用多种材质

[在线示例 / Live demo](https://hicc.pro/assets/threejs/src/chapter-04/05-mesh-face-material.html)

```js

function init() {

  // use the defaults
  var stats = initStats();
  var renderer = initRenderer();
  var camera = initCamera();

  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();

  // create a camera, which defines where we're looking at.
  // var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

  // create a render and set the size
  // var renderer = new THREE.WebGLRenderer();

  // renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
  // renderer.setSize(window.innerWidth, window.innerHeight);
  // renderer.shadowMapEnabled = false;

  // position and point the camera to the center of the scene
  // camera.position.x = -40;
  // camera.position.y = 40;
  // camera.position.z = 40;
  // camera.lookAt(scene.position);

  // add subtle ambient lighting
  //        var ambientLight = new THREE.AmbientLight(0x0c0c0c);
  //        scene.add(ambientLight);

  // add spotlight for the shadows
  var spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(-40, 60, -10);
  spotLight.castShadow = true;
  scene.add(spotLight);

  var group = new THREE.Mesh();
  // add all the rubik cube elements
  var mats = [];
  mats.push(new THREE.MeshBasicMaterial({
    color: 0x009e60
  }));
  // mats.push(new THREE.MeshBasicMaterial({
    // color: 0x009e60
  // }));
  mats.push(new THREE.MeshBasicMaterial({
    color: 0x0051ba
  }));
  // mats.push(new THREE.MeshBasicMaterial({
    // color: 0x0051ba
  // }));
  mats.push(new THREE.MeshBasicMaterial({
    color: 0xffd500
  }));
  // mats.push(new THREE.MeshBasicMaterial({
    // color: 0xffd500
  // }));
  mats.push(new THREE.MeshBasicMaterial({
    color: 0xff5800
  }));
  // mats.push(new THREE.MeshBasicMaterial({
    // color: 0xff5800
  // }));
  mats.push(new THREE.MeshBasicMaterial({
    color: 0xC41E3A
  }));
  // mats.push(new THREE.MeshBasicMaterial({
    // color: 0xC41E3A
  // }));
  mats.push(new THREE.MeshBasicMaterial({
    color: 0xffffff
  }));
  // mats.push(new THREE.MeshBasicMaterial({
    // color: 0xffffff
  // }));

  for (var x = 0; x < 3; x++) {
    for (var y = 0; y < 3; y++) {
      for (var z = 0; z < 3; z++) {
        var cubeGeom = new THREE.BoxGeometry(2.9, 2.9, 2.9);
        var cube = new THREE.Mesh(cubeGeom, mats);
        cube.position.set(x * 3 - 3, y * 3 - 3, z * 3 - 3);

        group.add(cube);
      }
    }
  }

  group.scale.copy(new THREE.Vector3(2,2,2))
  // call the render function
  scene.add(group);
  var step = 0;

  var controls = new function () {
    this.rotationSpeed = 0.01;
    this.numberOfObjects = scene.children.length;
  };

  var gui = new dat.GUI();
  gui.add(controls, 'rotationSpeed', 0, 0.5);

  render();

  debugger;
  
  function render() {
    stats.update();

    group.rotation.y = step += controls.rotationSpeed;
    group.rotation.z = step -= controls.rotationSpeed;
    group.rotation.x = step += controls.rotationSpeed;
    // render using requestAnimationFrame
    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }
}

```

### 高级材质

#### `THREE.meshLambertMaterial`

[在线示例 / Live demo](https://hicc.pro/assets/threejs/src/chapter-04/06-mesh-lambert-material.html)

```js
function init() {

  // use the defaults
  var stats = initStats();
  var renderer = initRenderer();
  var camera = initCamera();

  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();

  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();

  var groundGeom = new THREE.PlaneGeometry(100, 100, 4, 4);
  var groundMesh = new THREE.Mesh(groundGeom, new THREE.MeshBasicMaterial({
    color: 0x555555
  }));
  groundMesh.rotation.x = -Math.PI / 2;
  groundMesh.position.y = -20;
  scene.add(groundMesh);

  var sphereGeometry = new THREE.SphereGeometry(14, 20, 20);
  var cubeGeometry = new THREE.BoxGeometry(15, 15, 15);
  var planeGeometry = new THREE.PlaneGeometry(14, 14, 4, 4);

  var meshMaterial = new THREE.MeshLambertMaterial({
    color: 0x7777ff
  });
  var sphere = new THREE.Mesh(sphereGeometry, meshMaterial);
  var cube = new THREE.Mesh(cubeGeometry, meshMaterial);
  var plane = new THREE.Mesh(planeGeometry, meshMaterial);

  var selectedMesh = cube;

  // position the sphere
  sphere.position.x = 0;
  sphere.position.y = 3;
  sphere.position.z = 2;

  cube.position = sphere.position;
  plane.position = sphere.position;

  // add the sphere to the scene
  scene.add(cube);

  // add subtle ambient lighting
  var ambientLight = new THREE.AmbientLight(0x0c0c0c);
  scene.add(ambientLight);

  // add spotlight for the shadows
  var spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(-30, 60, 60);
  spotLight.castShadow = true;
  scene.add(spotLight);

  // call the render function
  var step = 0;

  var controls = new function () {
    this.rotationSpeed = 0.02;
    this.bouncingSpeed = 0.03;

    this.opacity = meshMaterial.opacity;
    this.transparent = meshMaterial.transparent;
    this.overdraw = meshMaterial.overdraw;
    this.visible = meshMaterial.visible;
    this.emissive = meshMaterial.emissive.getHex();

    // this.ambient = meshMaterial.ambient.getHex();
    this.side = "front";

    this.color = meshMaterial.color.getStyle();
    this.wrapAround = false;
    this.wrapR = 1;
    this.wrapG = 1;
    this.wrapB = 1;

    this.selectedMesh = "cube";
  };

  var gui = new dat.GUI();
  addBasicMaterialSettings(gui, controls, meshMaterial);
  var spGui = gui.addFolder("THREE.MeshLambertMaterial");
  spGui.addColor(controls, 'color').onChange(function (e) {
    meshMaterial.color.setStyle(e)
  });
  spGui.addColor(controls, 'emissive').onChange(function (e) {
    meshMaterial.emissive = new THREE.Color(e);
  });
  spGui.add(meshMaterial, 'wireframe');
  spGui.add(meshMaterial, 'wireframeLinewidth', 0, 20);

  loadGopher(meshMaterial).then(function(gopher) {
    gopher.scale.x = 4;
    gopher.scale.y = 4;
    gopher.scale.z = 4;
    gui.add(controls, 'selectedMesh', ["cube", "sphere", "plane", "gopher"]).onChange(function (e) {

      scene.remove(plane);
      scene.remove(cube);
      scene.remove(sphere);
      scene.remove(gopher);
  
      switch (e) {
        case "cube":
          scene.add(cube);
          selectedMesh = cube;
          break;
        case "sphere":
          scene.add(sphere);
          selectedMesh = sphere;
          break;
        case "plane":
          scene.add(plane);
          selectedMesh = plane;
          break;
        case "gopher":
          scene.add(gopher);
          selectedMesh = gopher;
          break;
      }
    });
  });

  render()

  function render() {
    stats.update();

    selectedMesh.rotation.y = step += 0.01;

    // render using requestAnimationFrame
    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }
}
```

#### `THREE.MeshPhongMaterial`

[在线示例 / Live demo](https://hicc.pro/assets/threejs/src/chapter-04/07-mesh-phong-material.html)

```js
function init() {

  // use the defaults
  var stats = initStats();
  var renderer = initRenderer();
  var camera = initCamera();

  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();
  addLargeGroundPlane(scene);

  // add spotlight for the shadows
  var spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(-0, 30, 60);
  spotLight.castShadow = true;
  spotLight.intensity = 0.6;
  scene.add(spotLight);

  // call the render function
  var step = 0;
  var material = new THREE.MeshPhongMaterial({color: 0x7777ff})
  var controls = new function () {
    this.color = material.color.getStyle();
    this.emissive = material.emissive.getStyle();
    this.specular = material.specular.getStyle();
  };

  var gui = new dat.GUI();
  
  addBasicMaterialSettings(gui, controls, material);
  addMeshSelection(gui, controls, material, scene);
  var spGui = gui.addFolder("THREE.MeshPhongMaterial");
  spGui.addColor(controls, 'color').onChange(function (e) {
    material.color.setStyle(e)
  });
  spGui.addColor(controls, 'emissive').onChange(function (e) {
    material.emissive = new THREE.Color(e);
  });
  spGui.addColor(controls, 'specular').onChange(function (e) {
    material.specular = new THREE.Color(e);
  });
  spGui.add(material, 'shininess', 0, 100, )
  spGui.add(material, 'wireframe');
  spGui.add(material, 'wireframeLinewidth', 0, 20);

  camera.lookAt(controls.selected.position);
  render();

  function render() {
    stats.update();

    if (controls.selected) controls.selected.rotation.y = step += 0.01;

    // render using requestAnimationFrame
    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }
}
```

#### `THREE.MeshToonMaterial`

[在线示例 / Live demo](https://hicc.pro/assets/threejs/src/chapter-04/08-mesh-toon-material.html)

```js
function init() {

  // use the defaults
  var stats = initStats();
  var renderer = initRenderer();
  var camera = initCamera();

  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();
  addLargeGroundPlane(scene);

  // add spotlight for the shadows
  var spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(-0, 30, 60);
  spotLight.castShadow = true;
  spotLight.intensity = 0.6;
  scene.add(spotLight);

  // call the render function
  var step = 0;
  var material = new THREE.MeshToonMaterial({color: 0x7777ff})
  var controls = new function () {
    this.color = material.color.getStyle();
    this.emissive = material.emissive.getStyle();
    this.specular = material.specular.getStyle();
  };

  var gui = new dat.GUI();
  
  addBasicMaterialSettings(gui, controls, material);
  addMeshSelection(gui, controls, material, scene);
  var spGui = gui.addFolder("THREE.MeshToonMaterial");
  spGui.addColor(controls, 'color').onChange(function (e) {
    material.color.setStyle(e)
  });
  spGui.addColor(controls, 'emissive').onChange(function (e) {
    material.emissive = new THREE.Color(e);
  });
  spGui.addColor(controls, 'specular').onChange(function (e) {
    material.specular = new THREE.Color(e);
  });
  spGui.add(material, 'shininess', 0, 100, )
  spGui.add(material, 'wireframe');
  spGui.add(material, 'wireframeLinewidth', 0, 20);

  camera.lookAt(controls.selected.position);
  render();

  function render() {
    stats.update();

    if (controls.selected) controls.selected.rotation.y = step += 0.01;

    // render using requestAnimationFrame
    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }
}

```

#### `THREE.MeshStandardMaterial`

[在线示例 / Live demo](https://hicc.pro/assets/threejs/src/chapter-04/09-mesh-standard-material.html)

```js
function init() {

  // use the defaults
  var stats = initStats();
  var renderer = initRenderer();
  var camera = initCamera();

  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();
  addLargeGroundPlane(scene);

  // add spotlight for the shadows
  var spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(-0, 30, 60);
  spotLight.castShadow = true;
  spotLight.intensity = 0.6;
  scene.add(spotLight);

  // call the render function
  var step = 0;
  var material = new THREE.MeshStandardMaterial({color: 0x7777ff})
  var controls = new function () {
    this.color = material.color.getStyle();
    this.emissive = material.emissive.getStyle();
  };

  var gui = new dat.GUI();
  
  addBasicMaterialSettings(gui, controls, material);
  addMeshSelection(gui, controls, material, scene);
  var spGui = gui.addFolder("THREE.MeshStandardMaterial");
  spGui.addColor(controls, 'color').onChange(function (e) {
    material.color.setStyle(e)
  });
  spGui.addColor(controls, 'emissive').onChange(function (e) {
    material.emissive = new THREE.Color(e);
  });
  spGui.add(material, 'metalness', 0, 1, 0.01);
  spGui.add(material, 'roughness', 0, 1, 0.01);
  spGui.add(material, 'wireframe');
  spGui.add(material, 'wireframeLinewidth', 0, 20);

  camera.lookAt(controls.selected.position);
  render();

  function render() {
    stats.update();

    if (controls.selected) controls.selected.rotation.y = step += 0.01;

    // render using requestAnimationFrame
    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }
}
```

#### `THREE.MeshPhysicalMaterial`

[在线示例 / Live demo](https://hicc.pro/assets/threejs/src/chapter-04/10-mesh-physical-material.html)

```js
function init() {

  // use the defaults
  var stats = initStats();
  var renderer = initRenderer();
  var camera = initCamera();

  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();
  addLargeGroundPlane(scene);

  // add spotlight for the shadows
  var spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(-0, 30, 60);
  spotLight.castShadow = true;
  spotLight.intensity = 0.6;
  scene.add(spotLight);

  // call the render function
  var step = 0;
  var material = new THREE.MeshPhysicalMaterial({color: 0x7777ff})
  var controls = new function () {
    this.color = material.color.getStyle();
    this.emissive = material.emissive.getStyle();
  };

  var gui = new dat.GUI();
  
  addBasicMaterialSettings(gui, controls, material);
  addMeshSelection(gui, controls, material, scene);
  var spGui = gui.addFolder("THREE.MeshPhysicalMaterial");
  spGui.addColor(controls, 'color').onChange(function (e) {
    material.color.setStyle(e)
  });
  spGui.addColor(controls, 'emissive').onChange(function (e) {
    material.emissive = new THREE.Color(e);
  });
  spGui.add(material, 'metalness', 0, 1, 0.01);
  spGui.add(material, 'roughness', 0, 1, 0.01);
  spGui.add(material, 'clearCoat', 0, 1, 0.01);
  spGui.add(material, 'clearCoatRoughness', 0, 1, 0.01);
  spGui.add(material, 'reflectivity', 0, 1, 0.01);
  spGui.add(material, 'wireframe');
  spGui.add(material, 'wireframeLinewidth', 0, 20);

  camera.lookAt(controls.selected.position);
  render();

  function render() {
    stats.update();

    if (controls.selected) controls.selected.rotation.y = step += 0.01;

    // render using requestAnimationFrame
    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }
}
```

#### `THREE.ShaderMaterial`创建自己的着色器

[在线示例 / Live demo](https://hicc.pro/assets/threejs/src/chapter-04/11-shader-material.html)

```html
<script id="vertex-shader" type="x-shader/x-vertex">
		uniform float time;
		varying vec2 vUv;

		void main()
		{
		vec3 posChanged = position;
		posChanged.x = posChanged.x*(abs(sin(time*1.0)));
		posChanged.y = posChanged.y*(abs(cos(time*1.0)));
		posChanged.z = posChanged.z*(abs(sin(time*1.0)));
		//gl_Position = projectionMatrix * modelViewMatrix * vec4(position*(abs(sin(time)/2.0)+0.5),1.0);
		gl_Position = projectionMatrix * modelViewMatrix * vec4(posChanged,1.0);
		}

</script>

<script id="fragment-shader-1" type="x-shader/x-fragment">
		precision highp float;
		uniform float time;
		uniform float alpha;
		uniform vec2 resolution;
		varying vec2 vUv;

		void main2(void)
		{
		vec2 position = vUv;
		float red = 1.0;
		float green = 0.25 + sin(time) * 0.25;
		float blue = 0.0;
		vec3 rgb = vec3(red, green, blue);
		vec4 color = vec4(rgb, alpha);
		gl_FragColor = color;
		}

		#define PI 3.14159
		#define TWO_PI (PI*2.0)
		#define N 68.5

		void main(void)
		{
		vec2 center = (gl_FragCoord.xy);
		center.x=-10.12*sin(time/200.0);
		center.y=-10.12*cos(time/200.0);

		vec2 v = (gl_FragCoord.xy - resolution/20.0) / min(resolution.y,resolution.x) * 15.0;
		v.x=v.x-10.0;
		v.y=v.y-200.0;
		float col = 0.0;

		for(float i = 0.0; i < N; i++)
		{
		float a = i * (TWO_PI/N) * 61.95;
		col += cos(TWO_PI*(v.y * cos(a) + v.x * sin(a) + sin(time*0.004)*100.0 ));
		}

		col /= 5.0;

		gl_FragColor = vec4(col*1.0, -col*1.0,-col*4.0, 1.0);
		}

</script>

<script id="fragment-shader-2" type="x-shader/x-fragment">
		// from http://glsl.heroku.com/e#7906.0

		uniform float time;
		uniform vec2 resolution;

		// 2013-03-30 by @hintz

		#define CGFloat float
		#define M_PI 3.14159265359

		vec3 hsvtorgb(float h, float s, float v)
		{
		float c = v * s;
		h = mod((h * 6.0), 6.0);
		float x = c * (1.0 - abs(mod(h, 2.0) - 1.0));
		vec3 color;

		if (0.0 <= h && h < 1.0)
		{
		color = vec3(c, x, 0.0);
		}
		else if (1.0 <= h && h < 2.0)
		{
		color = vec3(x, c, 0.0);
		}
		else if (2.0 <= h && h < 3.0)
		{
		color = vec3(0.0, c, x);
		}
		else if (3.0 <= h && h < 4.0)
		{
		color = vec3(0.0, x, c);
		}
		else if (4.0 <= h && h < 5.0)
		{
		color = vec3(x, 0.0, c);
		}
		else if (5.0 <= h && h < 6.0)
		{
		color = vec3(c, 0.0, x);
		}
		else
		{
		color = vec3(0.0);
		}

		color += v - c;

		return color;
		}

		void main(void)
		{

		vec2 position = (gl_FragCoord.xy - 0.5 * resolution) / resolution.y;
		float x = position.x;
		float y = position.y;

		CGFloat a = atan(x, y);

		CGFloat d = sqrt(x*x+y*y);
		CGFloat d0 = 0.5*(sin(d-time)+1.5)*d;
		CGFloat d1 = 5.0;

		CGFloat u = mod(a*d1+sin(d*10.0+time), M_PI*2.0)/M_PI*0.5 - 0.5;
		CGFloat v = mod(pow(d0*4.0, 0.75),1.0) - 0.5;

		CGFloat dd = sqrt(u*u+v*v);

		CGFloat aa = atan(u, v);

		CGFloat uu = mod(aa*3.0+3.0*cos(dd*30.0-time), M_PI*2.0)/M_PI*0.5 - 0.5;
		// CGFloat vv = mod(dd*4.0,1.0) - 0.5;

		CGFloat d2 = sqrt(uu*uu+v*v)*1.5;

		gl_FragColor = vec4( hsvtorgb(dd+time*0.5/d1, sin(dd*time), d2), 1.0 );
		}

</script>

<script id="fragment-shader-3" type="x-shader/x-fragment">
		uniform vec2 resolution;
		uniform float time;

		vec2 rand(vec2 pos)
		{
		return fract( 0.00005 * (pow(pos+2.0, pos.yx + 1.0) * 22222.0));
		}
		vec2 rand2(vec2 pos)
		{
		return rand(rand(pos));
		}

		float softnoise(vec2 pos, float scale)
		{
		vec2 smplpos = pos * scale;
		float c0 = rand2((floor(smplpos) + vec2(0.0, 0.0)) / scale).x;
		float c1 = rand2((floor(smplpos) + vec2(1.0, 0.0)) / scale).x;
		float c2 = rand2((floor(smplpos) + vec2(0.0, 1.0)) / scale).x;
		float c3 = rand2((floor(smplpos) + vec2(1.0, 1.0)) / scale).x;

		vec2 a = fract(smplpos);
		return mix(
		mix(c0, c1, smoothstep(0.0, 1.0, a.x)),
		mix(c2, c3, smoothstep(0.0, 1.0, a.x)),
		smoothstep(0.0, 1.0, a.y));
		}

		void main(void)
		{
		vec2 pos = gl_FragCoord.xy / resolution.y;
		pos.x += time * 0.1;
		float color = 0.0;
		float s = 1.0;
		for(int i = 0; i < 8; i++)
		{
		color += softnoise(pos+vec2(i)*0.02, s * 4.0) / s / 2.0;
		s *= 2.0;
		}
		gl_FragColor = vec4(color);
		}

</script>

<script id="fragment-shader-4" type="x-shader/x-fragment">

		uniform float time;
		uniform vec2 resolution;

		vec2 rand(vec2 pos)
		{
		return
		fract(
		(
		pow(
		pos+2.0,
		pos.yx+2.0
		)*555555.0
		)
		);
		}

		vec2 rand2(vec2 pos)
		{
		return rand(rand(pos));
		}

		float softnoise(vec2 pos, float scale) {
		vec2 smplpos = pos * scale;
		float c0 = rand2((floor(smplpos) + vec2(0.0, 0.0)) / scale).x;
		float c1 = rand2((floor(smplpos) + vec2(1.0, 0.0)) / scale).x;
		float c2 = rand2((floor(smplpos) + vec2(0.0, 1.0)) / scale).x;
		float c3 = rand2((floor(smplpos) + vec2(1.0, 1.0)) / scale).x;

		vec2 a = fract(smplpos);
		return mix(mix(c0, c1, smoothstep(0.0, 1.0, a.x)),
		mix(c2, c3, smoothstep(0.0, 1.0, a.x)),
		smoothstep(0.0, 1.0, a.x));
		}

		void main( void ) {
		vec2 pos = gl_FragCoord.xy / resolution.y - time * 0.4;

		float color = 0.0;
		float s = 1.0;
		for (int i = 0; i < 6; ++i) {
		color += softnoise(pos + vec2(0.01 * float(i)), s * 4.0) / s / 2.0;
		s *= 2.0;
		}
		gl_FragColor = vec4(color,mix(color,cos(color),sin(color)),color,1);
		}

</script>

<script id="fragment-shader-5" type="x-shader/x-fragment">

		uniform float time;
		uniform vec2 resolution;

		// tie nd die by Snoep Games.

		void main( void ) {

		vec3 color = vec3(1.0, 0., 0.);
		vec2 pos = (( 1.4 * gl_FragCoord.xy - resolution.xy) / resolution.xx)*1.5;
		float r=sqrt(pos.x*pos.x+pos.y*pos.y)/15.0;
		float size1=2.0*cos(time/60.0);
		float size2=2.5*sin(time/12.1);

		float rot1=13.00; //82.0+16.0*sin(time/4.0);
		float rot2=-50.00; //82.0+16.0*sin(time/8.0);
		float t=sin(time);
		float a = (60.0)*sin(rot1*atan(pos.x-size1*pos.y/r,pos.y+size1*pos.x/r)+time);
		a += 200.0*acos(pos.x*2.0+cos(time/2.0))+asin(pos.y*5.0+sin(time/2.0));
		a=a*(r/50.0);
		a=200.0*sin(a*5.0)*(r/30.0);
		if(a>5.0) a=a/200.0;
		if(a<0.5) a=a*22.5;
		gl_FragColor = vec4( cos(a/20.0),a*cos(a/200.0),sin(a/8.0), 1.0 );
		}

</script>

<script id="fragment-shader-6" type="x-shader/x-fragment">

		uniform float time;
		uniform vec2 resolution;

		void main( void )
		{

		vec2 uPos = ( gl_FragCoord.xy / resolution.xy );//normalize wrt y axis
		//suPos -= vec2((resolution.x/resolution.y)/2.0, 0.0);//shift origin to center

		uPos.x -= 1.0;
		uPos.y -= 0.5;

		vec3 color = vec3(0.0);
		float vertColor = 2.0;
		for( float i = 0.0; i < 15.0; ++i )
		{
		float t = time * (0.9);

		uPos.y += sin( uPos.x*i + t+i/2.0 ) * 0.1;
		float fTemp = abs(1.0 / uPos.y / 100.0);
		vertColor += fTemp;
		color += vec3( fTemp*(10.0-i)/10.0, fTemp*i/10.0, pow(fTemp,1.5)*1.5 );
		}

		vec4 color_final = vec4(color, 1.0);
		gl_FragColor = color_final;
		}

</script>
<script>
function init() {

  // use the defaults
  var stats = initStats();
  var renderer = initRenderer();
  var camera = initCamera();
  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();

  var cubeGeometry = new THREE.BoxGeometry(20, 20, 20);

  var meshMaterial1 = createMaterial("vertex-shader",
    "fragment-shader-1");
  var meshMaterial2 = createMaterial("vertex-shader",
    "fragment-shader-2");
  var meshMaterial3 = createMaterial("vertex-shader",
    "fragment-shader-3");
  var meshMaterial4 = createMaterial("vertex-shader",
    "fragment-shader-4");
  var meshMaterial5 = createMaterial("vertex-shader",
    "fragment-shader-5");
  var meshMaterial6 = createMaterial("vertex-shader",
    "fragment-shader-6");

  var material = [meshMaterial1, meshMaterial2, meshMaterial3, meshMaterial4, meshMaterial5, meshMaterial6];
  var cube = new THREE.Mesh(cubeGeometry, material);

  // add the sphere to the scene
  scene.add(cube);

  // add subtle ambient lighting
  var ambientLight = new THREE.AmbientLight(0x0c0c0c);
  scene.add(ambientLight);

  // add spotlight for the shadows
  var spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(-40, 60, -10);
  spotLight.castShadow = true;
  scene.add(spotLight);

  // call the render function
  var step = 0;
  var oldContext = null;

  var controls = new function () {
    this.rotationSpeed = 0.02;
    this.bouncingSpeed = 0.03;

    this.opacity = meshMaterial1.opacity;
    this.transparent = meshMaterial1.transparent;

    this.visible = meshMaterial1.visible;
    this.side = "front";

    this.wireframe = meshMaterial1.wireframe;
    this.wireframeLinewidth = meshMaterial1.wireframeLinewidth;

    this.selectedMesh = "cube";

    this.shadow = "flat";

  };

  render();

  function render() {
    stats.update();

    cube.rotation.y = step += 0.01;
    cube.rotation.x = step;
    cube.rotation.z = step;

    cube.material.forEach(function (e) {
      e.uniforms.time.value += 0.01;
    });

    // render using requestAnimationFrame
    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }

  function createMaterial(vertexShader, fragmentShader) {
    var vertShader = document.getElementById(vertexShader).innerHTML;
    var fragShader = document.getElementById(fragmentShader).innerHTML;

    var attributes = {};
    var uniforms = {
      time: {
        type: 'f',
        value: 0.2
      },
      scale: {
        type: 'f',
        value: 0.2
      },
      alpha: {
        type: 'f',
        value: 0.6
      },
      resolution: {
        type: "v2",
        value: new THREE.Vector2()
      }
    };

    uniforms.resolution.value.x = window.innerWidth;
    uniforms.resolution.value.y = window.innerHeight;

    var meshMaterial = new THREE.ShaderMaterial({
      uniforms: uniforms,
      vertexShader: vertShader,
      fragmentShader: fragShader,
      transparent: true

    });

    return meshMaterial;
  }
}
</script>
```

### 线性几何体的材质

#### `THREE.LineBasicMaterial`

[在线示例 / Live demo](https://hicc.pro/assets/threejs/src/chapter-04/12-line-material.html)

```js
function init() {

  // use the defaults
  var stats = initStats();
  var renderer = initRenderer();
  var camera = initCamera();

  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();

  var stats = initStats();

  // add subtle ambient lighting
  var ambientLight = new THREE.AmbientLight(0x0c0c0c);
  scene.add(ambientLight);

  // add spotlight for the shadows
  var spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(-40, 60, -10);
  spotLight.castShadow = true;
  scene.add(spotLight);

  // get the turtle
  var points = gosper(4, 60);

  var lines = new THREE.Geometry();
  var colors = [];
  var i = 0;
  points.forEach(function (e) {
    lines.vertices.push(new THREE.Vector3(e.x, e.z, e.y));
    colors[i] = new THREE.Color(0xffffff);
    colors[i].setHSL(e.x / 100 + 0.5, (e.y * 20) / 300, 0.8);
    i++;
  });

  lines.colors = colors;
  var material = new THREE.LineBasicMaterial({
    opacity: 1.0,
    linewidth: 1,
    vertexColors: THREE.VertexColors
  });

  var line = new THREE.Line(lines, material);
  line.position.set(25, -30, -60);
  scene.add(line);

  // call the render function
  var step = 0;
  render();

  function render() {
    stats.update();
    line.rotation.z = step += 0.01;

    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }

  function gosper(a, b) {

    var turtle = [0, 0, 0];
    var points = [];
    var count = 0;

    rg(a, b, turtle);

    return points;

    function rt(x) {
      turtle[2] += x;
    }

    function lt(x) {
      turtle[2] -= x;
    }

    function fd(dist) {
      //                ctx.beginPath();
      points.push({
        x: turtle[0],
        y: turtle[1],
        z: Math.sin(count) * 5
      });
      //                ctx.moveTo(turtle[0], turtle[1]);

      var dir = turtle[2] * (Math.PI / 180);
      turtle[0] += Math.cos(dir) * dist;
      turtle[1] += Math.sin(dir) * dist;

      points.push({
        x: turtle[0],
        y: turtle[1],
        z: Math.sin(count) * 5
      });
      //                ctx.lineTo(turtle[0], turtle[1]);
      //                ctx.stroke();

    }

    function rg(st, ln, turtle) {

      st--;
      ln = ln / 2.6457;
      if (st > 0) {
        //                    ctx.strokeStyle = '#111';
        rg(st, ln, turtle);
        rt(60);
        gl(st, ln, turtle);
        rt(120);
        gl(st, ln, turtle);
        lt(60);
        rg(st, ln, turtle);
        lt(120);
        rg(st, ln, turtle);
        rg(st, ln, turtle);
        lt(60);
        gl(st, ln, turtle);
        rt(60);
      }
      if (st == 0) {
        fd(ln);
        rt(60);
        fd(ln);
        rt(120);
        fd(ln);
        lt(60);
        fd(ln);
        lt(120);
        fd(ln);
        fd(ln);
        lt(60);
        fd(ln);
        rt(60)
      }
    }

    function gl(st, ln, turtle) {
      st--;
      ln = ln / 2.6457;
      if (st > 0) {
        //                    ctx.strokeStyle = '#555';
        lt(60);
        rg(st, ln, turtle);
        rt(60);
        gl(st, ln, turtle);
        gl(st, ln, turtle);
        rt(120);
        gl(st, ln, turtle);
        rt(60);
        rg(st, ln, turtle);
        lt(120);
        rg(st, ln, turtle);
        lt(60);
        gl(st, ln, turtle);
      }
      if (st == 0) {
        lt(60);
        fd(ln);
        rt(60);
        fd(ln);
        fd(ln);
        rt(120);
        fd(ln);
        rt(60);
        fd(ln);
        lt(120);
        fd(ln);
        lt(60);
        fd(ln);
      }
    }
  }

}
```

#### `THREE.LineDashedMaterial`

[在线示例 / Live demo](https://hicc.pro/assets/threejs/src/chapter-04/13-line-material-dashed.html)

```js
function init() {

  // use the defaults
  var stats = initStats();
  var renderer = initRenderer();
  var camera = initCamera();

  // create a scene, that will hold all our elements such as objects, cameras and lights.
  var scene = new THREE.Scene();

  // add subtle ambient lighting
  var ambientLight = new THREE.AmbientLight(0x0c0c0c);
  scene.add(ambientLight);

  // add spotlight for the shadows
  var spotLight = new THREE.SpotLight(0xffffff);
  spotLight.position.set(-40, 60, -10);
  spotLight.castShadow = true;
  scene.add(spotLight);

  // get the turtle
  var points = gosper(4, 60);

  var lines = new THREE.Geometry();
  var colors = [];
  var i = 0;
  points.forEach(function (e) {
    lines.vertices.push(new THREE.Vector3(e.x, e.z, e.y));
    colors[i] = new THREE.Color(0xffffff);
    colors[i].setHSL(e.x / 100 + 0.5, (e.y * 20) / 300, 0.8);
    i++;
  });

  lines.colors = colors;
  var material = new THREE.LineDashedMaterial({
    vertexColors: true,
    color: 0xffffff,
    dashSize: 2,
    gapSize: 2,
    scale: 0.1
  });

  var line = new THREE.Line(lines, material);
  line.computeLineDistances();
  line.position.set(25, -30, -60);
  scene.add(line);

  // call the render function
  var step = 0;
  render();

  function render() {
    stats.update();
    line.rotation.z = step += 0.01;

    requestAnimationFrame(render);
    renderer.render(scene, camera);
  }

  function gosper(a, b) {

    var turtle = [0, 0, 0];
    var points = [];
    var count = 0;

    rg(a, b, turtle);

    return points;

    function rt(x) {
      turtle[2] += x;
    }

    function lt(x) {
      turtle[2] -= x;
    }

    function fd(dist) {
      //                ctx.beginPath();
      points.push({
        x: turtle[0],
        y: turtle[1],
        z: Math.sin(count) * 5
      });
      //                ctx.moveTo(turtle[0], turtle[1]);

      var dir = turtle[2] * (Math.PI / 180);
      turtle[0] += Math.cos(dir) * dist;
      turtle[1] += Math.sin(dir) * dist;

      points.push({
        x: turtle[0],
        y: turtle[1],
        z: Math.sin(count) * 5
      });
      //                ctx.lineTo(turtle[0], turtle[1]);
      //                ctx.stroke();

    }

    function rg(st, ln, turtle) {

      st--;
      ln = ln / 2.6457;
      if (st > 0) {
        //                    ctx.strokeStyle = '#111';
        rg(st, ln, turtle);
        rt(60);
        gl(st, ln, turtle);
        rt(120);
        gl(st, ln, turtle);
        lt(60);
        rg(st, ln, turtle);
        lt(120);
        rg(st, ln, turtle);
        rg(st, ln, turtle);
        lt(60);
        gl(st, ln, turtle);
        rt(60);
      }
      if (st == 0) {
        fd(ln);
        rt(60);
        fd(ln);
        rt(120);
        fd(ln);
        lt(60);
        fd(ln);
        lt(120);
        fd(ln);
        fd(ln);
        lt(60);
        fd(ln);
        rt(60)
      }
    }

    function gl(st, ln, turtle) {
      st--;
      ln = ln / 2.6457;
      if (st > 0) {
        //                    ctx.strokeStyle = '#555';
        lt(60);
        rg(st, ln, turtle);
        rt(60);
        gl(st, ln, turtle);
        gl(st, ln, turtle);
        rt(120);
        gl(st, ln, turtle);
        rt(60);
        rg(st, ln, turtle);
        lt(120);
        rg(st, ln, turtle);
        lt(60);
        gl(st, ln, turtle);
      }
      if (st == 0) {
        lt(60);
        fd(ln);
        rt(60);
        fd(ln);
        fd(ln);
        rt(120);
        fd(ln);
        rt(60);
        fd(ln);
        lt(120);
        fd(ln);
        lt(60);
        fd(ln);
      }
    }
  }

}
```

---

原文 / Source: <https://hicc.pro/p/three.js/threejs-material>
