前置补充
在前文的基础代码之上, 又新增了些代码
// 弧度转度数
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);
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);
RectAreaLight 矩阵区域光源
矩形区域光源, 表示一个矩形区域发射出来的光照
例如长条日光灯或天花磨砂玻璃透进来的自然光
只能影响MeshStandardMaterial 和 MeshPhysicalMaterial
// 由于光照计算复杂, 将其着色分离到单独库中, 需要手动引入 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);