THREE.js 光照- 2

42 阅读3分钟

前置补充

在前文的基础代码之上, 又新增了些代码


  // 弧度转度数
  class DegRadHelper {
    constructor(obj, prop) {
      this.obj = obj;
      this.prop = prop;
    }
    get value() {
      return THREE.MathUtils.radToDeg(this.obj[this.prop]);
    }
    set value(v) {
      this.obj[this.prop] = THREE.MathUtils.degToRad(v);
    }
  }

  // gui 快捷绑定对象的 x y z属性
  function makeXYZGUI(gui, vector3, name, onChangeFn) {
    const folder = gui.addFolder(name);
    folder.add(vector3, "x", -10, 10).onChange(onChangeFn);
    folder.add(vector3, "y", 0, 10).onChange(onChangeFn);
    folder.add(vector3, "z", -10, 10).onChange(onChangeFn);
    folder.open();
  }

PointLight 点光源

点光源, 表示从一个点朝向各个方向发射出光线的一种光照效果

常用于模拟灯泡灯光, 有阴影

  const pointColor = 0xffffff;
  const pointerIntensity = 150;
  const pointLight = new THREE.PointLight(pointColor, pointerIntensity);
  pointLight.position.set(0, 10, 0);
  scene.add(pointLight);

  // 点光源辅助器
  const pointLightHelper = new THREE.PointLightHelper(pointLight);
  scene.add(pointLightHelper);
  
  function updatePointLight() {
   // 因为点光源没有 `target` 属性,所以 `onChange` 函数可以简化。
    pointLightHelper.update();
  }

  // 添加 gui 属性控制
  const pointLightFolder = gui.addFolder("点光源控制");
  gui.addColor(new ColorGUIHelper(pointLight, "color"), "value").name("color");
  gui.add(pointLight, "intensity", 0, 250, 1);
  gui.add(pointLight, "distance", 0, 40).onChange(updatePointLight);
  makeXYZGUI(pointLightFolder, pointLight.position, "position", updatePointLight);
  

image.png


SpotLight聚光灯

聚光灯光源, 可以看做是一个点光源被一个圆锥体限制住了光照范围

但实际有两个圆锥, 内圆锥和外圆锥, 光照强度在两个锥体间从设定的强度递减到 0

类似方向光一样需要一个目标点, 光源位置是圆锥的顶点, 目标点处于圆锥的中轴线上

常用于聚光灯、手电筒灯光, 有阴影

  const spotColor = 0xffffff;
  const spotIntensity = 150;
  const spotLight = new THREE.SpotLight(spotColor, spotIntensity);
  spotLight.position.set(0, 10, 0);
  spotLight.angle = 10; // 聚光灯照射角度,单位:弧度
  scene.add(spotLight);
  // 要将目标位置更改为默认值以外的任何位置,必须将其添加到场景中。
  scene.add(spotLight.target);

  const spotHelper = new THREE.SpotLightHelper(spotLight);
  scene.add(spotHelper);
  
  function updateSpotLight() {
    spotHelper.update();
  }
    
   // 添加 gui 控制
  const spotLightFolder = gui.addFolder("聚光灯控制");
  // 聚光灯的圆锥顶部大小通过 angle 属性控制
  // 以弧度作单位
  spotLightFolder
    .add(new DegRadHelper(spotLight, "angle"), "value", 0, 90)
    .name("angle")
    .onChange(updateSpotLight);
    
  // penumbra为 0 时, 内外圆锥大小一致
  // penumbra为 1 时, 内圆锥大小为 0, 光照强度从中轴线开始向外递减
  // penumbra 为 0.5 时,光照强度从外圆锥半径的中点处开始往外递减
  // 具体表现为聚光灯打向的位置的光是锐利还是模糊
  spotLightFolder.add(spotLight, "penumbra", 0, 1, 0.01).onChange(updateSpotLight);
  makeXYZGUI(spotLightFolder, spotLight.position, "position", updateSpotLight);
  

image.png

image.png


RectAreaLight 矩阵区域光源

矩形区域光源, 表示一个矩形区域发射出来的光照

例如长条日光灯或天花磨砂玻璃透进来的自然光

只能影响MeshStandardMaterialMeshPhysicalMaterial

// 由于光照计算复杂, 将其着色分离到单独库中, 需要手动引入 RectAreaLightUniformsLib 和 RectAreaLightHelper
import { RectAreaLightUniformsLib } from "three/addons/lights/RectAreaLightUniformsLib.js";
import { RectAreaLightHelper } from "three/addons/helpers/RectAreaLightHelper.js";
// 初始化
RectAreaLightUniformsLib.init();


const rectColor = 0xffffff;
const rectIntensity = 5;
const rectWidth = 10;
const rectHeight = 4;
const rectLight = new THREE.RectAreaLight(rectColor, rectIntensity, rectWidth, rectHeight);
rectLight.position.set(0, 10, 0);

// 与其他光线不同, rectAreaLight 不是使用目标点(`target`),而是使用自身的旋转角度来确定光照方向。
rectLight.rotation.x = THREE.MathUtils.degToRad(-90);
scene.add(rectLight);

// 另外,矩形光的辅助对象应该添加为光照的子节点,而不是添加为场景的子节点。
const rectHelper = new RectAreaLightHelper(rectLight);
rectLight.add(rectHelper);


// gui 属性控制
function updateRectLight() {
    rectLight.updateMatrixWorld();
  }
  const rectLightFolder = gui.addFolder("矩形区域光控制");
  rectLightFolder.addColor(new ColorGUIHelper(rectLight, "color"), "value").name("color");
  rectLightFolder.add(rectLight, "intensity", 0, 10).onChange(updateRectLight);
  rectLightFolder.add(rectLight, "width", 0, 20).onChange(updateRectLight);
  rectLightFolder.add(rectLight, "height", 0, 20).onChange(updateRectLight);
  makeXYZGUI(rectLightFolder, rectLight.position, "position", updateRectLight);
  makeXYZGUI(rectLightFolder, rectLight.rotation, "rotation", updateRectLight);

image.png