第31节:流体模拟与Shader实现水效果

第31节:流体模拟与Shader实现水效果

概述

流体模拟是计算机图形学中最具挑战性的领域之一。本节将深入探索基于Shader的实时流体模拟技术,从基础的波浪方程到复杂的交互式水效果,构建逼真的动态水域系统。

流体渲染系统架构:

流体渲染系统
物理模拟层
Shader计算层
视觉表现层
波浪方程求解
流体动力学
交互响应
顶点着色器
片段着色器
计算着色器
表面波纹
折射反射
焦散效果
实时模拟
GPU加速
视觉真实感

核心原理深度解析

源码 运行效果 上方下载

在这里插入图片描述

波浪物理模型

基于Navier-Stokes方程的简化流体模拟:

波浪类型物理模型数学公式适用场景
正弦波简谐运动y = A·sin(ωt + kx)基础波浪
Gerstner波粒子运动更真实的波峰形状海洋模拟
FFT波频谱分析基于频率域的合成开放海域

光学效果原理

水体渲染的关键光学现象:

  1. 折射效应

    • Snell定律计算光线偏折
    • 法线扰动模拟水面起伏
    • 深度差异导致的视觉扭曲
  2. 反射效应

    • 环境贴图反射
    • 菲涅尔效应控制反射强度
    • 镜面高光增强真实感

完整代码实现

高级水体渲染系统

<template>
  <div class="water-simulation-container">
    <!-- 主渲染画布 -->
    <canvas ref="waterCanvas" class="water-canvas"></canvas>
    
    <!-- 水体控制面板 -->
    <div class="water-controls">
      <div class="control-section">
        <h3>🌊 水体模拟控制</h3>
        
        <div class="control-group">
          <label>波浪强度: {{ waveIntensity }}</label>
          <input 
            type="range" 
            v-model="waveIntensity" 
            min="0" 
            max="2" 
            step="0.1"
          >
        </div>
        
        <div class="control-group">
          <label>波浪频率: {{ waveFrequency }}</label>
          <input 
            type="range" 
            v-model="waveFrequency" 
            min="0.1" 
            max="2" 
            step="0.1"
          >
        </div>
        
        <div class="control-group">
          <label>流动速度: {{ flowSpeed }}</label>
          <input 
            type="range" 
            v-model="flowSpeed" 
            min="0" 
            max="5" 
            step="0.1"
          >
        </div>
      </div>

      <div class="control-section">
        <h3>💧 视觉效果</h3>
        
        <div class="control-group">
          <label>水体颜色</label>
          <input type="color" v-model="waterColor" class="color-picker">
        </div>
        
        <div class="control-group">
          <label>透明度: {{ waterOpacity }}</label>
          <input 
            type="range" 
            v-model="waterOpacity" 
            min="0.1" 
            max="1" 
            step="0.05"
          >
        </div>
        
        <div class="control-group">
          <label>折射强度: {{ refractionStrength }}</label>
          <input 
            type="range" 
            v-model="refractionStrength" 
            min="0" 
            max="1" 
            step="0.05"
          >
        </div>
        
        <div class="control-group">
          <label>反射强度: {{ reflectionStrength }}</label>
          <input 
            type="range" 
            v-model="reflectionStrength" 
            min="0" 
            max="1" 
            step="0.05"
          >
        </div>
      </div>

      <div class="control-section">
        <h3>🎮 交互控制</h3>
        
        <div class="interaction-controls">
          <button @click="addRainEffect" class="control-button">
            🌧️ 降雨效果
          </button>
          <button @click="addObjectToWater" class="control-button">
            🚤 添加物体
          </button>
          <button @click="clearInteractions" class="control-button">
            🧹 清空交互
          </button>
        </div>
        
        <div class="interaction-stats">
          <div class="stat-item">
            <span>涟漪数量:</span>
            <span>{{ rippleCount }}</span>
          </div>
          <div class="stat-item">
            <span>交互点:</span>
            <span>{{ interactionPoints }}</span>
          </div>
        </div>
      </div>

      <div class="control-section">
        <h3>📊 性能监控</h3>
        <div class="performance-stats">
          <div class="stat-item">
            <span>帧率:</span>
            <span>{{ currentFPS }} FPS</span>
          </div>
          <div class="stat-item">
            <span>波浪顶点:</span>
            <span>{{ formatNumber(vertexCount) }}</span>
          </div>
          <div class="stat-item">
            <span>计算时间:</span>
            <span>{{ computeTime }}ms</span>
          </div>
        </div>
      </div>
    </div>

    <!-- 交互提示 -->
    <div class="interaction-hint">
      <p>💡 提示: 点击水面创建涟漪 | 拖动物体与水面交互</p>
    </div>

    <!-- 加载界面 -->
    <div v-if="isLoading" class="loading-overlay">
      <div class="water-loader">
        <div class="wave"></div>
        <div class="wave"></div>
        <div class="wave"></div>
        <h3>初始化水体系统...</h3>
        <div class="loading-progress">
          <div class="progress-bar">
            <div class="progress-fill" :style="progressStyle"></div>
          </div>
          <span class="progress-text">{{ loadingMessage }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { onMounted, onUnmounted, ref, reactive, computed } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
import { Water } from 'three/addons/objects/Water.js';

// 高级水体模拟器
class AdvancedWaterSimulator {
  constructor(renderer, scene, camera) {
    this.renderer = renderer;
    this.scene = scene;
    this.camera = camera;
    
    this.water = null;
    this.waterGeometry = null;
    this.waterMaterial = null;
    
    this.ripples = new Map();
    this.interactionPoints = [];
    this.rainParticles = [];
    
    this.time = 0;
    this.isSimulating = false;
    
    this.initWaterSystem();
  }

  // 初始化水体系统
  async initWaterSystem() {
    // 创建水几何体
    this.waterGeometry = new THREE.PlaneGeometry(100, 100, 128, 128);
    
    // 创建自定义水材质
    this.waterMaterial = this.createWaterMaterial();
    
    // 创建水面网格
    this.water = new THREE.Mesh(this.waterGeometry, this.waterMaterial);
    this.water.rotation.x = -Math.PI / 2;
    this.water.position.y = 0;
    this.water.receiveShadow = true;
    
    this.scene.add(this.water);
    
    // 设置水底
    this.createWaterBottom();
    
    // 设置环境
    this.setupWaterEnvironment();
    
    // 启动模拟
    this.startSimulation();
  }

  // 创建自定义水材质
  createWaterMaterial() {
    const material = new THREE.ShaderMaterial({
      uniforms: {
        time: { value: 0 },
        waveIntensity: { value: 1.0 },
        waveFrequency: { value: 1.0 },
        flowSpeed: { value: 1.0 },
        waterColor: { value: new THREE.Color(0x0077be) },
        waterOpacity: { value: 0.8 },
        refractionStrength: { value: 0.5 },
        reflectionStrength: { value: 0.3 },
        
        // 纹理
        normalMap: { value: this.createNormalTexture() },
        dudvMap: { value: this.createDudvTexture() },
        depthMap: { value: this.createDepthTexture() },
        
        // 环境
        cameraPos: { value: new THREE.Vector3() },
        lightDir: { value: new THREE.Vector3(1, 1, 1).normalize() }
      },
      
      vertexShader: this.getWaterVertexShader(),
      fragmentShader: this.getWaterFragmentShader(),
      
      transparent: true,
      side: THREE.DoubleSide
    });
    
    return material;
  }

  // 创建法线纹理
  createNormalTexture() {
    const canvas = document.createElement('canvas');
    canvas.width = 512;
    canvas.height = 512;
    const context = canvas.getContext('2d');
    
    // 生成水波法线图
    const gradient = context.createLinearGradient(0, 0, canvas.width, canvas.height);
    gradient.addColorStop(0, '#8080ff');
    gradient.addColorStop(0.5, '#8080ff');
    gradient.addColorStop(1, '#8080ff');
    
    context.fillStyle = gradient;
    context.fillRect(0, 0, canvas.width, canvas.height);
    
    const texture = new THREE.CanvasTexture(canvas);
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    
    return texture;
  }

  // 创建DuDv纹理(用于 distortion)
  createDudvTexture() {
    const canvas = document.createElement('canvas');
    canvas.width = 512;
    canvas.height = 512;
    const context = canvas.getContext('2d');
    
    // 生成扭曲图
    for (let x = 0; x < canvas.width; x++) {
      for (let y = 0; y < canvas.height; y++) {
        const value = Math.sin(x * 0.1) * Math.cos(y * 0.1) * 127 + 128;
        context.fillStyle = `rgb(${value}, ${value}, 255)`;
        context.fillRect(x, y, 1, 1);
      }
    }
    
    const texture = new THREE.CanvasTexture(canvas);
    texture.wrapS = THREE.RepeatWrapping;
    texture.wrapT = THREE.RepeatWrapping;
    
    return texture;
  }

  // 创建深度纹理
  createDepthTexture() {
    // 简化实现 - 实际应该从深度缓冲区获取
    const canvas = document.createElement('canvas');
    canvas.width = 512;
    canvas.height = 512;
    const context = canvas.getContext('2d');
    
    const gradient = context.createRadialGradient(
      canvas.width / 2, canvas.height / 2, 0,
      canvas.width / 2, canvas.height / 2, canvas.width / 2
    );
    gradient.addColorStop(0, '#ffffff');
    gradient.addColorStop(1, '#000000');
    
    context.fillStyle = gradient;
    context.fillRect(0, 0, canvas.width, canvas.height);
    
    return new THREE.CanvasTexture(canvas);
  }

  // 获取顶点着色器
  getWaterVertexShader() {
    return `
      uniform float time;
      uniform float waveIntensity;
      uniform float waveFrequency;
      uniform float flowSpeed;
      
      varying vec2 vUv;
      varying vec3 vPosition;
      varying vec3 vNormal;
      varying float vDepth;
      
      // Gerstner波函数
      vec3 gerstnerWave(vec3 position, float amplitude, float frequency, float speed, vec2 direction) {
        float phase = speed * frequency;
        float angle = dot(direction, position.xz) * frequency + time * phase;
        float cosine = cos(angle);
        float sine = sin(angle);
        
        vec3 result;
        result.x = direction.x * amplitude * cosine;
        result.y = amplitude * sine;
        result.z = direction.y * amplitude * cosine;
        
        return result;
      }
      
      void main() {
        vUv = uv;
        vPosition = position;
        
        // 应用多组Gerstner波
        vec3 waveSum = vec3(0.0);
        
        // 波1 - 主要波浪
        waveSum += gerstnerWave(position, 0.3 * waveIntensity, 0.5 * waveFrequency, 1.0 * flowSpeed, vec2(1.0, 0.0));
        
        // 波2 - 次要波浪
        waveSum += gerstnerWave(position, 0.2 * waveIntensity, 1.0 * waveFrequency, 1.5 * flowSpeed, vec2(0.7, 0.7));
        
        // 波3 - 细节波浪
        waveSum += gerstnerWave(position, 0.1 * waveIntensity, 2.0 * waveFrequency, 2.0 * flowSpeed, vec2(0.3, -0.8));
        
        // 应用涟漪效果
        waveSum += applyRipples(position);
        
        // 更新顶点位置
        vec3 newPosition = position + waveSum;
        
        // 计算法线(基于波函数的导数)
        vec3 tangent = vec3(1.0, 0.0, 0.0);
        vec3 binormal = vec3(0.0, 0.0, 1.0);
        vec3 normal = normalize(cross(tangent, binormal));
        
        vNormal = normal;
        vDepth = newPosition.y;
        
        gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
      }
      
      // 应用涟漪效果
      vec3 applyRipples(vec3 position) {
        vec3 rippleSum = vec3(0.0);
        
        // 这里应该从uniform读取涟漪数据
        // 简化实现:使用正弦波模拟涟漪
        for (int i = 0; i < 3; i++) {
          float dist = length(position.xz - vec2(float(i) * 10.0, 0.0));
          if (dist < 10.0) {
            float amplitude = 0.1 * exp(-dist * 0.3) * sin(dist * 5.0 - time * 10.0);
            rippleSum.y += amplitude;
          }
        }
        
        return rippleSum;
      }
    `;
  }

  // 获取片段着色器
  getWaterFragmentShader() {
    return `
      uniform vec3 waterColor;
      uniform float waterOpacity;
      uniform float refractionStrength;
      uniform float reflectionStrength;
      uniform vec3 cameraPos;
      uniform vec3 lightDir;
      uniform sampler2D normalMap;
      uniform sampler2D dudvMap;
      uniform sampler2D depthMap;
      
      varying vec2 vUv;
      varying vec3 vPosition;
      varying vec3 vNormal;
      varying float vDepth;
      
      // 菲涅尔效应计算
      float fresnel(vec3 viewDir, vec3 normal) {
        float cosTheta = dot(viewDir, normal);
        float f0 = 0.02; // 水的反射率
        return f0 + (1.0 - f0) * pow(1.0 - cosTheta, 5.0);
      }
      
      void main() {
        // 法线计算
        vec3 normal = normalize(vNormal);
        vec3 viewDir = normalize(cameraPos - vPosition);
        
        // 应用法线贴图
        vec3 normalMapValue = texture2D(normalMap, vUv * 5.0 + time * 0.05).rgb * 2.0 - 1.0;
        normal = normalize(normal + normalMapValue * 0.3);
        
        // 菲涅尔计算
        float fresnelFactor = fresnel(viewDir, normal);
        
        // 折射计算
        vec2 refractionOffset = normal.xz * refractionStrength * 0.1;
        vec2 refractionUV = vUv + refractionOffset;
        
        // 反射计算
        vec3 reflectDir = reflect(-viewDir, normal);
        float reflectionFactor = fresnelFactor * reflectionStrength;
        
        // 基础颜色
        vec3 baseColor = waterColor;
        
        // 深度影响颜色
        float depthFactor = clamp(vDepth * 0.1, 0.0, 1.0);
        baseColor = mix(baseColor, baseColor * 0.5, depthFactor);
        
        // 光照计算
        vec3 lightColor = vec3(1.0, 1.0, 0.9);
        float diffuse = max(dot(normal, lightDir), 0.0);
        vec3 diffuseColor = baseColor * diffuse * lightColor;
        
        // 镜面高光
        vec3 halfDir = normalize(lightDir + viewDir);
        float specular = pow(max(dot(normal, halfDir), 0.0), 32.0);
        vec3 specularColor = lightColor * specular * reflectionFactor;
        
        // 最终颜色合成
        vec3 finalColor = diffuseColor + specularColor;
        
        // 透明度处理
        float alpha = waterOpacity * (1.0 - depthFactor * 0.5);
        
        gl_FragColor = vec4(finalColor, alpha);
      }
    `;
  }

  // 创建水底
  createWaterBottom() {
    const bottomGeometry = new THREE.PlaneGeometry(100, 100, 1, 1);
    const bottomMaterial = new THREE.MeshStandardMaterial({
      color: 0x2f4f4f,
      roughness: 0.8,
      metalness: 0.2
    });
    
    const bottom = new THREE.Mesh(bottomGeometry, bottomMaterial);
    bottom.rotation.x = -Math.PI / 2;
    bottom.position.y = -5;
    bottom.receiveShadow = true;
    
    this.scene.add(bottom);
  }

  // 设置水环境
  setupWaterEnvironment() {
    // 环境光
    const ambientLight = new THREE.AmbientLight(0x404040, 0.4);
    this.scene.add(ambientLight);
    
    // 方向光
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
    directionalLight.position.set(50, 50, 25);
    directionalLight.castShadow = true;
    this.scene.add(directionalLight);
    
    // 添加一些环境物体
    this.addEnvironmentObjects();
  }

  // 添加环境物体
  addEnvironmentObjects() {
    // 添加一些石头
    const stoneGeometry = new THREE.SphereGeometry(2, 8, 6);
    const stoneMaterial = new THREE.MeshStandardMaterial({
      color: 0x666666,
      roughness: 0.9
    });
    
    for (let i = 0; i < 5; i++) {
      const stone = new THREE.Mesh(stoneGeometry, stoneMaterial);
      stone.position.set(
        (Math.random() - 0.5) * 80,
        -3,
        (Math.random() - 0.5) * 80
      );
      stone.scale.setScalar(Math.random() * 0.5 + 0.5);
      stone.castShadow = true;
      this.scene.add(stone);
    }
  }

  // 添加涟漪效果
  addRipple(position, strength = 1.0) {
    const rippleId = Date.now().toString();
    
    this.ripples.set(rippleId, {
      position: new THREE.Vector3(position.x, 0, position.z),
      strength: strength,
      startTime: this.time,
      duration: 3.0,
      id: rippleId
    });
    
    // 添加视觉反馈
    this.createRippleEffect(position, strength);
  }

  // 创建涟漪视觉效果
  createRippleEffect(position, strength) {
    const rippleGeometry = new THREE.RingGeometry(0.1, 1, 32);
    const rippleMaterial = new THREE.MeshBasicMaterial({
      color: 0xffffff,
      transparent: true,
      opacity: 0.6,
      side: THREE.DoubleSide
    });
    
    const ripple = new THREE.Mesh(rippleGeometry, rippleMaterial);
    ripple.position.set(position.x, 0.1, position.z);
    ripple.rotation.x = -Math.PI / 2;
    
    this.scene.add(ripple);
    
    // 涟漪动画
    const startTime = this.time;
    const animateRipple = () => {
      const elapsed = this.time - startTime;
      const progress = elapsed / 3.0;
      
      if (progress < 1.0) {
        const scale = 1 + progress * 10;
        ripple.scale.setScalar(scale);
        rippleMaterial.opacity = 0.6 * (1 - progress);
        requestAnimationFrame(animateRipple);
      } else {
        this.scene.remove(ripple);
        rippleGeometry.dispose();
        rippleMaterial.dispose();
      }
    };
    
    animateRipple();
  }

  // 添加降雨效果
  addRainEffect() {
    const rainCount = 50;
    
    for (let i = 0; i < rainCount; i++) {
      setTimeout(() => {
        const rainPosition = new THREE.Vector3(
          (Math.random() - 0.5) * 80,
          10,
          (Math.random() - 0.5) * 80
        );
        
        this.addRipple(rainPosition, 0.3);
        
        // 创建雨滴视觉效果
        this.createRainDrop(rainPosition);
      }, i * 100);
    }
  }

  // 创建雨滴效果
  createRainDrop(position) {
    const dropGeometry = new THREE.SphereGeometry(0.1, 4, 4);
    const dropMaterial = new THREE.MeshBasicMaterial({
      color: 0x88ccff,
      transparent: true,
      opacity: 0.7
    });
    
    const drop = new THREE.Mesh(dropGeometry, dropMaterial);
    drop.position.copy(position);
    
    this.scene.add(drop);
    
    // 雨滴下落动画
    const startTime = this.time;
    const animateDrop = () => {
      const elapsed = this.time - startTime;
      const progress = elapsed / 1.0;
      
      if (progress < 1.0) {
        drop.position.y = position.y - progress * 10;
        dropMaterial.opacity = 0.7 * (1 - progress);
        requestAnimationFrame(animateDrop);
      } else {
        this.scene.remove(drop);
        dropGeometry.dispose();
        dropMaterial.dispose();
      }
    };
    
    animateDrop();
  }

  // 添加水面物体
  addObjectToWater() {
    const geometry = new THREE.BoxGeometry(3, 1, 2);
    const material = new THREE.MeshStandardMaterial({
      color: 0xff6600,
      roughness: 0.7,
      metalness: 0.3
    });
    
    const object = new THREE.Mesh(geometry, material);
    object.position.set(
      (Math.random() - 0.5) * 50,
      1,
      (Math.random() - 0.5) * 50
    );
    object.castShadow = true;
    
    // 添加浮动动画
    object.userData.floatOffset = Math.random() * Math.PI * 2;
    
    this.scene.add(object);
    this.interactionPoints.push(object);
  }

  // 清空交互
  clearInteractions() {
    this.interactionPoints.forEach(object => {
      this.scene.remove(object);
      object.geometry.dispose();
      object.material.dispose();
    });
    
    this.interactionPoints = [];
    this.ripples.clear();
  }

  // 更新水体模拟
  update(deltaTime) {
    this.time += deltaTime;
    
    // 更新材质uniforms
    if (this.waterMaterial && this.waterMaterial.uniforms) {
      this.waterMaterial.uniforms.time.value = this.time;
      this.waterMaterial.uniforms.cameraPos.value.copy(this.camera.position);
    }
    
    // 更新涟漪
    this.updateRipples(deltaTime);
    
    // 更新浮动物体
    this.updateFloatingObjects(deltaTime);
  }

  // 更新涟漪
  updateRipples(deltaTime) {
    const currentTime = this.time;
    
    for (const [rippleId, ripple] of this.ripples) {
      const elapsed = currentTime - ripple.startTime;
      
      if (elapsed > ripple.duration) {
        this.ripples.delete(rippleId);
      }
    }
  }

  // 更新浮动物体
  updateFloatingObjects(deltaTime) {
    this.interactionPoints.forEach(object => {
      // 简单的浮动动画
      object.position.y = 1 + Math.sin(this.time * 2 + object.userData.floatOffset) * 0.2;
      object.rotation.y = this.time * 0.5 + object.userData.floatOffset;
    });
  }

  // 开始模拟
  startSimulation() {
    this.isSimulating = true;
  }

  // 停止模拟
  stopSimulation() {
    this.isSimulating = false;
  }

  // 设置材质参数
  setMaterialUniforms(params) {
    if (!this.waterMaterial || !this.waterMaterial.uniforms) return;
    
    const uniforms = this.waterMaterial.uniforms;
    
    if (params.waveIntensity !== undefined) {
      uniforms.waveIntensity.value = params.waveIntensity;
    }
    
    if (params.waveFrequency !== undefined) {
      uniforms.waveFrequency.value = params.waveFrequency;
    }
    
    if (params.flowSpeed !== undefined) {
      uniforms.flowSpeed.value = params.flowSpeed;
    }
    
    if (params.waterColor !== undefined) {
      uniforms.waterColor.value.set(params.waterColor);
    }
    
    if (params.waterOpacity !== undefined) {
      uniforms.waterOpacity.value = params.waterOpacity;
    }
    
    if (params.refractionStrength !== undefined) {
      uniforms.refractionStrength.value = params.refractionStrength;
    }
    
    if (params.reflectionStrength !== undefined) {
      uniforms.reflectionStrength.value = params.reflectionStrength;
    }
  }
}

export default {
  name: 'WaterSimulation',
  setup() {
    const waterCanvas = ref(null);
    const waveIntensity = ref(1.0);
    const waveFrequency = ref(1.0);
    const flowSpeed = ref(1.0);
    const waterColor = ref('#0077be');
    const waterOpacity = ref(0.8);
    const refractionStrength = ref(0.5);
    const reflectionStrength = ref(0.3);
    const rippleCount = ref(0);
    const interactionPoints = ref(0);
    const currentFPS = ref(0);
    const vertexCount = ref(0);
    const computeTime = ref(0);
    const isLoading = ref(true);
    const loadingMessage = ref('初始化水体系统...');

    let scene, camera, renderer, controls;
    let waterSimulator;
    let clock, stats;
    let frameCount = 0;
    let lastFpsUpdate = 0;

    // 初始化场景
    const initScene = async () => {
      // 创建场景
      scene = new THREE.Scene();
      scene.background = new THREE.Color(0x87ceeb);
      scene.fog = new THREE.Fog(0x87ceeb, 50, 200);

      // 创建相机
      camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      camera.position.set(0, 30, 50);

      // 创建渲染器
      renderer = new THREE.WebGLRenderer({
        canvas: waterCanvas.value,
        antialias: true,
        powerPreference: "high-performance"
      });
      
      renderer.setSize(window.innerWidth, window.innerHeight);
      renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
      renderer.shadowMap.enabled = true;
      renderer.shadowMap.type = THREE.PCFSoftShadowMap;

      // 添加控制器
      controls = new OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;
      controls.dampingFactor = 0.05;
      controls.minDistance = 10;
      controls.maxDistance = 100;

      // 初始化水体模拟器
      loadingMessage.value = '创建水体材质...';
      waterSimulator = new AdvancedWaterSimulator(renderer, scene, camera);
      
      // 等待初始化完成
      await new Promise(resolve => setTimeout(resolve, 2000));
      
      loadingMessage.value = '设置交互系统...';
      setupInteraction();
      
      isLoading.value = false;
      
      // 启动渲染循环
      clock = new THREE.Clock();
      animate();
    };

    // 设置交互
    const setupInteraction = () => {
      // 点击水面创建涟漪
      renderer.domElement.addEventListener('click', onWaterClick);
      
      // 键盘控制
      document.addEventListener('keydown', onKeyDown);
    };

    // 水面点击事件
    const onWaterClick = (event) => {
      // 转换鼠标坐标到3D空间
      const mouse = new THREE.Vector2();
      const rect = renderer.domElement.getBoundingClientRect();
      
      mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
      mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
      
      // 射线检测
      const raycaster = new THREE.Raycaster();
      raycaster.setFromCamera(mouse, camera);
      
      const intersects = raycaster.intersectObject(waterSimulator.water);
      
      if (intersects.length > 0) {
        const point = intersects[0].point;
        waterSimulator.addRipple(point, 1.0);
        updateStats();
      }
    };

    // 键盘控制
    const onKeyDown = (event) => {
      switch (event.code) {
        case 'KeyR':
          waterSimulator.addRainEffect();
          break;
        case 'KeyO':
          waterSimulator.addObjectToWater();
          break;
        case 'KeyC':
          waterSimulator.clearInteractions();
          break;
      }
      updateStats();
    };

    // 添加降雨效果
    const addRainEffect = () => {
      waterSimulator.addRainEffect();
      updateStats();
    };

    // 添加物体到水面
    const addObjectToWater = () => {
      waterSimulator.addObjectToWater();
      updateStats();
    };

    // 清空交互
    const clearInteractions = () => {
      waterSimulator.clearInteractions();
      updateStats();
    };

    // 更新统计信息
    const updateStats = () => {
      rippleCount.value = waterSimulator.ripples.size;
      interactionPoints.value = waterSimulator.interactionPoints.length;
      vertexCount.value = waterSimulator.waterGeometry.attributes.position.count;
    };

    // 动画循环
    const animate = () => {
      requestAnimationFrame(animate);
      
      const deltaTime = clock.getDelta();
      
      // 更新控制器
      controls.update();
      
      // 更新水体模拟
      if (waterSimulator) {
        const startTime = performance.now();
        waterSimulator.update(deltaTime);
        computeTime.value = (performance.now() - startTime).toFixed(2);
      }
      
      // 渲染场景
      renderer.render(scene, camera);
      
      // 更新性能统计
      updatePerformanceStats(deltaTime);
    };

    // 更新性能统计
    const updatePerformanceStats = (deltaTime) => {
      frameCount++;
      lastFpsUpdate += deltaTime;
      
      if (lastFpsUpdate >= 1.0) {
        currentFPS.value = Math.round(frameCount / lastFpsUpdate);
        frameCount = 0;
        lastFpsUpdate = 0;
      }
    };

    // 格式化数字
    const formatNumber = (num) => {
      if (num >= 1000000) {
        return (num / 1000000).toFixed(1) + 'M';
      } else if (num >= 1000) {
        return (num / 1000).toFixed(1) + 'K';
      }
      return num.toString();
    };

    // 进度条样式
    const progressStyle = computed(() => ({
      width: '100%'
    }));

    // 响应式设置
    watch(waveIntensity, (newValue) => {
      if (waterSimulator) {
        waterSimulator.setMaterialUniforms({ waveIntensity: parseFloat(newValue) });
      }
    });

    watch(waveFrequency, (newValue) => {
      if (waterSimulator) {
        waterSimulator.setMaterialUniforms({ waveFrequency: parseFloat(newValue) });
      }
    });

    watch(flowSpeed, (newValue) => {
      if (waterSimulator) {
        waterSimulator.setMaterialUniforms({ flowSpeed: parseFloat(newValue) });
      }
    });

    watch(waterColor, (newValue) => {
      if (waterSimulator) {
        waterSimulator.setMaterialUniforms({ waterColor: newValue });
      }
    });

    watch(waterOpacity, (newValue) => {
      if (waterSimulator) {
        waterSimulator.setMaterialUniforms({ waterOpacity: parseFloat(newValue) });
      }
    });

    watch(refractionStrength, (newValue) => {
      if (waterSimulator) {
        waterSimulator.setMaterialUniforms({ refractionStrength: parseFloat(newValue) });
      }
    });

    watch(reflectionStrength, (newValue) => {
      if (waterSimulator) {
        waterSimulator.setMaterialUniforms({ reflectionStrength: parseFloat(newValue) });
      }
    });

    onMounted(() => {
      initScene();
      window.addEventListener('resize', handleResize);
    });

    onUnmounted(() => {
      if (waterSimulator) {
        waterSimulator.stopSimulation();
      }
      if (renderer) {
        renderer.dispose();
      }
      window.removeEventListener('resize', handleResize);
    });

    const handleResize = () => {
      if (!camera || !renderer) return;
      
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    };

    return {
      waterCanvas,
      waveIntensity,
      waveFrequency,
      flowSpeed,
      waterColor,
      waterOpacity,
      refractionStrength,
      reflectionStrength,
      rippleCount,
      interactionPoints,
      currentFPS,
      vertexCount,
      computeTime,
      isLoading,
      loadingMessage,
      progressStyle,
      addRainEffect,
      addObjectToWater,
      clearInteractions,
      formatNumber
    };
  }
};
</script>

<style scoped>
.water-simulation-container {
  width: 100%;
  height: 100vh;
  position: relative;
  background: #000;
  overflow: hidden;
}

.water-canvas {
  width: 100%;
  height: 100%;
  display: block;
}

.water-controls {
  position: absolute;
  top: 20px;
  right: 20px;
  width: 320px;
  background: rgba(0, 0, 0, 0.8);
  padding: 20px;
  border-radius: 12px;
  color: white;
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.1);
  max-height: 80vh;
  overflow-y: auto;
}

.control-section {
  margin-bottom: 25px;
  padding-bottom: 15px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.control-section:last-child {
  margin-bottom: 0;
  border-bottom: none;
}

.control-section h3 {
  color: #00ffff;
  margin-bottom: 15px;
  font-size: 16px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.control-group {
  margin-bottom: 15px;
}

.control-group label {
  display: block;
  margin-bottom: 8px;
  color: #ccc;
  font-size: 14px;
}

.control-group input[type="range"] {
  width: 100%;
  height: 6px;
  background: #444;
  border-radius: 3px;
  outline: none;
  opacity: 0.7;
  transition: opacity 0.2s;
}

.control-group input[type="range"]:hover {
  opacity: 1;
}

.control-group input[type="range"]::-webkit-slider-thumb {
  appearance: none;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: #00ffff;
  cursor: pointer;
  box-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
}

.color-picker {
  width: 100%;
  height: 40px;
  border: 2px solid #00ffff;
  border-radius: 6px;
  background: transparent;
  cursor: pointer;
}

.interaction-controls {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 15px;
}

.control-button {
  padding: 12px;
  border: none;
  border-radius: 8px;
  background: linear-gradient(45deg, #667eea, #764ba2);
  color: white;
  cursor: pointer;
  font-size: 14px;
  transition: all 0.3s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.control-button:hover {
  transform: translateY(-2px);
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}

.interaction-stats,
.performance-stats {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.stat-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 0;
  font-size: 14px;
}

.stat-item span:first-child {
  color: #ccc;
}

.stat-item span:last-child {
  color: #00ffff;
  font-weight: bold;
}

.interaction-hint {
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(0, 0, 0, 0.7);
  padding: 12px 20px;
  border-radius: 20px;
  color: white;
  font-size: 14px;
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.1);
}

.interaction-hint p {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}

.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.water-loader {
  text-align: center;
  color: white;
  position: relative;
}

.wave {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 80px;
  height: 80px;
  margin: -40px 0 0 -40px;
  border: 4px solid rgba(255, 255, 255, 0.3);
  border-radius: 50%;
  animation: waveExpand 2s infinite;
}

.wave:nth-child(1) {
  animation-delay: 0s;
}

.wave:nth-child(2) {
  animation-delay: 0.5s;
}

.wave:nth-child(3) {
  animation-delay: 1s;
}

.water-loader h3 {
  margin-top: 120px;
  color: white;
  font-size: 20px;
}

.loading-progress {
  width: 300px;
  margin: 20px auto 0;
}

.progress-bar {
  width: 100%;
  height: 6px;
  background: rgba(255, 255, 255, 0.2);
  border-radius: 3px;
  overflow: hidden;
  margin-bottom: 10px;
}

.progress-fill {
  height: 100%;
  background: linear-gradient(90deg, #00ffff, #0088ff);
  border-radius: 3px;
  transition: width 0.3s ease;
}

.progress-text {
  color: #00ffff;
  font-size: 14px;
}

@keyframes waveExpand {
  0% {
    transform: scale(0.2);
    opacity: 1;
  }
  100% {
    transform: scale(1.5);
    opacity: 0;
  }
}

/* 响应式设计 */
@media (max-width: 768px) {
  .water-controls {
    width: 280px;
    right: 10px;
    top: 10px;
    padding: 15px;
  }
  
  .interaction-hint {
    bottom: 10px;
    left: 10px;
    right: 10px;
    transform: none;
    text-align: center;
  }
}
</style>

高级流体特性

实时波浪物理计算

// 基于FFT的海洋波浪模拟
class FFTWaterSimulator {
  constructor(resolution = 256) {
    this.resolution = resolution;
    this.fftSize = resolution * resolution;
    this.heightField = new Float32Array(this.fftSize);
    this.displacementX = new Float32Array(this.fftSize);
    this.displacementZ = new Float32Array(this.fftSize);
    
    this.initSpectrum();
    this.setupFFT();
  }

  // 初始化波浪频谱
  initSpectrum() {
    const windSpeed = 10.0;
    const windDirection = new THREE.Vector2(1, 0);
    
    for (let y = 0; y < this.resolution; y++) {
      for (let x = 0; x < this.resolution; x++) {
        const kx = (x - this.resolution / 2) * (2 * Math.PI / 100);
        const ky = (y - this.resolution / 2) * (2 * Math.PI / 100);
        
        const k = new THREE.Vector2(kx, ky);
        const magnitude = k.length();
        
        if (magnitude < 0.0001) continue;
        
        // Phillips频谱
        const L = (windSpeed * windSpeed) / 9.81;
        const kDotW = k.dot(windDirection.normalize());
        const phillips = Math.exp(-1.0 / (magnitude * L * magnitude * L)) 
                       / (magnitude * magnitude * magnitude * magnitude) 
                       * Math.pow(kDotW, 2);
        
        const index = y * this.resolution + x;
        this.heightField[index] = Math.sqrt(phillips) * this.gaussianRandom();
      }
    }
  }

  // 高斯随机数生成
  gaussianRandom() {
    let u = 0, v = 0;
    while(u === 0) u = Math.random();
    while(v === 0) v = Math.random();
    return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
  }

  // 设置FFT计算
  setupFFT() {
    // 这里应该实现FFT算法
    // 简化实现:使用预计算的波浪
    console.log('FFT系统初始化完成');
  }

  // 更新波浪状态
  update(time) {
    for (let y = 0; y < this.resolution; y++) {
      for (let x = 0; x < this.resolution; x++) {
        const index = y * this.resolution + x;
        
        // 简化的波浪传播计算
        const phase = time * Math.sqrt(9.81 * this.getWaveNumber(x, y));
        this.heightField[index] *= Math.cos(phase);
      }
    }
  }

  // 获取波数
  getWaveNumber(x, y) {
    const kx = (x - this.resolution / 2) * (2 * Math.PI / 100);
    const ky = (y - this.resolution / 2) * (2 * Math.PI / 100);
    return Math.sqrt(kx * kx + ky * ky);
  }

  // 获取高度场纹理
  getHeightFieldTexture() {
    const canvas = document.createElement('canvas');
    canvas.width = this.resolution;
    canvas.height = this.resolution;
    const context = canvas.getContext('2d');
    const imageData = context.createImageData(this.resolution, this.resolution);
    
    for (let i = 0; i < this.fftSize; i++) {
      const value = (this.heightField[i] + 1) * 128;
      imageData.data[i * 4] = value;     // R
      imageData.data[i * 4 + 1] = value; // G
      imageData.data[i * 4 + 2] = value; // B
      imageData.data[i * 4 + 3] = 255;   // A
    }
    
    context.putImageData(imageData, 0, 0);
    return new THREE.CanvasTexture(canvas);
  }
}

交互式涟漪传播系统

// 实时涟漪传播模拟
class RipplePropagationSystem {
  constructor(resolution = 512) {
    this.resolution = resolution;
    this.currentState = new Float32Array(resolution * resolution);
    this.previousState = new Float32Array(resolution * resolution);
    this.damping = 0.99;
    this.waveSpeed = 0.5;
    
    this.initGPUCompute();
  }

  // 初始化GPU计算
  initGPUCompute() {
    const rippleShader = `
      uniform sampler2D currentState;
      uniform sampler2D previousState;
      uniform float damping;
      uniform float waveSpeed;
      uniform vec2 resolution;
      
      void main() {
        vec2 uv = gl_FragCoord.xy / resolution;
        vec2 pixelSize = 1.0 / resolution;
        
        // 获取周围像素的值
        float left = texture2D(currentState, uv - vec2(pixelSize.x, 0.0)).r;
        float right = texture2D(currentState, uv + vec2(pixelSize.x, 0.0)).r;
        float top = texture2D(currentState, uv - vec2(0.0, pixelSize.y)).r;
        float bottom = texture2D(currentState, uv + vec2(0.0, pixelSize.y)).r;
        
        // 拉普拉斯算子计算曲率
        float curvature = (left + right + top + bottom) * 0.25 - texture2D(currentState, uv).r;
        
        // 波浪方程: new = 2 * current - previous + waveSpeed^2 * curvature
        float current = texture2D(currentState, uv).r;
        float previous = texture2D(previousState, uv).r;
        float newValue = 2.0 * current - previous + waveSpeed * waveSpeed * curvature;
        
        // 应用阻尼
        newValue *= damping;
        
        gl_FragColor = vec4(newValue, 0.0, 0.0, 1.0);
      }
    `;
    
    // 这里应该设置GPU计算渲染器
    console.log('涟漪传播系统初始化完成');
  }

  // 添加涟漪源
  addRippleSource(x, y, strength) {
    const index = Math.floor(y) * this.resolution + Math.floor(x);
    this.currentState[index] += strength;
  }

  // 更新涟漪传播
  update() {
    // 交换状态缓冲区
    const temp = this.currentState;
    this.currentState = this.previousState;
    this.previousState = temp;
    
    // 执行GPU计算更新
    this.executeGPUUpdate();
  }

  // 执行GPU更新
  executeGPUUpdate() {
    // 这里应该执行GPU计算着色器
    // 简化实现:CPU模拟
    for (let y = 1; y < this.resolution - 1; y++) {
      for (let x = 1; x < this.resolution - 1; x++) {
        const index = y * this.resolution + x;
        
        // 拉普拉斯算子
        const curvature = (
          this.previousState[index - 1] +
          this.previousState[index + 1] +
          this.previousState[index - this.resolution] +
          this.previousState[index + this.resolution]
        ) * 0.25 - this.previousState[index];
        
        // 波浪方程
        this.currentState[index] = 2 * this.previousState[index] - this.currentState[index] 
                                 + this.waveSpeed * this.waveSpeed * curvature;
        
        // 应用阻尼
        this.currentState[index] *= this.damping;
      }
    }
  }

  // 获取涟漪纹理
  getRippleTexture() {
    const canvas = document.createElement('canvas');
    canvas.width = this.resolution;
    canvas.height = this.resolution;
    const context = canvas.getContext('2d');
    const imageData = context.createImageData(this.resolution, this.resolution);
    
    for (let i = 0; i < this.currentState.length; i++) {
      const value = (this.currentState[i] * 0.5 + 0.5) * 255;
      imageData.data[i * 4] = value;     // R
      imageData.data[i * 4 + 1] = value; // G
      imageData.data[i * 4 + 2] = 255;   // B
      imageData.data[i * 4 + 3] = 255;   // A
    }
    
    context.putImageData(imageData, 0, 0);
    return new THREE.CanvasTexture(canvas);
  }
}

注意事项与最佳实践

  1. 性能优化关键

    • 使用GPU计算进行物理模拟
    • 实现多层次细节的水体渲染
    • 优化着色器计算复杂度
    • 使用纹理压缩减少内存占用
  2. 视觉质量优化

    • 实现真实的菲涅尔效应
    • 添加适当的折射和反射
    • 使用高质量的法线贴图
    • 实现动态光照和阴影
  3. 交互体验优化

    • 响应式涟漪传播系统
    • 多物体交互支持
    • 实时物理反馈
    • 性能自适应调整

下一节预告

第32节:体积渲染与云层烟雾效果
将深入探索体积渲染技术,包括:3D噪声纹理生成、Raymarching算法、实时体积光照计算,以及如何创建逼真的云层、烟雾和雾气效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二川bro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值