Three.js Shader 实现动态天空云层效果(附完整代码)

发布时间:2026/6/22 5:11:48

Three.js Shader 实现动态天空云层效果(附完整代码) Three.js Shader 实现动态天空云层效果附完整代码在游戏开发和虚拟现实场景中逼真的天空效果往往能大幅提升沉浸感。而云层的动态变化更是让整个场景活起来的关键要素。本文将带你深入Three.js的Shader编程世界从零实现一个性能优异、视觉效果丰富的动态云层系统。1. 动态云层的核心原理云层效果的实现本质上是对噪声函数的艺术化应用。通过多层噪声叠加我们可以模拟出云朵自然的不规则形态。而让云层动起来的关键则在于对噪声采样坐标的持续偏移。1.1 噪声函数的选择在Shader编程中常用的噪声函数包括Perlin噪声产生自然连续的渐变效果Simplex噪声计算效率更高适合实时渲染Value噪声实现简单但效果较生硬Worley噪声适合模拟云朵的细胞状结构// 示例简化版的3D噪声函数 float noise(vec3 p) { vec3 i floor(p); vec3 f fract(p); f f*f*(3.0-2.0*f); float n dot(i, vec3(1.0, 57.0, 113.0)); return mix(mix(mix(hash(n0.0), hash(n1.0),f.x), mix(hash(n57.0), hash(n58.0),f.x),f.y), mix(mix(hash(n113.0), hash(n114.0),f.x), mix(hash(n170.0), hash(n171.0),f.x),f.y),f.z); }1.2 多层噪声叠加技术单一噪声层往往显得过于单调。通过将不同尺度的噪声叠加称为分形噪声或湍流噪声可以创造出更丰富的细节噪声层权重频率作用基础层0.51.0决定云朵大体形状细节层0.252.0增加边缘细节微结构层0.1254.0丰富表面纹理float fbm(vec3 p) { float f 0.0; f 0.5000 * noise(p); p * 2.02; f 0.2500 * noise(p); p * 2.03; f 0.1250 * noise(p); p * 2.01; f 0.0625 * noise(p); return f; }2. Three.js中的ShaderMaterial配置Three.js提供了ShaderMaterial这一强大工具让我们能够完全自定义渲染管线。以下是创建动态云层的基本框架2.1 基础场景设置首先需要创建一个球体作为天空穹顶并应用我们的Shader材质const geometry new THREE.SphereGeometry(5000, 50, 50); const material new THREE.ShaderMaterial({ uniforms: { iTime: { value: 0 }, iResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) } }, vertexShader: ..., fragmentShader: ..., side: THREE.BackSide, transparent: true }); const skyDome new THREE.Mesh(geometry, material); scene.add(skyDome);2.2 关键Uniform参数ShaderMaterial通过uniforms向着色器传递动态参数iTime用于实现动画效果的时间变量iResolution屏幕分辨率确保效果适配不同尺寸iMouse可选实现交互效果sunPosition控制光照方向提示在动画循环中更新iTime的值function animate() { material.uniforms.iTime.value clock.getElapsedTime(); requestAnimationFrame(animate); }3. 完整Shader代码解析下面我们拆解实现动态云层的完整着色器代码理解每个关键部分的实现原理。3.1 顶点着色器顶点着色器相对简单主要任务是传递UV坐标varying vec2 vUv; varying vec3 vPosition; void main() { vUv uv; vPosition position; gl_Position projectionMatrix * modelViewMatrix * vec4(position, 1.0); }3.2 片段着色器核心逻辑片段着色器是效果实现的核心主要包含以下几个部分噪声函数定义实现前面提到的3D噪声光线步进(Raymarching)模拟光线在云层中的散射光照计算添加阳光照射效果颜色混合最终输出颜色合成uniform float iTime; uniform vec2 iResolution; void main() { vec2 uv gl_FragCoord.xy / iResolution.xy; // 相机设置 vec3 ro vec3(0.0, 0.0, 3.0); // 相机位置 vec3 rd normalize(vec3((uv-0.5)*vec2(iResolution.x/iResolution.y,1.0), -1.0)); // 云层密度计算 float density calculateCloudDensity(ro, rd); // 光照计算 vec3 lightColor calculateLighting(density, rd); // 背景色与云层混合 vec3 bgColor mix(vec3(0.5,0.7,0.9), vec3(0.1,0.2,0.4), rd.y); vec3 finalColor mix(bgColor, lightColor, density); gl_FragColor vec4(finalColor, 1.0); }4. 性能优化技巧实时渲染动态云层需要考虑性能问题以下是几个关键优化点4.1 渲染质量与性能平衡通过控制以下参数可以在效果和性能间取得平衡光线步进次数通常32-64次足够噪声层数3-4层效果较好分辨率可适当降低采样精度// 优化后的光线步进循环 for(int i0; i40; i) { if(sum.a 0.99) break; // ... 计算步骤 ... t max(0.1, 0.03*t); // 自适应步长 }4.2 预处理与缓存对于静态元素可以考虑预计算部分噪声纹理使用低分辨率渲染然后上采样实现LOD(细节层次)系统4.3 WebGL2特性利用如果目标平台支持WebGL2可以使用计算着色器将部分计算移到GPU通用计算管线多渲染目标同时输出多个渲染结果变换反馈缓存中间计算结果5. 进阶效果实现基础云层效果实现后可以进一步添加以下增强效果5.1 昼夜循环系统通过修改光照参数和颜色渐变实现从日出到日落的自然过渡vec3 getSkyColor(float timeOfDay) { if(timeOfDay 0.25) { // 黎明 return mix(nightColor, dawnColor, timeOfDay/0.25); } else if(timeOfDay 0.5) { // 白天 return mix(dawnColor, dayColor, (timeOfDay-0.25)/0.25); } // ... 其他时段类似 }5.2 天气效果模拟通过调整噪声参数可以模拟不同天气状况天气类型密度风速颜色晴天低快亮白阴天高慢深灰雷雨极高变化暗黑5.3 体积光照效果添加光线散射效果增强视觉冲击力float calculateLightScattering(vec3 pos, vec3 lightDir) { float sum 0.0; for(int i0; i8; i) { pos lightDir * 0.1; sum getDensityAt(pos); } return exp(-sum * 0.2); }6. 完整代码实现以下是整合所有功能的完整实现代码import * as THREE from three; class CloudScene { constructor() { this.initScene(); this.createSkyDome(); this.animate(); } initScene() { this.renderer new THREE.WebGLRenderer({ antialias: true }); this.renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(this.renderer.domElement); this.camera new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); this.camera.position.z 5; this.scene new THREE.Scene(); this.clock new THREE.Clock(); } createSkyDome() { const geometry new THREE.SphereGeometry(500, 60, 60); const material new THREE.ShaderMaterial({ uniforms: { iTime: { value: 0 }, iResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }, sunPosition: { value: new THREE.Vector3(-1, 0.5, -1) } }, vertexShader: ..., // 前面提供的顶点着色器 fragmentShader: ..., // 前面提供的片段着色器 side: THREE.BackSide, transparent: true }); this.skyDome new THREE.Mesh(geometry, material); this.scene.add(this.skyDome); } animate() { const animate () { requestAnimationFrame(animate); this.skyDome.material.uniforms.iTime.value this.clock.getElapsedTime(); this.renderer.render(this.scene, this.camera); }; animate(); } } new CloudScene();在实现过程中我发现在移动设备上直接使用复杂的分形噪声计算会导致帧率下降。一个实用的解决方案是预生成几张不同密度的噪声纹理然后在运行时进行混合这样既能保持视觉效果又能显著提升性能。

相关新闻