three SphericalHarmonics3 三阶球谐函数

301 阅读7分钟

在 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 中,SphericalHarmonics3lerp 方法用于对两个球谐函数进行线性插值。该方法将当前球谐函数与另一个球谐函数 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]