Three.js实战用GLSL着色器打造动态水面与粒子特效附完整代码在三维可视化领域Three.js已成为Web端最受欢迎的3D引擎之一。但真正能让作品脱颖而出的往往是对着色器编程的深度掌握。本文将带您深入GLSL着色器的实战应用从噪声函数到物理模拟完整实现一个具有交互性的动态水面场景并附赠可复用的粒子系统解决方案。1. 环境准备与基础架构1.1 项目初始化首先创建标准的Three.js项目结构我们需要以下核心组件!DOCTYPE html html head meta charsetutf-8 titleGLSL水面特效/title stylebody { margin: 0; } canvas { display: block; }/style /head body script typemodule // 主代码将在这里实现 /script /body /html关键依赖包括Three.js核心库r128OrbitControls相机控制EffectComposer后处理dat.GUI参数调试提示建议使用ES Module方式引入依赖可通过CDN直接加载import * as THREE from https://cdn.jsdelivr.net/npm/three0.128.0/build/three.module.js1.2 场景基础配置建立标准的Three.js场景循环// 初始化场景 const scene new THREE.Scene() scene.background new THREE.Color(0x000814) // 相机设置 const camera new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000) camera.position.set(0, 5, 15) // 渲染器配置 const renderer new THREE.WebGLRenderer({ antialias: true }) renderer.setSize(window.innerWidth, window.innerHeight) document.body.appendChild(renderer.domElement) // 动画循环 function animate() { requestAnimationFrame(animate) renderer.render(scene, camera) } animate()2. GLSL着色器核心原理2.1 着色器工作流程Three.js中着色器材质(ShaderMaterial)的工作流程顶点着色器阶段处理几何体每个顶点的位置变换计算并传递变量到片元着色器典型应用波浪变形、顶点动画片元着色器阶段决定每个像素的最终颜色实现光照、反射等视觉效果典型应用水面着色、粒子外观2.2 关键Uniform参数Uniform类型作用示例time实现动画效果uniform float uTimeresolution适配屏幕尺寸uniform vec2 uResolutiontextures贴图采样uniform sampler2D uNoiseMapgeometry几何属性attribute vec3 position3. 动态水面实现3.1 波浪算法设计使用分形噪声组合实现自然波浪效果// 噪声函数集合 float noise(vec3 p) { // 实现Perlin噪声 // ... } float fbm(vec3 p) { float total 0.0; float frequency 1.0; float amplitude 1.0; for(int i0; i4; i) { total noise(p * frequency) * amplitude; frequency * 2.0; amplitude * 0.5; } return total; }3.2 菲涅尔反射实现水面特有的菲涅尔效应float fresnel pow(1.0 - max(dot(normalize(N), normalize(V)), 0.0), 5.0); vec3 reflectColor textureCube(envMap, reflect(-V, N)).rgb; vec3 refractColor textureCube(envMap, refract(-V, N, 0.9)).rgb; vec3 color mix(refractColor, reflectColor, fresnel);3.3 完整水面着色器顶点着色器核心代码uniform float uTime; varying vec3 vPosition; void main() { vPosition position; // 波浪位移 float waveHeight sin(position.x * 0.2 uTime) * 0.5; vec3 newPosition vec3(position.x, position.y waveHeight, position.z); gl_Position projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0); }片元着色器核心代码uniform sampler2D uWaterTexture; varying vec3 vPosition; void main() { // 基础颜色 vec4 baseColor texture2D(uWaterTexture, vPosition.xz * 0.1); // 添加波浪高光 float specular pow(max(dot(normalize(lightDir), normalize(viewDir)), 0.0), 32.0); gl_FragColor baseColor vec4(vec3(specular * 0.8), 1.0); }4. 粒子系统集成4.1 粒子初始化创建具有生命周期的粒子系统const particleCount 5000; const particles new THREE.BufferGeometry(); const posArray new Float32Array(particleCount * 3); for(let i0; iparticleCount*3; i) { posArray[i] (Math.random() - 0.5) * 10; } particles.setAttribute(position, new THREE.BufferAttribute(posArray, 3));4.2 粒子着色器动态粒子顶点着色器attribute float aLifetime; uniform float uTime; void main() { // 生命周期计算 float progress mod(uTime aLifetime, 1.0); // 大小衰减 gl_PointSize 10.0 * (1.0 - progress); // 位置更新 vec3 newPos position; newPos.y progress * 2.0; gl_Position projectionMatrix * modelViewMatrix * vec4(newPos, 1.0); }4.3 粒子-水面交互实现粒子与水面碰撞效果// 在顶点着色器中 float waterHeight getWaveHeight(position.xz); if(newPos.y waterHeight) { newPos.y waterHeight 0.1; velocity.y * -0.5; // 反弹 }5. 性能优化技巧5.1 渲染优化策略实例化渲染对大量粒子使用InstancedMeshLOD系统根据距离调整渲染质量后处理优化合理使用Bloom等效果5.2 着色器优化技巧减少分支语句使用内置函数替代自定义实现合理选择精度修饰符预计算不变的值注意在移动端设备上建议将精度设置为mediumpprecision mediump float;6. 完整代码整合将所有模块组合成完整解决方案// 水面材质 const waterMaterial new THREE.ShaderMaterial({ vertexShader: waterVS, fragmentShader: waterFS, uniforms: { uTime: { value: 0 }, uTexture: { value: waterTexture } } }) // 粒子材质 const particleMaterial new THREE.ShaderMaterial({ vertexShader: particleVS, fragmentShader: particleFS, transparent: true, blending: THREE.AdditiveBlending }) // 动画循环更新 function animate() { const time performance.now() * 0.001 waterMaterial.uniforms.uTime.value time particleMaterial.uniforms.uTime.value time renderer.render(scene, camera) }7. 进阶扩展方向7.1 风向影响系统uniform vec3 uWindDirection; void applyWind(inout vec3 position, float strength) { position.x uWindDirection.x * strength; position.z uWindDirection.z * strength; }7.2 雨天涟漪效果通过法线贴图动态生成雨滴涟漪vec2 rippleUV vUv * 10.0; float ripple texture2D(uRippleMap, rippleUV uTime*0.1).r; normal ripple * 0.2;7.3 焦散效果模拟使用投影纹理技术实现水下焦散vec4 caustics texture2D(uCausticsTex, vWorldPos.xz * 0.1 uTime*0.05); color.rgb caustics.rgb * 0.3;在实现这些高级效果时建议逐步测试每个功能模块。比如先单独调试好波浪算法再集成菲涅尔反射最后加入粒子交互。这样的模块化开发方式能有效降低调试难度。
Three.js实战:用GLSL着色器打造动态水面与粒子特效(附完整代码)
Three.js实战用GLSL着色器打造动态水面与粒子特效附完整代码在三维可视化领域Three.js已成为Web端最受欢迎的3D引擎之一。但真正能让作品脱颖而出的往往是对着色器编程的深度掌握。本文将带您深入GLSL着色器的实战应用从噪声函数到物理模拟完整实现一个具有交互性的动态水面场景并附赠可复用的粒子系统解决方案。1. 环境准备与基础架构1.1 项目初始化首先创建标准的Three.js项目结构我们需要以下核心组件!DOCTYPE html html head meta charsetutf-8 titleGLSL水面特效/title stylebody { margin: 0; } canvas { display: block; }/style /head body script typemodule // 主代码将在这里实现 /script /body /html关键依赖包括Three.js核心库r128OrbitControls相机控制EffectComposer后处理dat.GUI参数调试提示建议使用ES Module方式引入依赖可通过CDN直接加载import * as THREE from https://cdn.jsdelivr.net/npm/three0.128.0/build/three.module.js1.2 场景基础配置建立标准的Three.js场景循环// 初始化场景 const scene new THREE.Scene() scene.background new THREE.Color(0x000814) // 相机设置 const camera new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000) camera.position.set(0, 5, 15) // 渲染器配置 const renderer new THREE.WebGLRenderer({ antialias: true }) renderer.setSize(window.innerWidth, window.innerHeight) document.body.appendChild(renderer.domElement) // 动画循环 function animate() { requestAnimationFrame(animate) renderer.render(scene, camera) } animate()2. GLSL着色器核心原理2.1 着色器工作流程Three.js中着色器材质(ShaderMaterial)的工作流程顶点着色器阶段处理几何体每个顶点的位置变换计算并传递变量到片元着色器典型应用波浪变形、顶点动画片元着色器阶段决定每个像素的最终颜色实现光照、反射等视觉效果典型应用水面着色、粒子外观2.2 关键Uniform参数Uniform类型作用示例time实现动画效果uniform float uTimeresolution适配屏幕尺寸uniform vec2 uResolutiontextures贴图采样uniform sampler2D uNoiseMapgeometry几何属性attribute vec3 position3. 动态水面实现3.1 波浪算法设计使用分形噪声组合实现自然波浪效果// 噪声函数集合 float noise(vec3 p) { // 实现Perlin噪声 // ... } float fbm(vec3 p) { float total 0.0; float frequency 1.0; float amplitude 1.0; for(int i0; i4; i) { total noise(p * frequency) * amplitude; frequency * 2.0; amplitude * 0.5; } return total; }3.2 菲涅尔反射实现水面特有的菲涅尔效应float fresnel pow(1.0 - max(dot(normalize(N), normalize(V)), 0.0), 5.0); vec3 reflectColor textureCube(envMap, reflect(-V, N)).rgb; vec3 refractColor textureCube(envMap, refract(-V, N, 0.9)).rgb; vec3 color mix(refractColor, reflectColor, fresnel);3.3 完整水面着色器顶点着色器核心代码uniform float uTime; varying vec3 vPosition; void main() { vPosition position; // 波浪位移 float waveHeight sin(position.x * 0.2 uTime) * 0.5; vec3 newPosition vec3(position.x, position.y waveHeight, position.z); gl_Position projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0); }片元着色器核心代码uniform sampler2D uWaterTexture; varying vec3 vPosition; void main() { // 基础颜色 vec4 baseColor texture2D(uWaterTexture, vPosition.xz * 0.1); // 添加波浪高光 float specular pow(max(dot(normalize(lightDir), normalize(viewDir)), 0.0), 32.0); gl_FragColor baseColor vec4(vec3(specular * 0.8), 1.0); }4. 粒子系统集成4.1 粒子初始化创建具有生命周期的粒子系统const particleCount 5000; const particles new THREE.BufferGeometry(); const posArray new Float32Array(particleCount * 3); for(let i0; iparticleCount*3; i) { posArray[i] (Math.random() - 0.5) * 10; } particles.setAttribute(position, new THREE.BufferAttribute(posArray, 3));4.2 粒子着色器动态粒子顶点着色器attribute float aLifetime; uniform float uTime; void main() { // 生命周期计算 float progress mod(uTime aLifetime, 1.0); // 大小衰减 gl_PointSize 10.0 * (1.0 - progress); // 位置更新 vec3 newPos position; newPos.y progress * 2.0; gl_Position projectionMatrix * modelViewMatrix * vec4(newPos, 1.0); }4.3 粒子-水面交互实现粒子与水面碰撞效果// 在顶点着色器中 float waterHeight getWaveHeight(position.xz); if(newPos.y waterHeight) { newPos.y waterHeight 0.1; velocity.y * -0.5; // 反弹 }5. 性能优化技巧5.1 渲染优化策略实例化渲染对大量粒子使用InstancedMeshLOD系统根据距离调整渲染质量后处理优化合理使用Bloom等效果5.2 着色器优化技巧减少分支语句使用内置函数替代自定义实现合理选择精度修饰符预计算不变的值注意在移动端设备上建议将精度设置为mediumpprecision mediump float;6. 完整代码整合将所有模块组合成完整解决方案// 水面材质 const waterMaterial new THREE.ShaderMaterial({ vertexShader: waterVS, fragmentShader: waterFS, uniforms: { uTime: { value: 0 }, uTexture: { value: waterTexture } } }) // 粒子材质 const particleMaterial new THREE.ShaderMaterial({ vertexShader: particleVS, fragmentShader: particleFS, transparent: true, blending: THREE.AdditiveBlending }) // 动画循环更新 function animate() { const time performance.now() * 0.001 waterMaterial.uniforms.uTime.value time particleMaterial.uniforms.uTime.value time renderer.render(scene, camera) }7. 进阶扩展方向7.1 风向影响系统uniform vec3 uWindDirection; void applyWind(inout vec3 position, float strength) { position.x uWindDirection.x * strength; position.z uWindDirection.z * strength; }7.2 雨天涟漪效果通过法线贴图动态生成雨滴涟漪vec2 rippleUV vUv * 10.0; float ripple texture2D(uRippleMap, rippleUV uTime*0.1).r; normal ripple * 0.2;7.3 焦散效果模拟使用投影纹理技术实现水下焦散vec4 caustics texture2D(uCausticsTex, vWorldPos.xz * 0.1 uTime*0.05); color.rgb caustics.rgb * 0.3;在实现这些高级效果时建议逐步测试每个功能模块。比如先单独调试好波浪算法再集成菲涅尔反射最后加入粒子交互。这样的模块化开发方式能有效降低调试难度。