经纬志
首页 / three.js / 教程

材质与光照

先看结论

材质描述表面如何与光交互,灯光提供光。地形有没有立体感、数据分层的颜色准不准、 模型会不会发灰,基本都由这两组参数决定。

最后验证 2026-11-03 · three.js 0.160.0 · 更新 2026-10-27 · 引用本文

材质从简单到复杂

材质受光适用场景代价
MeshBasicMaterial 否,颜色固定 标注点、辅助线、背景面、不需要立体感的图层 最低
MeshLambertMaterial 漫反射 大范围地形、粗略建筑体块 低
MeshPhongMaterial 漫反射 + 高光 需要明确高光但不在意物理正确性的物体 中
MeshStandardMaterial PBR 默认选择:建筑、地形、设备模型 中
MeshPhysicalMaterial PBR + 清漆、透射、折射 玻璃、水面、需要清漆层的展示模型 较高
PointsMaterial / LineBasicMaterial 点与线专用 点云、轨迹、经纬网 低

一个常见误区是给地形用 MeshBasicMaterial:颜色是准的,但没有明暗, 起伏完全看不出来。地形应当用受光材质,再配合方向光。

PBR 的两个核心参数

MeshStandardMaterial 用 roughness 与 metalness 描述表面。前者控制反射的模糊程度,后者控制有多少反射被当作金属反射。 非金属物体的 metalness 通常接近 0;把建筑写成 1,会得到一块像金属板的立面。

const material = new THREE.MeshStandardMaterial({
  color: 0x9aa7b4,
  roughness: 0.85,   // 粗糙,接近混凝土
  metalness: 0.05,   // 非金属
  map: colorTexture, // 基础色贴图
  normalMap: normalTexture,
  side: THREE.FrontSide
});

金属材质需要环境贴图才有反射内容。没有环境贴图时,metalness 越高画面越暗, 因为金属没有漫反射,而可反射的环境是空的。展示类场景可以用 PMREMGenerator 从一张 HDR 或程序生成的天空生成环境贴图。

光源类型

光源行为典型用途
AmbientLight均匀照亮所有表面,无方向补暗部,避免阴影死黑
HemisphereLight天空色与地面色从上到下渐变室外场景的基础环境光
DirectionalLight平行光,只有方向没有位置衰减太阳光、地形主光
PointLight点光源,随距离衰减路灯、室内补光
SpotLight锥形光,有角度与衰减聚光、夜间照明
RectAreaLight面光源,只影响 Standard/Physical柔和的展示照明

从 r155 起,点光源与聚光灯的 intensity 采用物理单位,默认 decay = 2,亮度随距离平方衰减。同样是「1」,平行光看起来正常, 点光源却几乎看不见,这是单位差异,不是代码错了。室内点光源常见取值在几十到几百之间。

一组能用的室外布光

// 天空与地面反射,提供基础亮度
scene.add(new THREE.HemisphereLight(0xcfe4ff, 0x1a222c, 1.0));

// 主光:太阳,负责明暗关系与阴影
const sun = new THREE.DirectionalLight(0xfff0d8, 2.4);
sun.position.set(8, 14, 6);
sun.castShadow = true;
sun.shadow.mapSize.set(2048, 2048);
sun.shadow.camera.left = -30;
sun.shadow.camera.right = 30;
sun.shadow.camera.top = 30;
sun.shadow.camera.bottom = -30;
sun.shadow.camera.near = 1;
sun.shadow.camera.far = 80;
sun.shadow.bias = -0.0005;
scene.add(sun);

// 补光:压住主光背后的死黑,强度要低
const fill = new THREE.DirectionalLight(0x8fb4ff, 0.6);
fill.position.set(-10, 6, -8);
scene.add(fill);

阴影的代价与边界

阴影会把场景从主光视角再渲染一遍到深度纹理。对象越多、贴图越大,开销越高。 平行光的阴影相机范围要贴着实际场景设置,范围开得越大,同样分辨率下每个像素覆盖的地面越宽, 阴影边缘越糊,还会出现锯齿。

静态场景可以关闭阴影自动更新:renderer.shadowMap.autoUpdate = false, 需要时再置为 true 并渲染一帧。城市级三维场景里,这能省下可观的每帧开销。

阴影不出现

依次检查:渲染器 shadowMap.enabled 是否为 true;投光物体 castShadow 是否为 true;受光物体 receiveShadow 是否为 true;光源的 castShadow 是否开启;阴影相机的范围是否覆盖了物体。任意一项没开,阴影都不会出现。

颜色空间

颜色贴图要标成 sRGB,数据贴图(法线、粗糙度、金属度、位移)保持线性。 把法线贴图也标成 sRGB,光照方向会被错误解释,表面细节看起来发脏。 渲染器的输出统一为 SRGBColorSpace,这条链路才闭合。

先定光照,再调颜色。同一份颜色在不同光照下完全不是同一种观感。 做专题图时,最好固定一套主光方向与强度,再按数据的视觉层级调材质颜色, 否则换一个场景就要重新配色。

引用本文

经纬志(2026)。《材质与光照》。https://www.jingweizhi.com/three/tutorial/materials-lights.html(访问日期 2026-11-03)。