在 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; // 也用于环境反射
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 r118+ 提供了 Sky 和 PMREMGenerator:
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();
});
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');
纹理尺寸:根据需求选择合适的纹理尺寸(512x512、1024x1024等)
使用HDR:HDR贴图能提供更好的光照效果
共享环境贴图:使用相同的贴图作为背景和环境光
压缩纹理:使用.basis或.ktx2压缩格式
按需加载:根据相机距离动态加载不同精度的天空盒
Poly Haven - 免费高质量的HDR环境贴图
HDRIHaven - 免费HDR天空盒资源
选择哪种方式取决于你的具体需求:
简单场景:纯色或渐变背景
一般3D场景:立方体天空盒
高质量渲染:HDR环境贴图
特殊效果:天空着色器或自定义动态天空