【Three.js】day101-lighting-materials

作者:mario 发布时间: 2026-09-07 阅读量:5 评论数:0

Day 101 · 光照与材质 — 让工业场景有质感

Day 99 第一天就踩过"全黑"的坑:Standard 材质没有灯就是黑。今天正面解决它——光照与材质是整个 3D"质感"的来源。你要掌握:四类常用材质各自什么时候用、四类光源各自长什么样、以及最重要的匹配原则(什么材质配什么光)。最后用 Day 66 的工业 token 纪律给场景上色,让立方体从"塑料玩具"变成"工业设备"。


目录


一、光照模型:光是怎么让物体"看见"的

1.1 三个要素

一个物体最终呈现的颜色 ≈ 光源 × 材质 × 表面朝向

最终颜色 ≈ 光的颜色/强度 × 材质的吸收/反射属性 × 表面法线朝向光源的程度
  • 光源:提供"光"(颜色、强度、方向)

  • 材质:决定表面"怎么处理光"(吸收哪些、反射哪些、高光多亮)

  • 法线:表面朝向——面朝灯就亮,背朝灯就暗(这就是"立体感"的来源)

记忆钩子:没有光源,Standard 材质一片黑;没有材质,光无处着落。两者必须同时在场,这就是 Day 99 三步排查法的第 2 步。

1.2 从"有没有立体感"看光照的意义

Day 99 只有一盏 AmbientLight——立方体是"一片均匀的亮",看不出棱角(环境光没有方向,四面等亮)。加一盏 DirectionalLight 后,朝灯的面亮、背灯的面暗——立体感立刻出现。今天的目标就是让场景"有明暗、有层次"。


二、材质:物体表面"吃什么光"

2.1 四种常用材质对比

材质

是否需要光

表现

用在哪

性能

MeshBasicMaterial

❌ 不需要

纯色/贴图,无明暗

辅助线、文字、警示框、特殊高亮

最便宜

MeshLambertMaterial

✅ 需要

漫反射,哑光,无高光

远处/次要物体(省算力)

便宜

MeshPhongMaterial

✅ 需要

漫反射 + 高光(shininess)

塑料/金属质感、可调高光

中等

MeshStandardMaterial

✅ 需要

PBR 物理材质,最真实

工业设备主力(金属度/粗糙度)

最贵

2.2 MeshStandardMaterial 的两个关键属性

PBR 材质用两个旋钮描述"材质是金属还是塑料":

const material = new THREE.MeshStandardMaterial({
  color: 0x00c6ff,          // 基础色(Day 66 token)
  metalness: 0.6,           // 金属度 0~1:0 塑料 / 1 金属
  roughness: 0.4,           // 粗糙度 0~1:0 镜面抛光 / 1 磨砂
});
  • metalness = 1 + roughness = 0 → 镜面金属(反光强烈,环境要求高)

  • metalness = 0 + roughness = 1 → 粗糙哑光(吸收光,不易出效果)

  • 工业场景常用折中:metalness 0.3~0.8roughness 0.3~0.6

现在就能解释 Day 99 的"全黑":Standard 材质要算光,而光没配齐。材质要效果,先问自己配了什么灯


三、光源:四种灯怎么选

3.1 四种光源对比

光源

方向性

衰减

用在哪

性能

AmbientLight 环境光

无方向,全场景均匀

兜底:保证最暗处也能看见

最便宜

DirectionalLight 方向光

有方向,平行光

主光:模拟太阳/顶灯,工业场景主力

便宜

PointLight 点光源

从一点向四周

局部补光:台灯/指示灯

中等

SpotLight 聚光灯

锥形光束

强调单个物体/舞台效果

较贵

3.2 工业场景的"三灯法则"

不是灯越多越好——每多一盏灯 = 每帧多一次逐像素计算。一套够用的默认组合:

// 三灯法则:兜底 + 主光 + 补光(照抄这个组合,再按需调整)
scene.add(new THREE.AmbientLight(0xffffff, 0.4));        // ① 兜底:让暗处可辨
const sun = new THREE.DirectionalLight(0xffffff, 1.2);   // ② 主光:给立体感
sun.position.set(5, 8, 4);                               //    主光从斜上方来
scene.add(sun);
// ③ 补光(可选):PointLight 点亮局部,或用另一盏 DirectionalLight 提亮背光面

方向光的诀窍position 决定了光照方向(从该点射向原点),所以方向光也要摆位置——摆歪了立体感就歪。

3.3 主光的立体感设置

// 主光常见参数:给阴影做准备(Day 106+ 阴影篇再深入)
const sun = new THREE.DirectionalLight(0xffffff, 1.2);
sun.position.set(5, 8, 4);      // 斜上方 45° 方向,立体感最佳
scene.add(sun);

四、匹配原则:材质 × 光的组合表

这是今天最核心的交付物——照这张表配,就不会再全黑/过曝/死板:

材质

建议配光

备注

MeshBasicMaterial

无需光

想要"自发光"效果时用(省一盏灯)

MeshLambertMaterial

Ambient + Directional

远处物体省钱方案

MeshPhongMaterial

Ambient + Directional + 可选 Point

需要高光时

MeshStandardMaterial

三灯法则(兜底+主光+补光)

工业主力,最吃光

排查口诀(第 2 步的完整版):先看材质要不要光 → 要光就查灯齐不齐 → 灯齐还黑就看强度和位置


五、工业配色纪律:Day 66 token 的 3D 版

5.1 从 token 到 3D 材质

第 3 个月的 industrial-dark token 直接搬进 3D——视觉语言要跨 2D/3D 一致(Day 105 大屏与 3D 同框时配色不打架):

// src/core/three/theme.ts:3D 版 token(与 Day 66 industrial-dark 对齐)
export const threeTheme = {
  bg:      0x0d1421,   // 场景底色 = 大屏底色
  primary: 0x00c6ff,   // 主色(青蓝)
  accent:  0xff6b35,   // 强调色(橙)——告警/关键设备
  success: 0x00e676,   // 运行正常(绿)
  danger:  0xff3b30,   // 告警(红)
  panel:   0x16233a,   // 次级面板(深蓝灰)
} as const;

5.2 一句话配色纪律

大面积用 panel/bg(深色打底),主设备用 primary,状态色(绿/红)只给"有语义"的物体——和 2D 大屏完全一致:颜色是语义,不是装饰。


六、完整示例:一个有质感的工业设备

组装今天全部内容——一个"金属质感 + 状态指示灯"的设备:

// src/core/three/build-device.ts
import * as THREE from "three";
import { threeTheme as T } from "./theme";

/**
 * 构建一个"工业设备":金属机身 + 指示灯 + 三灯照明
 * 返回 device 与 lights,Day 102 会把它升级为可复用的 Group
 */
export function buildDevice(scene: THREE.Scene): {
  device: THREE.Mesh;
  setStatus: (s: "normal" | "alarm") => void;
} {
  // —— 灯光:三灯法则(每场景配一次,不是每物体一次)——
  scene.add(new THREE.AmbientLight(0xffffff, 0.4));                 // 兜底
  const sun = new THREE.DirectionalLight(0xffffff, 1.2);            // 主光
  sun.position.set(5, 8, 4);
  scene.add(sun);

  // —— 机身:金属质感(Standard + metalness/roughness)——
  const bodyMat = new THREE.MeshStandardMaterial({
    color: T.panel,
    metalness: 0.7,
    roughness: 0.4,
  });
  const body = new THREE.Mesh(new THREE.BoxGeometry(2, 1, 1), bodyMat);
  body.position.y = 1;

  // —— 指示灯:自发光质感(Basic 不需要光 = 恒亮)——
  const lightMat = new THREE.MeshBasicMaterial({ color: T.success });
  const indicator = new THREE.Mesh(new THREE.SphereGeometry(0.15, 16, 16), lightMat);
  indicator.position.set(0.8, 1.45, 0);

  // 组合:设备 = 机身 + 指示灯(Day 102 升级为 Group 层级)
  const device = new THREE.Group();
  device.add(body, indicator);
  scene.add(device);

  return {
    device,
    // 状态切换:改指示灯颜色(告警红 / 正常绿)——Day 104 交互的雏形
    setStatus(s) {
      lightMat.color.setHex(s === "alarm" ? T.danger : T.success);
    },
  };
}

观察点:MeshBasicMaterial 指示灯永远亮(不吃光、不衰减)——这是"指示灯/警示框"用 Basic 的根本原因:要恒亮就用 Basic,要真实感就用 Standard


七、对照表第 2 行:手写 WebGL vs Three.js(光照)

环节

手写 WebGL

Three.js

渲染循环

rAF + 手动矩阵 + draw

renderer.render(scene, camera)

光照

手写着色器(fragment shader 里手算 Phong/Blinn 光照方程),每个光源手动循环

new DirectionalLight(...) 改属性,内部自动生成着色器代码

材质

手写 uniform + 光照方程分支

换一个 MeshStandardMaterial 类即可

结论:光照是"手写 WebGL 成本飙升"的第一个分水岭——真正的光照方程(Phong 反射模型:环境 + 漫反射 + 高光三部分)你自己写要一整天,Three.js 封装成一盏灯。知道它替你做了什么,就够了(面试被追问"Phong 模型三要素"能答出:环境光 + 漫反射 + 镜面高光)。


八、常见坑点

坑 1:材质要光,但只配了环境光

症状:物体"平"、没有立体感。原因:AmbientLight 无方向,四面等亮。修法:加一盏 DirectionalLight 当主光

坑 2:方向光忘了摆 position

症状:物体一侧奇亮/奇暗。原因:方向光默认从原点(0,0,0)射向原点——位置没设,方向随机。修法:sun.position.set(x,y,z)

坑 3:每物体都加一套灯

症状:性能骤降(多光源逐像素叠加)。原因:把"每物体配灯"当成了习惯。修法:三灯法则——灯是场景级的,配一次全局复用

坑 4:Basic 材质想要"被光照"

症状:Basic 物体怎么调 metalness 都没反应。原因:Basic 根本不参与光照计算。修法:要光影就用 Standard,要恒亮就用 Basic,别混

坑 5:金属度拉满后"太刺眼/全黑"

症状:metalness=1 的物体在弱光下发黑。原因:金属反射强,环境不足时反而显得暗。修法:环境贴图(Week 16)或降低 metalness 到 0.6~0.8


九、自测挑战

T1 · 三灯场景(60 分钟)

搭一个场景:主光从斜上方 45° 照一个 metalness=0.7 的金属立方体,环境光兜底。截三张图对比:只有环境光 / 只有方向光 / 两者都有——亲眼看"立体感从哪来"

T2 · 材质四连(40 分钟)

同一个立方体,依次换成 Basic/Lambert/Phong/Standard,各截一张图并记录:哪个最亮、哪个最有立体感、哪个最贵(渲染成本猜一下)。四张图钉进笔记,面试"材质怎么选"直接看表

T3 · 指示灯(30 分钟)

做第 6 节的设备,实现 setStatus("alarm") 时指示灯变红。给 setStatus 加一个基于 getElapsedTime() 的闪烁动画(visible = sin(t) > 0),模拟告警灯——今天顺带预习 Day 104 的"状态驱动视觉"。

T4 · 配色对照(20 分钟)

用 Day 66 的 token 给 3D 场景上色,与 2D 大屏截图放一起对比——确认 2D/3D 视觉语言一致(这是 Day 105 的验收项之一)。


十、总结

环节

要点

光照模型

最终颜色 ≈ 光 × 材质 × 法线朝向

材质

Basic 恒亮 / Lambert 哑光 / Phong 高光 / Standard PBR(工业主力)

光源

三灯法则:Ambient 兜底 + Directional 主光 + Point/补光

匹配原则

先看材质要不要光 → 查灯齐不齐 → 查强度位置

工业配色

Day 66 token 直搬 3D:颜色是语义,不是装饰

对照表

第 2 行:光照封装成本最高——Phong 模型三要素要能讲

金属设备有了立体感、指示灯会随状态变红。明天给设备装上更多零件:几何体与 Group 层级变换。


明日预告:Day 102 几何与变换——常用几何体清单、position/rotation/scale 三变换、以及 Group 怎么把"机身+指示灯+管道"组装成一台可以整体移动的设备。

评论