基于物理的渲染和照明 
此笔记记录于DISCOVER three.js,大多数为其中的摘要,少数为笔者自己的理解
起步 
要打开物理上正确的照明,只需启用渲染器的 .physicallyCorrectLights设置:
function createRenderer() {
  const renderer = new WebGLRenderer();
  // turn on the physically correct lighting model
  renderer.physicallyCorrectLights = true;
  return renderer;
}默认情况下禁用此设置以保持向后兼容性。但是,打开它没有缺点,因此我们将始终启用它。
three.js 中的大小单位是米
使用米为单位是一种约定,而不是规则。如果您不遵循它,那么除了物理上精确的照明之外的一切都仍然有效。 事实上,在某些情况下使用不同的比例是有意义的。例如,如果您正在构建一个大规模的空间模拟,您可能会决定使用 1 单位=1000 公里。但是,如果您想要物理上准确的照明,那么您必须使用以下公式将场景构建到真实世界的规模:
three.js 中的光照 
- 直接照明:直接来自灯泡并撞击物体的光线。
- 间接照明:光线在击中物体之前已经从墙壁和房间内的其他物体反弹,每次反弹都会改变颜色并失去强度。
与这些相匹配,three.js 中的灯光类分为两种类型:
- 直接光照,模拟直接光照。
- 环境光,这是 一种 廉价且可信的间接照明方式。
间接照明很难模拟,因为这样做需要计算从场景中所有表面永远反射的无限数量的光线。没有足够强大的计算机来做到这一点,即使我们限制自己仅计算几千条光线,每条光线只产生几次反弹( 光线追踪 ),实时计算通常仍然需要很长时间。因此,如果我们想要场景中的真实光照,我们需要某种方式(环境光)来伪造间接光照。
three.js 核心中总共有四种直接光源类型可用,每一种都模拟一个常见的现实世界光源:
- DirectionalLight=> 阳光
- PointLight=> 灯泡
- RectAreaLight=> 条形照明或明亮的窗户
- SpotLight=> 聚光灯
默认下禁用阴影:
即使我们使用 PBR,现实世界和 three.js 之间的一个区别是默认情况下对象不会阻挡光线。光路径中的每个物体都会收到照明,即使路上有一堵墙。落在物体上的光会照亮它,但也会直接穿过并照亮后面的物体。物理正确性就这么多!
我们可以逐个对象的、逐个光照的手动启用阴影。但是,阴影很昂贵,因此我们通常只为一盏灯或两盏灯启用阴影,尤其是当我们的场景需要在移动设备上工作时。只有直接光类型可以投射阴影,环境光不能。
在代码中增加光源 
DirectionalLight设计的目的是模仿遥远的光源,例如太阳。来自DirectionalLight的光线不会随着距离而消失。场景中的所有对象都将被同样明亮地照亮,无论它们放在哪里——即使是在灯光后面。
创建components/light
import { DirectionalLight } from 'three';
function createLights() {
  // Create a directional light
  const light = new DirectionalLight('white', 8); // 颜色,强度
  // move the light right, up, and towards us
  light.position.set(10, 10, 10);
  return light;
}
export { createLights };所有 three.js 灯都有颜色和强度设置,继承自 Light基类。
World.js中导入并将其添加到场景中:
import { createCamera } from './components/camera.js';
import { createCube } from './components/cube.js';
import { createLights } from './components/lights.js';
import { createScene } from './components/scene.js';
import { createRenderer } from './systems/renderer.js';
import { Resizer } from './systems/Resizer.js';
let scene;
let camera;
let renderer;
class World {
  constructor(container) {
    camera = createCamera();
    scene = createScene();
    renderer = createRenderer();
    container.append(renderer.domElement);
    const cube = createCube();
    const light = createLights();
    scene.add(cube, light);
    const resizer = new Resizer(container, camera, renderer);
  }
  render() {
    // draw a single frame
    renderer.render(scene, camera);
  }
}
export { World };材质: MeshStandardMaterial 
添加灯光不会有任何立竿见影的效果,因为我们目前使用的是MeshBasicMaterial。 正如我们前面提到的,这种材质会忽略场景中的任何灯光。
- 我们将用 MeshStandardMaterial代替基本材料MeshBasicMaterial。这是一种高质量、通用、物理精确的材料,可以使用真实世界的物理方程对光做出反应。
- 顾名思义,MeshStandardMaterial应该是几乎所有情况下的首选“标准”材料。通过添加精心制作的纹理,我们可以使用MeshStandardMaterial重建几乎任何常见的表面。
如果你在上面两个场景中打开 Material 菜单,你会看到这两种材质有很多相同的设置,比如透明(材质是否透明)、不透明度(透明程度)、可见(true/false 显示/隐藏材质),等等。这样做的原因是这两种材料,实际上,所有的 three.js 材料,都继承自 Material基类。你不能直接使用 Material。相反,您必须始终使用它的派生类中的某一个,例如MeshStandardMaterial或者MeshBasicMaterial。
在代码中切换材质 
cuebe.js
import { BoxBufferGeometry, Mesh, MeshStandardMaterial } from 'three';
function createCube() {
  const geometry = new BoxBufferGeometry(2, 2, 2);
  // Switch the old "basic" material to
  // a physically correct "standard" material
  const material = new MeshStandardMaterial({ color: 'purple' });
  const cube = new Mesh(geometry, material);
  cube.rotation.set(-0.5, -0.1, 0.8); // 旋转
  return cube;
}
export { createCube };