背景
原创内容
Three.js 的 Sky 组件打造逼真动态天空
Threejs
前端开发
2025-08-20
10分钟
136
头像
Dean
Threejs

Three.js 的 Sky 组件打造逼真动态天空

想给你的 3D 场景加上随时间变化的动态天空?别再费劲找六面贴图了!Three.js 自带的 Sky 组件,几行代码就能模拟日出日落、晴空雾霭,效果惊艳还省心。

一、Sky 组件:虚拟天空生成

在 Three.js 中,Sky 是一个基于物理大气散射模型(瑞利散射和米氏散射)的组件。它不用传统的六面贴图,而是通过着色器实时计算天空颜色和光照,能动态调整太阳位置、空气浑浊度等参数,轻松模拟从万里晴空到黄昏暮色的各种效果。比静态天空盒更灵活、更真实。

核心优势:

  • 动态变化:轻松实现日出日落、昼夜交替;
  • 物理真实:基于大气光学原理,色彩过渡自然;
  • 参数丰富:一键切换晴天、阴天、雾霾等天气;
  • 环境光照:直接生成环境贴图,影响整个场景光照。

二、使用组件

javascript 复制代码
// 引入 Sky 组件
import * as THREE from 'three';
import { Sky } from 'three/examples/jsm/objects/Sky.js';


// 创建天空实例并添加到场景
javascript
const scene = new THREE.Scene();
const sky = new Sky();
sky.scale.setScalar(100000); // 把天空撑大,包裹整个场景
scene.add(sky);

// 设置 10 万以上,避免相机移动时穿帮。



// 调整关键参数
// Sky 通过 uniforms 参数控制效果。
// 以下是必调参数及典型值:

const skyUniforms = sky.material.uniforms;

// 常用参数配置
skyUniforms['turbidity'].value = 5;      // 空气浑浊度 (2-20)
skyUniforms['rayleigh'].value = 2;      // 瑞利散射 (控制蓝天强度)
skyUniforms['mieCoefficient'].value = 0.005; // 米氏散射系数 (影响光晕)
skyUniforms['mieDirectionalG'].value = 0.8; // 光晕方向性 (0-1)

放置太阳

太阳位置决定光照方向和天空颜色分布:

javascript 复制代码
const sun = new THREE.Vector3();
// 设置太阳在 3D 空间中的位置 (x, y, z)
sun.set(0.3, -0.038, -0.95); // 示例:夕阳位置
skyUniforms['sunPosition'].value.copy(sun);


//别忘了同步更新场景其他光源,若场景中有模拟阳光的 `DirectionalLight`,需同步其位置:

directionalLight.position.copy(sun);

三、参数详解

参数 物理原理 效果 典型值
turbidity 空气中悬浮颗粒密度 值越高越浑浊(雾霾天) 2(清澈)~ 20(雾霾)
rayleigh 瑞利散射(短波蓝光散射) 值越高天空越蓝 3(晴天)~ 0.5(暮色)
mieCoefficient 米氏散射(大颗粒散射) 值越高雾感/光晕越强 0.005(晴)~ 0.1(雾)
mieDirectionalG 米氏散射方向性 值越高太阳光晕越集中 0.8(标准)~ 0.95(强光晕)

常用天气配方参考:

javascript 复制代码
// 晴天配方 
turbidity: 2, rayleigh: 3, mieCoefficient: 0.005

// 黄昏配方
turbidity: 10, rayleigh: 2, mieCoefficient: 0.08

// 雾霾配方
turbidity: 20, rayleigh: 1, mieCoefficient: 0.1

四、让天空动起来

动态日出日落

通过动画循环更新太阳位置,模拟太阳轨迹:

javascript 复制代码
function animate() {
    requestAnimationFrame(animate);
    
    // 随时间改变太阳高度/角度
    const time = Date.now() * 0.0005;
    sun.set(Math.sin(time)  100, Math.cos(time)  50, -50);
    skyUniforms['sunPosition'].value.copy(sun);
    
    // 别忘了同步光源!
    directionalLight.position.copy(sun);
    
    renderer.render(scene, camera);
}

技巧:当 sun.y < 0(地平线下)时,可结合星空贴图实现夜空!

生成环境光照

PMREMGenerator 将天空转为环境贴图,让物体反射天空光:

javascript 复制代码
const pmremGenerator = new THREE.PMREMGenerator(renderer);
const envMap = pmremGenerator.fromScene(sky).texture;
scene.environment = envMap; // 全局环境光
scene.background = envMap;  // 背景天空
  • 注意:此操作较耗时,建议在初始化时执行。*

五、常见问题排查

  1. 天空全黑或者颜色出现异常?

    • 检查 rayleigh 是否过低(建议 ≥1)
    • 确保 sunPosition 已正确设置并同步光源
  2. 控制台报错 uniforms undefined

    • 确认 sky 实例已成功加入 scene 后再访问 material
  3. 太阳光晕不够逼真显得太假?

    • 降低 mieCoefficient(0.005~0.01)
    • 调高 mieDirectionalG(0.8~0.95)