Three.js 实现日照分析:基于维度和时间模拟太阳高度角与方位角的变化

1. 需求背景

最近的项目是为某楼盘进行太阳光照模拟,目标是模拟从早上8点到下午5点的日照变化。为了实现这一功能,我们需要计算观测点的太阳高度角和方位角,并根据这些数据在场景中动态调整太阳的位置与光照效果。此外,光照的色彩变化也需要逐步过渡,以实现更逼真的效果。

2. 天文背景

太阳的高度角和方位角的变化是基于地球的公转。地球绕太阳的运动决定了不同季节的日照时长与强度:

  • 春分与秋分:太阳直射赤道,北半球与南半球的昼夜时长相等。
  • 夏至与冬至:太阳直射北回归线或南回归线,导致两半球的季节交替。

我们这次的观测点是长沙市,其纬度为28°11′49〃N,经度为112°58′42〃E。通过查询近五年长沙的太阳高度角与方位角的变化数据,我们得到了一个平均值。

太阳直射点的这种变化规律导致了地球各地不同季节的交替。例如,当太阳直射点位于北半球时,北半球将经历夏季,而南半球则经历冬季,反之亦然

3. 数据准备

知道了,一年中太阳直射点的运动规律。那就好办了,我们只需要知道要观测的目标点的维度就可以了。

这次我们选择的观测点是长沙

 长沙经纬度=28 °11′49〃N,112 °58′42〃

以下数据来自国际天文学联合会,展示了长沙在春分、夏至、秋分和冬至,从早上8点到下午5点的太阳高度角和方位角变化:

// 长沙春分,夏至,秋分,冬至从早上8点到下午5点太阳方位角和太阳高度角变化,数据来源国际天文学联合会 www.timeanddate.com
const CHANGSHA = [
  [
    [22.86, 30.45, 38.11, 44.94, 49.03, 44.68, 37.68, 30.11, 22.62, 15.71],
    [
      119.62, 126.43, 143.12, 165.6, 180, 195.63, 218.37, 235.08, 246.64,
      253.77,
    ],
  ],

  [
    [61.19, 68.25, 74.15, 77.59, 77.86, 74.77, 68.19, 60.09, 51.47, 43.13],
    [
      84.51, 98.94, 125.33, 156.77, 178.06, 198.51, 225.16, 239.45, 250.1,
      256.54,
    ],
  ],

  [
    [22.89, 30.16, 37.51, 44.26, 48.31, 43.99, 36.85, 29.13, 21.49, 14.38],
    [
      120.38, 127.25, 144.28, 167.62, 180.0, 192.64, 214.02, 230.44, 241.84,
      249.24,
    ],
  ],

  [
    [22.04, 29.08, 35.36, 40.43, 43.03, 40.76, 34.97, 28.38, 21.43, 15.06],
    [120.5, 127.8, 144.57, 167.15, 180, 193.71, 216.14, 232.91, 244.67, 252.4],
  ],
];

export { CHANGSHA };
4. 实现太阳在 Three.js 场景中的动态模拟
使用 Three.js 的 Sky 类

我们可以使用 Three.js 提供的 Sky 类来生成天空背景。关键是通过调整 phitheta 来控制太阳的位置,并将其转换为球坐标系下的三维坐标,更新天空中的太阳位置。

import * as THREE from "three";
import { Sky } from "three/examples/jsm/objects/Sky.js";

export default class Sun {
  sky: Sky;
  constructor(scene: THREE.Scene, renderer: THREE.WebGLRenderer) {
    const sun = new THREE.Vector3();
    this.sky = new Sky();
    this.sky.scale.setScalar(40000);
    scene.add(this.sky);

    const effectController = {
      turbidity: 20,
      rayleigh: 3,
      mieCoefficient: 0.005,
      mieDirectionalG: 0.7,
      elevation: 2,
      azimuth: 180,
    };

    const uniforms = this.sky.material.uniforms;
    uniforms["turbidity"].value = effectController.turbidity;
    uniforms["rayleigh"].value = effectController.rayleigh;
    uniforms["mieCoefficient"].value = effectController.mieCoefficient;
    uniforms["mieDirectionalG"].value = effectController.mieDirectionalG;

    const phi = THREE.MathUtils.degToRad(90 - effectController.elevation);
    const theta = THREE.MathUtils.degToRad(effectController.azimuth);
    sun.setFromSphericalCoords(1, phi, theta);

    uniforms["sunPosition"].value.copy(sun);
    renderer.toneMappingExposure = 1;
  }
}
光照效果

为了模拟太阳光照,我们将使用直射光 (DirectionalLight)半球光 (HemisphereLight),并在其中加入光晕效果,使场景看起来更加逼真。

import { DirectionalLight, HemisphereLight, PointLight, Color, Vector3, TextureLoader } from "three";
import { Lensflare, LensflareElement } from "three/examples/jsm/objects/Lensflare.js";

// 直射光设置
function createDirLight(dirColor: string | number | Color) {
  const dirLight = new DirectionalLight(dirColor);
  dirLight.castShadow = true;
  dirLight.shadow.mapSize.width = Math.pow(2, 13);
  dirLight.shadow.mapSize.height = Math.pow(2, 13);
  const d = 400;
  dirLight.shadow.camera.left = -d;
  dirLight.shadow.camera.right = d;
  dirLight.shadow.camera.top = d;
  dirLight.shadow.camera.bottom = -d;
  dirLight.shadow.camera.far = 800;
  dirLight.shadow.camera.near = 10;
  return dirLight;
}

// 半球光设置
function hlight() {
  const hemiLight = new HemisphereLight(0xb1e1ff, "#1e1e1e", 0.4);
  hemiLight.color.setHSL(0.6, 1, 0.6);
  hemiLight.groundColor.setHSL(0.095, 1, 0.75);
  hemiLight.position.set(0, 50, 0);
  return hemiLight;
}

// 光晕效果
function addLight(light: PointLight) {
  const lensflare = new Lensflare();
  const textureLoader = new TextureLoader();
  const textureFlare0 = textureLoader.load("textures/lensflare/lensflare0.png");
  lensflare.addElement(new LensflareElement(textureFlare0, 250, 0, light.color));
  light.add(lensflare);
}
5. 动态时间控制与交互

用户可以通过滑动条动态调整太阳光照的时间,Three.js 提供了 MathUtils.lerp 方法来实现高度角和方位角的线性插值,保证在模拟过程中太阳的运动流畅逼真。

function updateSunPosition() {
  const index = time.value - 8;
  const curent = Math.floor(index);
  const next = Math.ceil(index);

  const phi = MathUtils.degToRad(90 - MathUtils.lerp(SphericalCoords[0][curent], SphericalCoords[0][next], index - curent));
  const theta = MathUtils.degToRad(180 - MathUtils.lerp(SphericalCoords[1][curent], SphericalCoords[1][next], index - curent));

  sunDir.setFromSphericalCoords(1, phi, theta);
  renderer.render(scene, camera);
}

里面的场景初始化或者循环播放啥的不是核心就不过多的啰嗦啦,看最终效果

通过上述方法,我们可以在 Three.js 场景中实现基于时间与纬度的太阳光照模拟,为建筑楼盘提供更真实的日照分析效果。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奥德坤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值