首页 / Three.js 入门教程 / 基础材质四件套

Three.js 入门教程

基础材质四件套

本教程共 40 篇 · 第 13 篇 · 更新于 2026-08-14 · 约 7 分钟阅读

Three.js材质MeshBasicMaterialMeshPhongMaterial光照

本节目标:学会 MeshBasicMaterial、MeshNormalMaterial、MeshLambertMaterial、MeshPhongMaterial 四种基础材质的特性与用途,知道它们各自需要什么、适合什么。

为什么还要学基础材质

PBR 材质(第 14 章)是主流,但四种基础材质仍值得学。Basic 和 Normal 不依赖灯光,是调试利器;Lambert 和 Phong 性能好、上手快,在低端设备和老项目里还很常见。更重要的是,它们把光照模型的演进讲得很清楚:不受光、只受漫反射、再加上高光。理解这条线,PBR 就只是”更精细的 Phong”。

MeshBasicMaterial:完全不受光的纯色

最基础的材质。它把颜色原样画在表面,完全忽略场景里所有灯光,没有明暗变化,物体看起来像剪影或扁平图标。

const material = new THREE.MeshBasicMaterial({ color: 0x44aaff });

特点:

  1. 不需要任何灯光,场景没灯也能看见;
  2. 没有光影变化,立体感弱,转起来才看得出是 3D;
  3. 配合 map 纹理可以做纯纹理展示、2D 风格化效果。

用途:调试(快速看几何体形状)、加载完成前的占位、扁平风格场景。 调试时它还有个妙用:配合 wireframe: true 看三角形布线,配合半透明看模型内部结构。因为不受光,用 Basic 做调试能排除”灯光问题”这个变量。

MeshNormalMaterial:法线当颜色

它的颜色不是设置的,而是由表面法线方向算出来的:法线的 x、y、z 分量映射成 R、G、B。默认场景里,物体呈现五彩斑斓的”彩虹”效果。

const material = new THREE.MeshNormalMaterial();

特点:

  1. 不需要灯光;
  2. 没有 color 属性,颜色由法线方向决定;
  3. 法线是光照计算的依据,所以它是检查法线是否正确的最好工具——法线错了,颜色分布会立刻露出破绽。

用途:调试法线、快速预览几何体、做”科技感”装饰。 它同样支持 flatShadingwireframe,组合起来能快速判断一个模型”形状对不对、面朝向对不对”。第 15 章之后,还可以给标准材质挂法线贴图,Normal 材质是理解法线贴图的基础。

MeshLambertMaterial:哑光漫反射

第一个”正经”受光材质。它只计算漫反射:光从哪个方向来,表面就朝哪个方向亮。效果是哑光、不反光,像磨砂塑料、粉笔、墙面。

const material = new THREE.MeshLambertMaterial({ color: 0x44aaff });

注意它需要灯光,没灯就全黑。它支持 emissive 自发光和 map 纹理,但没有高光——表面永远不会出现”亮点”。 自发光用来模拟”自己会发光但又不照亮别人”的表面,比如屏幕、灯箱:

const material = new THREE.MeshLambertMaterial({
  color: 0x333333,
  emissive: 0xff6600,       // 自发光颜色
  emissiveIntensity: 0.8,   // 自发光强度,默认 1
});

特点:

  1. 计算量小,性能好;
  2. 适合哑光物体:木材、石头、粗糙塑料;
  3. 无法表现镜面反射。

MeshPhongMaterial:加上高光的 Lambert

在 Lambert 的基础上加了镜面高光(specular)。高光区域由 shininess 控制:值越大,高光点越小越锐利。

const material = new THREE.MeshPhongMaterial({
  color: 0x44aaff,
  shininess: 60,      // 高光锐利程度,默认 30
  specular: 0xffffff, // 高光颜色,默认 0x111111(很暗)
  emissive: 0x000000, // 自发光颜色,默认黑
});

特点:

  1. 需要灯光;
  2. 能表现塑料、瓷器、湿润表面等带高光的物体;
  3. specular 默认是很暗的 0x111111,想看到明显高光记得调亮它。 shininessspecular 的分工要分清:shininess 只控制高光点的锐利程度(30 是柔和的大光斑,100 以上是锐利的小亮点),specular 控制高光的颜色和明暗。两者配合,能调出从塑料到瓷器的各种质感。
Tip

判断”该用 Lambert 还是 Phong”:物体表面会反光(哪怕一点点)就用 Phong,纯哑光用 Lambert。

场景选型:四件套怎么用

把材质和常见需求对上号:

  1. 木头、石头、粗糙塑料这类哑光物体,用 Lambert,性能最好;
  2. 塑料玩具、瓷器、湿润的石头这类带高光的物体,用 Phong;
  3. 扁平 UI 风格、调试占位,用 Basic,完全不受光影响;
  4. 需要检查模型法线是否正常,用 Normal,颜色分布会直接暴露问题;
  5. 需要纹理贴图又有真实光影,Lambert 和 Phong 都支持 map,看需不需要高光来选。

性能排序(从快到慢):Basic ≈ Normal < Lambert < Phong。老设备或大量物体同屏时,优先选靠前的材质。 为什么性能有差别?Lambert 每帧要算一次漫反射光照,Phong 还要多算一次高光,Basic 和 Normal 根本不算光照。物体数量上万个时,这点差距会放大成帧率的明显差异。当然,追求真实感时性能差一点是值得的,这正是 PBR 材质存在的意义。

四件套对比

材质需要灯光明暗变化高光性能典型用途
MeshBasicMaterial最快调试、扁平风格、占位
MeshNormalMaterial有(法线着色)调试法线、预览
MeshLambertMaterial漫反射哑光物体
MeshPhongMaterial漫反射较快带高光的物体

完整示例:四个球

一个页面四个球,分别用四种材质,转起来对比。保存成 HTML 直接运行: 从左到右依次是 Basic、Normal、Lambert、Phong。运行后重点观察两点:前两个球不管怎么转,亮度都不变(它们不算光照);第四个球表面有一个移动的亮斑(Phong 的高光),第三个球没有。这个亮斑就是”高光”两个字的具体含义。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <title>基础材质四件套</title>
  <style>
    html, body { margin: 0; height: 100%; }
  </style>
</head>
<body>
  <script type="importmap">
  {
    "imports": {
      "three": "https://cdn.jsdelivr.net/npm/three@0.185.0/build/three.module.js",
      "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.185.0/examples/jsm/"
    }
  }
  </script>

  <script type="module">
    import * as THREE from 'three';
    import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 0.1, 100);
    camera.position.set(3, 2.5, 6);
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(innerWidth, innerHeight);
    document.body.appendChild(renderer.domElement);

    // 灯光:Lambert 和 Phong 需要它,Basic 和 Normal 无视它
    scene.add(new THREE.AmbientLight(0xffffff, 3));
    const dirLight = new THREE.DirectionalLight(0xffffff, 3);
    dirLight.position.set(3, 6, 4);
    scene.add(dirLight);

    const controls = new OrbitControls(camera, renderer.domElement);

    // 四种材质,四个球
    const materials = [
      new THREE.MeshBasicMaterial({ color: 0x44aaff }),
      new THREE.MeshNormalMaterial(),
      new THREE.MeshLambertMaterial({ color: 0x44aaff }),
      new THREE.MeshPhongMaterial({
        color: 0x44aaff,
        shininess: 60,
        specular: 0xffffff,
      }),
    ];

    materials.forEach((material, i) => {
      const sphere = new THREE.Mesh(new THREE.SphereGeometry(0.9, 32, 16), material);
      sphere.position.x = (i - 1.5) * 2;
      scene.add(sphere);
    });

    function animate() {
      requestAnimationFrame(animate);
      scene.children.forEach((obj) => {
        if (obj.isMesh) obj.rotation.y += 0.01;
      });
      controls.update();
      renderer.render(scene, camera);
    }
    animate();

    addEventListener('resize', () => {
      camera.aspect = innerWidth / innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(innerWidth, innerHeight);
    });
  </script>
</body>
</html>
Note

把示例里的灯光注释掉再看:Basic 和 Normal 的球依然可见,Lambert 和 Phong 的球全黑。这就是”谁依赖灯光”最直观的演示。

常见问题

  1. Lambert、Phong 的物体全黑:没加灯。这两种材质必须依赖灯光,场景里至少要有一盏灯。
  2. Basic 材质看着像纸片:这是它的本性——不受光、没有明暗。想要立体感就换受光材质。
  3. Normal 材质想改颜色改不了:它的颜色由法线方向决定,没有 color 属性。想自定义颜色就换别的材质。
  4. Phong 高光不明显specular 默认是 0x111111(很暗),调亮到 0xffffff 再配合 shininess 看效果。
  5. 低多边形物体棱角太生硬:试试 flatShading: true,切面风格有时比平滑着色更好看。

小结

四种基础材质构成一条演进线:Basic 完全不受光,Normal 用法线着色,Lambert 加上漫反射,Phong 再加高光。它们覆盖了”不需要光”和”需要光但很便宜”的场景。想要更真实、更物理的质感,下一章进入 PBR 材质。