在 Three.js 中,SphericalHarmonics3 是一个用于处理三阶球谐函数 (Spherical Harmonics) 的数学类。球谐函数在计算机图形学中广泛应用,尤其是在全局光照和光源传递的模拟中,球谐函数可以有效压缩和表示复杂的光照环境。
SphericalHarmonics3 有两个属性十三个方法
假设你有一个户外场景,天空会向下发射漫反射光照(通常称为“天空光”),而地面会反射一些光线向上。SphericalHarmonics3 可以用来描述这种 来自不同方向 的环境光,捕捉到这些反射光的颜色和强度。然后,通过 LightProbe 把这些信息应用到场景中,使得物体表面看起来受到了来自天空和地面的光线影响。
简单示例:该示例中天空的光照反射不显示”注意是不反射光,不是不显示天空光“,接近地表的显示反射光,地面的反射光偏红色,
<template>
<div id="parkingLot" ref="parkingLot">
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import * as THREE from 'three';
import { PMREMGenerator } from 'three/src/extras/PMREMGenerator.js';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
import hdrImg from "@public/hdr/bgImage.hdr"
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
const parkingLot = ref();
onMounted(async () => {
const DOMEl = parkingLot.value;
// 获取 DOMEl 的宽度和高度,以设置渲染器的大小。
const width = DOMEl.clientWidth;
const height = DOMEl.clientHeight;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize( width, height );
renderer.localClippingEnabled = true;
// 启用阴影
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 阴影类型
DOMEl.appendChild( renderer.domElement );
const scene = new THREE.Scene();
renderer.setClearColor(0x000000);
const camera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 );
camera.position.set( 0, 80, 0 );
camera.lookAt( 0, 0, 0 );
// 创建 PMREM 生成器
const pmremGenerator = new PMREMGenerator(renderer);
// 使用 RGBELoader 加载 HDR 环境图
new RGBELoader().load(hdrImg, (hdrTexture) => {
// 从 HDR 图像生成 PMREM 贴图
const pmremTexture = pmremGenerator.fromEquirectangular(hdrTexture).texture;
scene.background = pmremTexture; // 作为背景图像
scene.environment = pmremTexture; // 作为全局环境贴图
// 创建球谐函数对象
const sh = new THREE.SphericalHarmonics3();
// 设置球谐函数的系数,使其不反射空中的光照
sh.coefficients[0].set(0.0, 0.0, 0.0); // 均匀的天空光设为 0
sh.coefficients[1].set(0.0, 0.0, 0.0); // 从天空方向来的光照设为 0
sh.coefficients[2].set(1.0, 0.0, 0.0); // 从地面反射回来的光照 偏红
// 创建环境光探针并应用球谐函数
const lightProbe = new THREE.LightProbe(sh);
scene.add(lightProbe);
// 创建物体
const geometry = new THREE.SphereGeometry(1, 32, 32);
const material = new THREE.MeshStandardMaterial({ color: 0xffffff });
const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
// 创建 OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 渲染循环
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
});
});
</script>
<style lang="scss" scoped="scoped">
#parkingLot {
width: 940px;
height: 940px;
border: 1px solid #ccc;
margin: 30px auto;
}
</style>
属性
- coefficients : Array 包含(9)个SH系数的数组。单个系数表示为Vector3的实例。
- **`coefficients[0]`:整体光照**
- 这是球谐函数的常数项,代表 **来自所有方向的均匀环境光**。换句话说,它可以近似为场景的全局环境光照,是一个基础的、无方向性的光线。如果所有光线都均匀分布,这个系数通常比较大。
例如:`sh.coefficients[0].set(0.5, 0.5, 0.5)` 表示整体环境光照为中等亮度的白光。
- **`coefficients[1]` 至 `coefficients[3]`:一次谐波项,表示方向性光照**
- 这三个系数负责表示 **环境光照的方向性变化**,即某些特定方向的光照强度可能更强或更弱。这些方向上的光照信息可以帮助描述场景中光线的方向性。
例如:`sh.coefficients[1].set(0.1, 0.2, 0.3)` 表示从某个特定方向来的光线更偏向蓝绿色方向,且较为微弱。
- **`coefficients[4]` 至 `coefficients[8]`:二次谐波项,表示更复杂的光照变化**
- 这些系数捕捉的是更高阶的光照变化,通常用于表示 **更加复杂的环境光照分布**,比如遮挡、反射、漫射光等。这些项会帮助我们更精细地描述场景中光照的复杂模式。
- **`sh.coefficients[0].set(0.5, 0.5, 0.5)`**
这个系数表示环境中均匀分布的白光。物体表面会受到来自所有方向的光线影响,强度为中等。
- **`sh.coefficients[1].set(0.1, 0.2, 0.3)`**
这个系数表示从某个特定方向来的光线更偏向蓝绿色(较弱的红色),且整体光线强度较低,表示这个方向上的光线比较暗淡。
- **`sh.coefficients[2].set(0.2, 0.1, 0.0)`**
这个系数代表另一个方向上的光线,具有偏橙色调,说明该方向上的环境光是偏暖色调的。
- isSphericalHarmonics3 : Boolean 用于检查给定对象是否为SphericalHarmonics3类型的只读标志。 方法
- add ( sh : SphericalHarmonics3 ) : SphericalHarmonics3 sh - 要添加的SH。 将给定的 SH 添加到此实例。sh1.add(sh2) 返回一个新的 SphericalHarmonics3 对象 shCombined,它包含了 sh1 和 sh2 的所有光照效果的和。
// 创建第一个球谐函数对象
const sh1 = new THREE.SphericalHarmonics3();
sh1.coefficients[0].set(0.1, 0.1, 0.1); // 均匀光
sh1.coefficients[1].set(0.2, 0.2, 0.2); // 从天空方向来的光
sh1.coefficients[2].set(0.3, 0.2, 0.1); // 从地面反射的光
// 创建第二个球谐函数对象
const sh2 = new THREE.SphericalHarmonics3();
sh2.coefficients[0].set(0.0, 0.0, 0.0); // 无均匀光
sh2.coefficients[1].set(0.4, 0.1, 0.0); // 从天空方向来的光
sh2.coefficients[2].set(0.0, 0.0, 0.0); // 无地面反射光
// 将两个球谐函数相加
const shCombined = sh1.add(sh2);
// 使用组合后的球谐函数
const lightProbe = new THREE.LightProbe(shCombined);
scene.add(lightProbe);
- addScaledSH ( sh : SphericalHarmonics3, scale : Number ) : SphericalHarmonics3 sh - 要添加的SH。 scale - 比例因子。 一次 执行.add()和.scale()的便捷方法。
- clone () : SphericalHarmonics3 返回具有相等系数的SphericalHarmonics3的新实例。
- copy ( sh : SphericalHarmonics3 ) : SphericalHarmonics3 sh - 要复制的SH。 将给定的SH复制到此实例。
- equals ( sh : SphericalHarmonics3 ) : Boolean sh - 要与之比较的 SH。 如果给定的SH和此实例具有相等的系数,则返回true。
- fromArray ( array : Array, offset : Number ) : SphericalHarmonics3 array - 保存SH系数数的数组。 offset - (可选)数组偏移量。 从给定数组设置此实例的系数。
- getAt ( normal : Vector3, target : Vector3 ) : Vector3 normal - 法向量(假定为单位长度)。 target - 结果向量。 返回给定法线方向的辐射度。
// 创建一个球谐函数对象
const sh = new THREE.SphericalHarmonics3();
sh.coefficients[0].set(0.5, 0.5, 0.5); // 均匀光
sh.coefficients[1].set(0.1, 0.2, 0.3); // 从天空方向来的光照
sh.coefficients[2].set(0.2, 0.1, 0.0); // 从地面反射的光照
// 创建一个法向量
const normal = new THREE.Vector3(0, 1, 0); // 指向上方的法向量
const color = new THREE.Vector3(); // 用于存储结果的颜色
// 计算法向量的光照值
sh.getAt(normal, color);
// 输出结果
console.log(`光照颜色: R: ${color.x}, G: ${color.y}, B: ${color.z}`);// 光照颜色: R: 0.1899078, G: 0.23876809999999998, B: 0.2876284
- getIrradianceAt ( normal : Vector3, target : Vector3 ) : Vector3 normal - 法向量(假定为单位长度)。 target - 结果向量。 返回给定法线方向的辐照度(辐射度与余弦波瓣卷积)。
// 创建一个球谐函数对象
const sh = new THREE.SphericalHarmonics3();
sh.coefficients[0].set(0.5, 0.5, 0.5); // 均匀光
sh.coefficients[1].set(0.2, 0.2, 0.3); // 从天空方向来的光照
sh.coefficients[2].set(0.1, 0.1, 0.0); // 从地面反射的光照
// 创建一个法向量
const normal = new THREE.Vector3(0, 1, 0); // 指向上方的法向量
const irradiance = new THREE.Vector3(); // 用于存储结果的辐照度
// 计算法向量的辐照度值
sh.getIrradianceAt(normal, irradiance);
// 输出结果
console.log(`辐照度: R: ${irradiance.x}, G: ${irradiance.y}, B: ${irradiance.z}`);// 辐照度: R: 0.6477790999999999, G: 0.6477790999999999, B: 0.7501119
- lerp ( sh : SphericalHarmonics3, alpha : Number ) : SphericalHarmonics3 在 Three.js 中,
SphericalHarmonics3的lerp方法用于对两个球谐函数进行线性插值。该方法将当前球谐函数与另一个球谐函数sh进行插值,产生一个新的SphericalHarmonics3对象。插值因子alpha决定了插值的比例。
// 创建两个球谐函数对象
const sh1 = new THREE.SphericalHarmonics3();
sh1.coefficients[0].set(0.5, 0.5, 0.5); // 均匀光
sh1.coefficients[1].set(0.2, 0.2, 0.3); // 从天空方向来的光照
sh1.coefficients[2].set(0.1, 0.1, 0.0); // 从地面反射的光照
const sh2 = new THREE.SphericalHarmonics3();
sh2.coefficients[0].set(0.1, 0.1, 0.1); // 另一种均匀光
sh2.coefficients[1].set(0.3, 0.3, 0.4); // 从天空方向来的光照
sh2.coefficients[2].set(0.0, 0.0, 0.2); // 从地面反射的光照
// 进行插值
const alpha = 0.5; // 中间值
const shLerp = sh1.lerp(sh2, alpha);
- scale ( scale : Number ) : SphericalHarmonics3 sh - 比例因子。 按给定的比例因子缩放此SH。
// 创建一个球谐函数对象
const sh = new THREE.SphericalHarmonics3();
sh.coefficients[0].set(0.5, 0.5, 0.5); // 均匀光
sh.coefficients[1].set(0.2, 0.2, 0.3); // 从天空方向来的光照
sh.coefficients[2].set(0.1, 0.1, 0.0); // 从地面反射的光照
// 输出原始系数
console.log('原始系数:');
console.log(`coeff[0]: R: ${sh.coefficients[0].x}, G: ${sh.coefficients[0].y}, B: ${sh.coefficients[0].z}`);// coeff[0]: R: 0.5, G: 0.5, B: 0.5
console.log(`coeff[1]: R: ${sh.coefficients[1].x}, G: ${sh.coefficients[1].y}, B: ${sh.coefficients[1].z}`);// coeff[1]: R: 0.2, G: 0.2, B: 0.3
console.log(`coeff[2]: R: ${sh.coefficients[2].x}, G: ${sh.coefficients[2].y}, B: ${sh.coefficients[2].z}`);// coeff[2]: R: 0.1, G: 0.1, B: 0
// 缩放系数
const scaleFactor = 2; // 设定缩放因子
const shScaled = sh.scale(scaleFactor);
// 输出缩放后的系数
console.log('缩放后的系数:');
console.log(`coeff[0]: R: ${shScaled.coefficients[0].x}, G: ${shScaled.coefficients[0].y}, B: ${shScaled.coefficients[0].z}`);// coeff[0]: R: 1, G: 1, B: 1
console.log(`coeff[1]: R: ${shScaled.coefficients[1].x}, G: ${shScaled.coefficients[1].y}, B: ${shScaled.coefficients[1].z}`);// coeff[1]: R: 0.4, G: 0.4, B: 0.6
console.log(`coeff[2]: R: ${shScaled.coefficients[2].x}, G: ${shScaled.coefficients[2].y}, B: ${shScaled.coefficients[2].z}`);// coeff[2]: R: 0.2, G: 0.2, B: 0
- set ( coefficients : Array ) : SphericalHarmonics3 coefficients - 一组SH系数。 将给定的SH系数设置为此实例。
- toArray ( array : Array, offset : Number ) : Array array - (可选)目标数组。 offset - (可选)数组偏移量。 返回包含系数的数组,或将它们复制到提供的数组中。系数表示为数字。
- zero () : SphericalHarmonics3 将所有SH系数设置为0。
- getBasisAt ( normal : Vector3, shBasis : Array ) : undefined normal - 法向量(假定为单位长度)。 shBasis - 生成的SH基础。 计算给定法向量的 SH 基础。
// 创建一个球谐函数对象
const sh = new THREE.SphericalHarmonics3();
sh.coefficients[0].set(1.0, 1.0, 1.0); // 设置均匀光
sh.coefficients[1].set(0.5, 0.5, 0.5); // 设置来自某个方向的光
sh.coefficients[2].set(0.2, 0.2, 0.0); // 设置其他反射光
// 创建法线方向
const normal = new THREE.Vector3(0, 1, 0).normalize(); // 向上方向
// 创建一个空数组来存储计算的基
const shBasis = [];
// 计算球谐基
THREE.SphericalHarmonics3.getBasisAt(normal, shBasis);
// 输出计算得到的球谐基
console.log('球谐基:', shBasis); // [0.282095,0.488603,0,0,0,0,-0.315392,0,-0.546274]