1. .场景背景和天空盒

在 Three.js 中设置背景和天空盒有多种方式

 纯色背景

设置纯色背景颜色

javascript

// 方法1:通过渲染器设置
renderer.setClearColor(0x87CEEB); // 天蓝色

// 方法2:在场景中设置
scene.background = new THREE.Color(0x87CEEB);

// 方法3:使用CSS颜色
scene.background = new THREE.Color('#87CEEB');

 渐变背景

javascript

const canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 256;
const context = canvas.getContext('2d');

// 创建渐变
const gradient = context.createLinearGradient(0, 0, 0, 256);
gradient.addColorStop(0, '#87CEEB'); // 顶部颜色
gradient.addColorStop(1, '#E0F7FA'); // 底部颜色

context.fillStyle = gradient;
context.fillRect(0, 0, 1, 256);

const texture = new THREE.CanvasTexture(canvas);
scene.background = texture;

 立方体天空盒(传统天空盒)

这是最常见的天空盒实现方式:

javascript

// 1. 准备6张图片(上、下、左、右、前、后)
const urls = [
    'right.jpg',  // +X
    'left.jpg',   // -X
    'top.jpg',    // +Y
    'bottom.jpg', // -Y
    'front.jpg',  // +Z
    'back.jpg'    // -Z
];

// 2. 加载立方体纹理
const loader = new THREE.CubeTextureLoader();
loader.setPath('path/to/textures/');

const cubeTexture = loader.load(urls, () => {
    console.log('天空盒加载完成');
});

// 3. 应用到场景
scene.background = cubeTexture;

// 可选:设置环境光
scene.environment = cubeTexture; // 用于物体反射

 等距柱状投影天空盒(单张全景图)

使用单张360度全景图:

javascript

// 1. 加载全景图
const loader = new THREE.TextureLoader();
const texture = loader.load('panorama.jpg');

// 2. 设置映射方式
texture.mapping = THREE.EquirectangularReflectionMapping;

// 3. 应用到场景
scene.background = texture;
scene.environment = texture; // 也用于环境反射

 球形天空盒(SphereGeometry)

javascript

// 1. 创建球体
const sphereGeometry = new THREE.SphereGeometry(1000, 60, 40);
sphereGeometry.scale(-1, 1, 1); // 反转法线,让纹理显示在内部

// 2. 加载纹理
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('panorama.jpg');

// 3. 创建材质和网格
const sphereMaterial = new THREE.MeshBasicMaterial({
    map: texture,
    side: THREE.BackSide
});

const skySphere = new THREE.Mesh(sphereGeometry, sphereMaterial);

// 4. 添加到场景
scene.add(skySphere);

 使用 Three.js 内置的天空盒

Three.js r118+ 提供了 Sky 和 PMREMGenerator

1.1. 天空着色器(大气效果):

javascript

import { Sky } from 'three/examples/jsm/objects/Sky.js';

const sky = new Sky();
sky.scale.setScalar(450000);

// 配置天空参数
const skyUniforms = sky.material.uniforms;
skyUniforms['turbidity'].value = 10;
skyUniforms['rayleigh'].value = 2;
skyUniforms['mieCoefficient'].value = 0.005;
skyUniforms['mieDirectionalG'].value = 0.8;

// 设置太阳位置
const sun = new THREE.Vector3();
const phi = THREE.MathUtils.degToRad(90 - 2);
const theta = THREE.MathUtils.degToRad(180);
sun.setFromSphericalCoords(1, phi, theta);
skyUniforms['sunPosition'].value.copy(sun);

scene.add(sky);

 现代天空盒设置(推荐)

使用 PMREM(预过滤的Mipmap辐射环境贴图):

javascript

// 1. 创建PMREM生成器
const pmremGenerator = new THREE.PMREMGenerator(renderer);
pmremGenerator.compileEquirectangularShader();

// 2. 加载HDR或EXR全景图
new THREE.RGBELoader()
    .setDataType(THREE.HalfFloatType)
    .load('environment.hdr', (texture) => {

        // 3. 生成环境贴图
        const envMap = pmremGenerator.fromEquirectangular(texture).texture;

        // 4. 应用到场景
        scene.background = envMap;
        scene.environment = envMap;

        // 5. 清理
        texture.dispose();
        pmremGenerator.dispose();
    });

 动态天空盒

1.1. 随时间变化的天空:

javascript

class DynamicSky {
    constructor(scene) {
        this.scene = scene;
        this.time = 0;

        // 创建渐变天空
        this.createGradientSky();
    }

    createGradientSky() {
        this.canvas = document.createElement('canvas');
        this.canvas.width = 1;
        this.canvas.height = 512;
        this.ctx = this.canvas.getContext('2d');
        this.texture = new THREE.CanvasTexture(this.canvas);
        this.scene.background = this.texture;
    }

    update(timeDelta) {
        this.time += timeDelta;

        // 根据时间计算颜色(模拟日夜循环)
        const t = (Math.sin(this.time * 0.0001) + 1) * 0.5; // 0到1之间

        const dayColor = '#87CEEB';
        const nightColor = '#0C1445';
        const sunsetColor = '#FF7E5F';

        // 混合颜色
        let topColor, bottomColor;

        if (t < 0.5) {
            // 白天到日落
            const dayFactor = t * 2;
            topColor = this.mixColors(dayColor, sunsetColor, dayFactor);
            bottomColor = this.mixColors('#E0F7FA', '#FEB47B', dayFactor);
        } else {
            // 日落到夜晚
            const nightFactor = (t - 0.5) * 2;
            topColor = this.mixColors(sunsetColor, nightColor, nightFactor);
            bottomColor = this.mixColors('#FEB47B', '#1A1A2E', nightFactor);
        }

        // 绘制渐变
        const gradient = this.ctx.createLinearGradient(0, 0, 0, 512);
        gradient.addColorStop(0, topColor);
        gradient.addColorStop(1, bottomColor);

        this.ctx.fillStyle = gradient;
        this.ctx.fillRect(0, 0, 1, 512);
        this.texture.needsUpdate = true;
    }

    mixColors(color1, color2, factor) {
        // 简单的颜色混合函数
        const c1 = new THREE.Color(color1);
        const c2 = new THREE.Color(color2);
        return c1.clone().lerp(c2, factor).getStyle();
    }
}

// 使用
const dynamicSky = new DynamicSky(scene);

function animate() {
    const timeDelta = clock.getDelta();
    dynamicSky.update(timeDelta);
    // ... 其他动画逻辑
}

 完整示例:现代天空盒实现

javascript

import * as THREE from 'three';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';

class SkyboxManager {
    constructor(scene, renderer) {
        this.scene = scene;
        this.renderer = renderer;
        this.currentSkybox = null;
    }

    // 加载HDR环境贴图
    async loadHDRI(path) {
        return new Promise((resolve, reject) => {
            new RGBELoader()
                .setDataType(THREE.HalfFloatType)
                .load(
                    path,
                    (texture) => {
                        const pmremGenerator = new THREE.PMREMGenerator(this.renderer);
                        const envMap = pmremGenerator.fromEquirectangular(texture).texture;

                        this.scene.background = envMap;
                        this.scene.environment = envMap;

                        pmremGenerator.dispose();
                        texture.dispose();

                        this.currentSkybox = envMap;
                        resolve(envMap);
                    },
                    undefined,
                    reject
                );
        });
    }

    // 加载立方体天空盒
    async loadCubeMap(folder, format = 'jpg') {
        const urls = [
            `${folder}/px.${format}`,  // right
            `${folder}/nx.${format}`,  // left
            `${folder}/py.${format}`,  // top
            `${folder}/ny.${format}`,  // bottom
            `${folder}/pz.${format}`,  // front
            `${folder}/nz.${format}`   // back
        ];

        return new Promise((resolve, reject) => {
            new THREE.CubeTextureLoader()
                .setPath('')
                .load(urls, (cubeTexture) => {
                    cubeTexture.encoding = THREE.sRGBEncoding;
                    this.scene.background = cubeTexture;
                    this.currentSkybox = cubeTexture;
                    resolve(cubeTexture);
                }, undefined, reject);
        });
    }

    // 设置为纯色
    setSolidColor(color) {
        this.scene.background = new THREE.Color(color);
        this.currentSkybox = null;
    }

    // 清除天空盒
    clear() {
        this.scene.background = null;
        this.scene.environment = null;
        this.currentSkybox = null;
    }
}

// 使用示例
const skyboxManager = new SkyboxManager(scene, renderer);

// 加载HDR天空盒
await skyboxManager.loadHDRI('environments/venice_sunset_1k.hdr');

// 或者加载立方体贴图
await skyboxManager.loadCubeMap('textures/skybox', 'jpg');

 性能优化建议

  1. 纹理尺寸:根据需求选择合适的纹理尺寸(512x512、1024x1024等)

  2. 使用HDR:HDR贴图能提供更好的光照效果

  3. 共享环境贴图:使用相同的贴图作为背景和环境光

  4. 压缩纹理:使用.basis.ktx2压缩格式

  5. 按需加载:根据相机距离动态加载不同精度的天空盒

 资源推荐

选择哪种方式取决于你的具体需求: