Harmony Flutter 跨平台开发实战:鸿蒙与音乐律动艺术、着色器与GPU编程:高性能视觉渲染

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net


🎨 一、着色器基础

📚 1.1 什么是着色器

着色器(Shader)是运行在GPU上的小程序,用于控制图形渲染的各个阶段。它们使用GLSL(OpenGL Shading Language)或类似语言编写,能够高效地并行处理大量数据。

渲染管线概述

复制代码
图形渲染管线:

┌─────────────┐
│  顶点数据   │ ← 位置、颜色、纹理坐标
└──────┬──────┘
       ↓
┌─────────────┐
│ 顶点着色器  │ ← 变换、光照计算
└──────┬──────┘
       ↓
┌─────────────┐
│ 图元装配    │ ← 三角形、线段
└──────┬──────┘
       ↓
┌─────────────┐
│ 几何着色器  │ ← 可选:生成新图元
└──────┬──────┘
       ↓
┌─────────────┐
│ 光栅化      │ ← 转换为片段
└──────┬──────┘
       ↓
┌─────────────┐
│ 片段着色器  │ ← 颜色、纹理、光照
└──────┬──────┘
       ↓
┌─────────────┐
│ 输出合并    │ ← 深度测试、混合
└──────┬──────┘
       ↓
┌─────────────┐
│  帧缓冲    │ ← 最终图像
└─────────────┘

着色器类型

复制代码
主要着色器类型:

1. 顶点着色器(Vertex Shader)
   - 处理每个顶点
   - 坐标变换
   - 逐顶点光照
   - 传递数据给片段着色器

2. 片段着色器(Fragment Shader)
   - 处理每个像素
   - 纹理采样
   - 逐像素光照
   - 颜色计算

3. 几何着色器(Geometry Shader)
   - 处理图元
   - 生成新顶点
   - 程序化几何

4. 计算着色器(Compute Shader)
   - 通用计算
   - 物理模拟
   - 图像处理

🔬 1.2 GLSL 语言基础

数据类型

glsl 复制代码
// 标量类型
float f = 1.0;      // 浮点数
int i = 1;          // 整数
bool b = true;      // 布尔值

// 向量类型
vec2 v2 = vec2(1.0, 2.0);     // 2D向量
vec3 v3 = vec3(1.0, 2.0, 3.0); // 3D向量
vec4 v4 = vec4(1.0, 2.0, 3.0, 4.0); // 4D向量

// 向量分量访问
v3.x, v3.y, v3.z     // 位置分量
v3.r, v3.g, v3.b     // 颜色分量
v3.s, v3.t, v3.p     // 纹理坐标

// 矩阵类型
mat2 m2;    // 2x2矩阵
mat3 m3;    // 3x3矩阵
mat4 m4;    // 4x4矩阵

// 纹理采样器
sampler2D tex;       // 2D纹理
samplerCube cube;    // 立方体贴图

内置函数

glsl 复制代码
// 数学函数
abs(x)           // 绝对值
sin(x), cos(x)   // 三角函数
pow(x, y)        // 幂函数
sqrt(x)          // 平方根
log(x)           // 对数

// 几何函数
length(v)        // 向量长度
distance(a, b)   // 两点距离
dot(a, b)        // 点积
cross(a, b)      // 叉积
normalize(v)     // 归一化
reflect(v, n)    // 反射
refract(v, n, ratio) // 折射

// 插值与平滑
mix(a, b, t)     // 线性插值
smoothstep(edge0, edge1, x) // 平滑阶梯
step(edge, x)    // 阶梯函数

// 纹理采样
texture(tex, uv) // 纹理采样
textureLod(tex, uv, lod) // 指定LOD采样

🎯 1.3 Flutter 中的着色器

Flutter Shader API

dart 复制代码
import 'dart:ui' as ui;
import 'dart:typed_data';

// 加载着色器
Future<ui.FragmentShader> loadShader(String assetPath) async {
  final program = await ui.FragmentShader.fromAsset(assetPath);
  return program;
}

// 使用着色器
class ShaderPainter extends CustomPainter {
  final ui.FragmentShader shader;
  final double time;
  
  ShaderPainter(this.shader, this.time);
  
  @override
  void paint(Canvas canvas, Size size) {
    // 设置 uniform 变量
    shader.setFloat(0, size.width);
    shader.setFloat(1, size.height);
    shader.setFloat(2, time);
    
    // 使用着色器绘制
    final paint = Paint()..shader = shader;
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint);
  }
  
  @override
  bool shouldRepaint(covariant ShaderPainter old) => time != old.time;
}

🌈 二、经典着色器效果

🎨 2.1 渐变与颜色效果

径向渐变着色器

glsl 复制代码
// radial_gradient.frag
#version 460 core

precision mediump float;

layout(location = 0) out vec4 fragColor;

uniform float u_width;
uniform float u_height;
uniform float u_time;

void main() {
    vec2 uv = gl_FragCoord.xy / vec2(u_width, u_height);
    vec2 center = vec2(0.5, 0.5);
    
    float dist = distance(uv, center);
    
    // 多色渐变
    vec3 color1 = vec3(1.0, 0.2, 0.4);  // 粉红
    vec3 color2 = vec3(0.2, 0.4, 1.0);  // 蓝色
    vec3 color3 = vec3(0.2, 1.0, 0.4);  // 绿色
    
    float t = dist * 2.0 + u_time * 0.5;
    vec3 color = mix(color1, color2, sin(t) * 0.5 + 0.5);
    color = mix(color, color3, sin(t * 2.0) * 0.5 + 0.5);
    
    fragColor = vec4(color, 1.0);
}

彩虹效果

glsl 复制代码
// rainbow.frag
#version 460 core

precision mediump float;

layout(location = 0) out vec4 fragColor;

uniform float u_width;
uniform float u_height;
uniform float u_time;

vec3 hsv2rgb(vec3 c) {
    vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

void main() {
    vec2 uv = gl_FragCoord.xy / vec2(u_width, u_height);
    
    float hue = uv.x + uv.y + u_time * 0.1;
    vec3 color = hsv2rgb(vec3(hue, 0.8, 1.0));
    
    fragColor = vec4(color, 1.0);
}

🌀 2.2 噪声与分形效果

Perlin 噪声实现

glsl 复制代码
// perlin_noise.frag
#version 460 core

precision mediump float;

layout(location = 0) out vec4 fragColor;

uniform float u_width;
uniform float u_height;
uniform float u_time;

// 伪随机函数
float random(vec2 st) {
    return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}

// 2D 噪声
float noise(vec2 st) {
    vec2 i = floor(st);
    vec2 f = fract(st);
    
    // 四个角的随机值
    float a = random(i);
    float b = random(i + vec2(1.0, 0.0));
    float c = random(i + vec2(0.0, 1.0));
    float d = random(i + vec2(1.0, 1.0));
    
    // 平滑插值
    vec2 u = f * f * (3.0 - 2.0 * f);
    
    return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
}

// 分形布朗运动(FBM)
float fbm(vec2 st) {
    float value = 0.0;
    float amplitude = 0.5;
    float frequency = 1.0;
    
    for (int i = 0; i < 6; i++) {
        value += amplitude * noise(st * frequency);
        frequency *= 2.0;
        amplitude *= 0.5;
    }
    
    return value;
}

void main() {
    vec2 uv = gl_FragCoord.xy / vec2(u_width, u_height);
    
    // 动态噪声
    float n = fbm(uv * 5.0 + u_time * 0.2);
    
    // 颜色映射
    vec3 color1 = vec3(0.1, 0.1, 0.3);
    vec3 color2 = vec3(0.8, 0.4, 0.2);
    vec3 color = mix(color1, color2, n);
    
    fragColor = vec4(color, 1.0);
}

🌊 2.3 波纹与扭曲效果

水波纹效果

glsl 复制代码
// water_ripple.frag
#version 460 core

precision mediump float;

layout(location = 0) out vec4 fragColor;

uniform float u_width;
uniform float u_height;
uniform float u_time;

void main() {
    vec2 uv = gl_FragCoord.xy / vec2(u_width, u_height);
    vec2 center = vec2(0.5, 0.5);
    
    // 计算到中心的距离
    float dist = distance(uv, center);
    
    // 波纹效果
    float wave = sin(dist * 30.0 - u_time * 3.0) * 0.02;
    
    // 扭曲 UV
    vec2 distortedUV = uv + normalize(uv - center) * wave;
    
    // 颜色
    vec3 color1 = vec3(0.1, 0.3, 0.6);
    vec3 color2 = vec3(0.2, 0.5, 0.8);
    float t = sin(dist * 20.0 - u_time * 2.0) * 0.5 + 0.5;
    vec3 color = mix(color1, color2, t);
    
    // 添加高光
    float highlight = pow(1.0 - abs(wave * 50.0), 3.0) * 0.5;
    color += highlight;
    
    fragColor = vec4(color, 1.0);
}

🎵 三、音频驱动的着色器效果

🎼 3.1 音频频谱可视化

频谱条形图着色器

glsl 复制代码
// spectrum_bars.frag
#version 460 core

precision mediump float;

layout(location = 0) out vec4 fragColor;

uniform float u_width;
uniform float u_height;
uniform float u_time;
uniform float u_bars[64];  // 频谱数据

void main() {
    vec2 uv = gl_FragCoord.xy / vec2(u_width, u_height);
    
    // 计算当前像素属于哪个频谱条
    int barIndex = int(uv.x * 64.0);
    float barValue = u_bars[barIndex];
    
    // 绘制条形
    float barHeight = barValue;
    float inBar = step(uv.y, barHeight);
    
    // 颜色渐变
    float hue = uv.x * 0.8;
    vec3 color = vec3(
        sin(hue * 6.28) * 0.5 + 0.5,
        sin((hue + 0.33) * 6.28) * 0.5 + 0.5,
        sin((hue + 0.66) * 6.28) * 0.5 + 0.5
    );
    
    // 发光效果
    float glow = exp(-abs(uv.y - barHeight) * 10.0) * 0.5;
    
    color = color * inBar + color * glow;
    
    fragColor = vec4(color, 1.0);
}

🎚️ 3.2 音乐节拍响应

脉冲效果着色器

glsl 复制代码
// pulse.frag
#version 460 core

precision mediump float;

layout(location = 0) out vec4 fragColor;

uniform float u_width;
uniform float u_height;
uniform float u_time;
uniform float u_beat;      // 节拍强度 [0, 1]
uniform float u_energy;    // 总能量

void main() {
    vec2 uv = gl_FragCoord.xy / vec2(u_width, u_height);
    vec2 center = vec2(0.5, 0.5);
    
    // 距离中心的距离
    float dist = distance(uv, center);
    
    // 脉冲波
    float pulse = sin(dist * 20.0 - u_time * 5.0) * 0.5 + 0.5;
    
    // 节拍响应
    float beatPulse = u_beat * exp(-dist * 3.0);
    
    // 颜色
    vec3 color1 = vec3(0.8, 0.2, 0.4);
    vec3 color2 = vec3(0.2, 0.4, 0.8);
    vec3 color = mix(color1, color2, pulse);
    
    // 添加节拍脉冲
    color += vec3(beatPulse);
    
    // 能量影响亮度
    color *= 0.5 + u_energy * 0.5;
    
    fragColor = vec4(color, 1.0);
}

🔧 四、Dart/Flutter 着色器集成

🎯 4.1 着色器加载与管理

dart 复制代码
import 'dart:ui' as ui;
import 'dart:typed_data';

class ShaderManager {
  static final ShaderManager _instance = ShaderManager._internal();
  factory ShaderManager() => _instance;
  ShaderManager._internal();
  
  final Map<String, ui.FragmentShader> _shaders = {};
  
  Future<void> loadShader(String name, String assetPath) async {
    try {
      final shader = await ui.FragmentShader.fromAsset(assetPath);
      _shaders[name] = shader;
    } catch (e) {
      debugPrint('Failed to load shader $name: $e');
    }
  }
  
  ui.FragmentShader? getShader(String name) => _shaders[name];
  
  void dispose() {
    for (final shader in _shaders.values) {
      shader.dispose();
    }
    _shaders.clear();
  }
}

🎨 4.2 着色器绘制器

dart 复制代码
class ShaderPainter extends CustomPainter {
  final ui.FragmentShader shader;
  final List<double> uniforms;
  
  ShaderPainter(this.shader, this.uniforms);
  
  @override
  void paint(Canvas canvas, Size size) {
    int index = 0;
    shader.setFloat(index++, size.width);
    shader.setFloat(index++, size.height);
    
    for (final uniform in uniforms) {
      shader.setFloat(index++, uniform);
    }
    
    canvas.drawRect(
      Rect.fromLTWH(0, 0, size.width, size.height),
      Paint()..shader = shader,
    );
  }
  
  @override
  bool shouldRepaint(covariant ShaderPainter old) => 
      !listEquals(uniforms, old.uniforms);
}

💻 五、完整代码实现

dart 复制代码
import 'package:flutter/material.dart';
import 'package:just_audio_ohos/just_audio_ohos.dart';
import 'package:audio_session/audio_session.dart';
import 'dart:math';
import 'dart:typed_data';
import 'dart:ui' as ui;

void main() {
  runApp(const ShaderApp());
}

class ShaderApp extends StatelessWidget {
  const ShaderApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '着色器效果',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.purple, brightness: Brightness.dark),
        useMaterial3: true,
      ),
      home: const ShaderHomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class ShaderHomePage extends StatelessWidget {
  const ShaderHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('着色器效果'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _buildCard(context, title: '渐变效果', description: '多彩径向渐变', icon: Icons.gradient, color: Colors.purple,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const GradientDemo()))),
          _buildCard(context, title: '噪声效果', description: 'Perlin与Simplex噪声', icon: Icons.grain, color: Colors.indigo,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const NoiseDemo()))),
          _buildCard(context, title: '波纹效果', description: '水波与涟漪', icon: Icons.waves, color: Colors.blue,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const RippleDemo()))),
          _buildCard(context, title: '漩涡效果', description: '扭曲与旋转', icon: Icons.cyclone, color: Colors.cyan,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const SwirlDemo()))),
          _buildCard(context, title: '频谱可视化', description: '音频频谱着色器', icon: Icons.equalizer, color: Colors.teal,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const SpectrumShaderDemo()))),
          _buildCard(context, title: '音乐脉冲', description: '节拍响应效果', icon: Icons.music_note, color: Colors.green,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const MusicPulseDemo()))),
          _buildCard(context, title: '等离子效果', description: '经典等离子体', icon: Icons.bolt, color: Colors.yellow,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const PlasmaDemo()))),
          _buildCard(context, title: '万花筒', description: '对称镜像效果', icon: Icons.filter_vintage, color: Colors.pink,
              onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const KaleidoscopeDemo()))),
        ],
      ),
    );
  }

  Widget _buildCard(BuildContext context, {required String title, required String description, required IconData icon, 
      required Color color, required VoidCallback onTap}) {
    return Card(
      margin: const EdgeInsets.only(bottom: 12),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(16),
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Row(children: [
            Container(width: 56, height: 56, decoration: BoxDecoration(color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12)),
                child: Icon(icon, color: color, size: 28)),
            const SizedBox(width: 16),
            Expanded(child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
              Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
              const SizedBox(height: 4),
              Text(description, style: TextStyle(color: Colors.grey[600], fontSize: 14)),
            ])),
            Icon(Icons.chevron_right, color: Colors.grey[400]),
          ]),
        ),
      ),
    );
  }
}

class GradientDemo extends StatefulWidget {
  const GradientDemo({super.key});
  @override
  State<GradientDemo> createState() => _GradientDemoState();
}

class _GradientDemoState extends State<GradientDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  double _time = 0;
  int _gradientType = 0;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _controller.addListener(() {
      _time += 0.016;
      setState(() {});
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('渐变效果')),
      body: Stack(children: [
        CustomPaint(painter: GradientPainter(_time, _gradientType), size: Size.infinite),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(12)),
      child: Column(children: [
        const Text('渐变类型', style: TextStyle(color: Colors.white70, fontSize: 12)),
        const SizedBox(height: 8),
        Wrap(spacing: 8, children: [
          ActionChip(label: const Text('径向', style: TextStyle(color: Colors.white, fontSize: 11)),
              backgroundColor: _gradientType == 0 ? Colors.purple : Colors.purple.withOpacity(0.3),
              onPressed: () => setState(() => _gradientType = 0)),
          ActionChip(label: const Text('线性', style: TextStyle(color: Colors.white, fontSize: 11)),
              backgroundColor: _gradientType == 1 ? Colors.purple : Colors.purple.withOpacity(0.3),
              onPressed: () => setState(() => _gradientType = 1)),
          ActionChip(label: const Text('彩虹', style: TextStyle(color: Colors.white, fontSize: 11)),
              backgroundColor: _gradientType == 2 ? Colors.purple : Colors.purple.withOpacity(0.3),
              onPressed: () => setState(() => _gradientType = 2)),
        ]),
      ]),
    );
  }
}

class GradientPainter extends CustomPainter {
  final double time;
  final int gradientType;
  
  GradientPainter(this.time, this.gradientType);
  
  @override
  void paint(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    
    switch (gradientType) {
      case 0:
        _drawRadialGradient(canvas, size, center);
        break;
      case 1:
        _drawLinearGradient(canvas, size);
        break;
      case 2:
        _drawRainbowGradient(canvas, size, center);
        break;
    }
  }
  
  void _drawRadialGradient(Canvas canvas, Size size, Offset center) {
    final colors = <Color>[
      HSVColor.fromAHSV(1, (time * 30) % 360, 0.8, 1).toColor(),
      HSVColor.fromAHSV(1, (time * 30 + 60) % 360, 0.8, 1).toColor(),
      HSVColor.fromAHSV(1, (time * 30 + 120) % 360, 0.8, 1).toColor(),
      HSVColor.fromAHSV(1, (time * 30 + 180) % 360, 0.8, 1).toColor(),
    ];
    
    final shader = ui.Gradient.radial(center, size.width * 0.7, colors, [0.0, 0.33, 0.66, 1.0]);
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..shader = shader);
  }
  
  void _drawLinearGradient(Canvas canvas, Size size) {
    final angle = time * 0.5;
    final dx = cos(angle) * size.width;
    final dy = sin(angle) * size.height;
    
    final colors = <Color>[
      HSVColor.fromAHSV(1, (time * 40) % 360, 0.9, 1).toColor(),
      HSVColor.fromAHSV(1, (time * 40 + 90) % 360, 0.9, 1).toColor(),
      HSVColor.fromAHSV(1, (time * 40 + 180) % 360, 0.9, 1).toColor(),
    ];
    
    final shader = ui.Gradient.linear(Offset(-dx, -dy), Offset(size.width + dx, size.height + dy), colors, [0.0, 0.5, 1.0]);
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..shader = shader);
  }
  
  void _drawRainbowGradient(Canvas canvas, Size size, Offset center) {
    for (int y = 0; y < size.height; y++) {
      final hue = (y / size.height * 360 + time * 30) % 360;
      canvas.drawRect(Rect.fromLTWH(0, y.toDouble(), size.width, 1), 
          Paint()..color = HSVColor.fromAHSV(1, hue, 0.8, 1).toColor());
    }
  }
  
  @override
  bool shouldRepaint(covariant GradientPainter old) => true;
}

class NoiseDemo extends StatefulWidget {
  const NoiseDemo({super.key});
  @override
  State<NoiseDemo> createState() => _NoiseDemoState();
}

class _NoiseDemoState extends State<NoiseDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  double _time = 0;
  int _noiseType = 0;
  double _scale = 5.0;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _controller.addListener(() {
      _time += 0.016;
      setState(() {});
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('噪声效果')),
      body: Stack(children: [
        CustomPaint(painter: NoisePainter(_time, _noiseType, _scale), size: Size.infinite),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(12)),
      child: Column(children: [
        Row(children: [
          Expanded(child: Column(children: [
            const Text('噪声类型', style: TextStyle(color: Colors.white70, fontSize: 11)),
            Wrap(spacing: 4, children: [
              ActionChip(label: const Text('Perlin', style: TextStyle(color: Colors.white, fontSize: 10)),
                  backgroundColor: _noiseType == 0 ? Colors.indigo : Colors.indigo.withOpacity(0.3),
                  onPressed: () => setState(() => _noiseType = 0)),
              ActionChip(label: const Text('FBM', style: TextStyle(color: Colors.white, fontSize: 10)),
                  backgroundColor: _noiseType == 1 ? Colors.indigo : Colors.indigo.withOpacity(0.3),
                  onPressed: () => setState(() => _noiseType = 1)),
            ]),
          ])),
          Expanded(child: Column(children: [
            Text('缩放: ${_scale.toStringAsFixed(1)}', style: const TextStyle(color: Colors.white70, fontSize: 11)),
            Slider(value: _scale, min: 1, max: 20, onChanged: (v) => setState(() => _scale = v), activeColor: Colors.indigo),
          ])),
        ]),
      ]),
    );
  }
}

class NoisePainter extends CustomPainter {
  final double time;
  final int noiseType;
  final double scale;
  
  NoisePainter(this.time, this.noiseType, this.scale);
  
  @override
  void paint(Canvas canvas, Size size) {
    for (int x = 0; x < size.width; x += 2) {
      for (int y = 0; y < size.height; y += 2) {
        double noise;
        
        switch (noiseType) {
          case 0:
            noise = _perlinNoise(x / size.width * scale, y / size.height * scale, time * 0.5);
            break;
          default:
            noise = _fbmNoise(x / size.width * scale, y / size.height * scale, time * 0.3);
        }
        
        final hue = (noise * 180 + time * 20) % 360;
        canvas.drawRect(Rect.fromLTWH(x.toDouble(), y.toDouble(), 2, 2),
            Paint()..color = HSVColor.fromAHSV(1, hue, 0.7, noise * 0.5 + 0.5).toColor());
      }
    }
  }
  
  double _perlinNoise(double x, double y, double t) {
    return (sin(x * 3 + t) * cos(y * 3 + t) + 1) / 2;
  }
  
  double _fbmNoise(double x, double y, double t) {
    double value = 0;
    double amplitude = 0.5;
    for (int i = 0; i < 4; i++) {
      value += amplitude * (sin(x * (i + 1) + t * (i + 1)) * cos(y * (i + 1) + t * (i + 1)) + 1) / 2;
      amplitude *= 0.5;
    }
    return value;
  }
  
  @override
  bool shouldRepaint(covariant NoisePainter old) => true;
}

class RippleDemo extends StatefulWidget {
  const RippleDemo({super.key});
  @override
  State<RippleDemo> createState() => _RippleDemoState();
}

class _RippleDemoState extends State<RippleDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  double _time = 0;
  double _waveSpeed = 3.0;
  double _waveFreq = 20.0;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _controller.addListener(() {
      _time += 0.016;
      setState(() {});
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('波纹效果')),
      body: Stack(children: [
        CustomPaint(painter: RipplePainter(_time, _waveSpeed, _waveFreq), size: Size.infinite),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(12)),
      child: Column(children: [
        Row(children: [
          Expanded(child: Column(children: [
            Text('波速: ${_waveSpeed.toStringAsFixed(1)}', style: const TextStyle(color: Colors.white70, fontSize: 11)),
            Slider(value: _waveSpeed, min: 1, max: 10, onChanged: (v) => setState(() => _waveSpeed = v), activeColor: Colors.blue),
          ])),
          Expanded(child: Column(children: [
            Text('频率: ${_waveFreq.toStringAsFixed(0)}', style: const TextStyle(color: Colors.white70, fontSize: 11)),
            Slider(value: _waveFreq, min: 5, max: 50, onChanged: (v) => setState(() => _waveFreq = v), activeColor: Colors.blue),
          ])),
        ]),
      ]),
    );
  }
}

class RipplePainter extends CustomPainter {
  final double time;
  final double waveSpeed;
  final double waveFreq;
  
  RipplePainter(this.time, this.waveSpeed, this.waveFreq);
  
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a1520));
    
    final center = Offset(size.width / 2, size.height / 2);
    final maxDist = sqrt(size.width * size.width + size.height * size.height) / 2;
    
    for (int x = 0; x < size.width; x += 3) {
      for (int y = 0; y < size.height; y += 3) {
        final dist = sqrt(pow(x - center.dx, 2) + pow(y - center.dy, 2));
        final wave = sin(dist / maxDist * waveFreq - time * waveSpeed);
        final intensity = (wave + 1) / 2;
        
        final hue = (200 + intensity * 40) % 360;
        canvas.drawRect(Rect.fromLTWH(x.toDouble(), y.toDouble(), 3, 3),
            Paint()..color = HSVColor.fromAHSV(intensity * 0.8, hue, 0.8, 1).toColor());
      }
    }
  }
  
  @override
  bool shouldRepaint(covariant RipplePainter old) => true;
}

class SwirlDemo extends StatefulWidget {
  const SwirlDemo({super.key});
  @override
  State<SwirlDemo> createState() => _SwirlDemoState();
}

class _SwirlDemoState extends State<SwirlDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  double _time = 0;
  double _swirlStrength = 5.0;
  double _rotationSpeed = 2.0;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _controller.addListener(() {
      _time += 0.016;
      setState(() {});
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('漩涡效果')),
      body: Stack(children: [
        CustomPaint(painter: SwirlPainter(_time, _swirlStrength, _rotationSpeed), size: Size.infinite),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(12)),
      child: Column(children: [
        Row(children: [
          Expanded(child: Column(children: [
            Text('漩涡强度: ${_swirlStrength.toStringAsFixed(1)}', style: const TextStyle(color: Colors.white70, fontSize: 11)),
            Slider(value: _swirlStrength, min: 1, max: 15, onChanged: (v) => setState(() => _swirlStrength = v), activeColor: Colors.cyan),
          ])),
          Expanded(child: Column(children: [
            Text('旋转速度: ${_rotationSpeed.toStringAsFixed(1)}', style: const TextStyle(color: Colors.white70, fontSize: 11)),
            Slider(value: _rotationSpeed, min: 0.5, max: 5, onChanged: (v) => setState(() => _rotationSpeed = v), activeColor: Colors.cyan),
          ])),
        ]),
      ]),
    );
  }
}

class SwirlPainter extends CustomPainter {
  final double time;
  final double swirlStrength;
  final double rotationSpeed;
  
  SwirlPainter(this.time, this.swirlStrength, this.rotationSpeed);
  
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    final center = Offset(size.width / 2, size.height / 2);
    
    for (int x = 0; x < size.width; x += 4) {
      for (int y = 0; y < size.height; y += 4) {
        final dx = x - center.dx;
        final dy = y - center.dy;
        final dist = sqrt(dx * dx + dy * dy);
        final angle = atan2(dy, dx) + dist * swirlStrength / 100 - time * rotationSpeed;
        
        final srcX = center.dx + cos(angle) * dist;
        final srcY = center.dy + sin(angle) * dist;
        
        final pattern = sin(srcX * 0.05) * cos(srcY * 0.05);
        final intensity = (pattern + 1) / 2;
        
        final hue = (intensity * 180 + time * 30) % 360;
        canvas.drawRect(Rect.fromLTWH(x.toDouble(), y.toDouble(), 4, 4),
            Paint()..color = HSVColor.fromAHSV(0.7, hue, 0.8, 1).toColor());
      }
    }
  }
  
  @override
  bool shouldRepaint(covariant SwirlPainter old) => true;
}

class SpectrumShaderDemo extends StatefulWidget {
  const SpectrumShaderDemo({super.key});
  @override
  State<SpectrumShaderDemo> createState() => _SpectrumShaderDemoState();
}

class _SpectrumShaderDemoState extends State<SpectrumShaderDemo> with TickerProviderStateMixin {
  late AnimationController _animController;
  late AudioPlayer _audioPlayer;
  Float32List _audioData = Float32List(64);
  double _energy = 0, _bass = 0, _mid = 0, _treble = 0;
  double _time = 0;
  bool _isPlaying = false;
  int _spectrumType = 0;
  
  static const String _audioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';

  @override
  void initState() {
    super.initState();
    _initAudio();
    _animController = AnimationController(vsync: this, duration: const Duration(milliseconds: 33))..repeat();
    _animController.addListener(_update);
  }
  
  Future<void> _initAudio() async {
    _audioPlayer = AudioPlayer();
    final session = await AudioSession.instance;
    await session.configure(const AudioSessionConfiguration.music());
    _audioPlayer.playerStateStream.listen((s) => setState(() => _isPlaying = s.playing));
    try { await _audioPlayer.setUrl(_audioUrl); } catch (e) { debugPrint('加载失败: $e'); }
  }
  
  void _update() {
    _time += 0.033;
    for (int i = 0; i < 64; i++) {
      if (_isPlaying) {
        final freq = (i / 64) * 8 + 1;
        _audioData[i] = _audioData[i] * 0.85 + (sin(_time * freq) * 0.5 + 0.5) * 0.15;
      } else {
        _audioData[i] *= 0.95;
      }
    }
    
    double total = 0, bassE = 0, midE = 0, trebleE = 0;
    for (int i = 0; i < 64; i++) {
      total += _audioData[i];
      if (i < 16) bassE += _audioData[i];
      else if (i < 40) midE += _audioData[i];
      else trebleE += _audioData[i];
    }
    _energy = total / 64;
    _bass = bassE / 16;
    _mid = midE / 24;
    _treble = trebleE / 24;
    
    setState(() {});
  }

  @override
  void dispose() {
    _animController.dispose();
    _audioPlayer.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('频谱可视化')),
      body: Stack(children: [
        CustomPaint(painter: SpectrumPainter(_audioData, _time, _spectrumType), size: Size.infinite),
        Positioned(bottom: 30, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.8), borderRadius: BorderRadius.circular(16)),
      child: Column(mainAxisSize: MainAxisSize.min, children: [
        Row(children: [
          const Icon(Icons.music_note, color: Colors.teal),
          const SizedBox(width: 8),
          const Expanded(child: Text('SoundHelix - Song 1', style: TextStyle(color: Colors.white, fontSize: 14))),
          Container(padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
              decoration: BoxDecoration(color: _isPlaying ? Colors.teal : Colors.grey[800], borderRadius: BorderRadius.circular(12)),
              child: Text(_isPlaying ? '播放中' : '暂停', style: const TextStyle(color: Colors.white, fontSize: 12))),
        ]),
        const SizedBox(height: 12),
        Row(mainAxisAlignment: MainAxisAlignment.center, children: [
          IconButton(icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow, color: Colors.teal, size: 36),
              onPressed: () => _isPlaying ? _audioPlayer.pause() : _audioPlayer.play()),
        ]),
        Wrap(spacing: 8, children: [
          ActionChip(label: const Text('条形', style: TextStyle(color: Colors.white, fontSize: 11)),
              backgroundColor: _spectrumType == 0 ? Colors.teal : Colors.teal.withOpacity(0.3),
              onPressed: () => setState(() => _spectrumType = 0)),
          ActionChip(label: const Text('圆形', style: TextStyle(color: Colors.white, fontSize: 11)),
              backgroundColor: _spectrumType == 1 ? Colors.teal : Colors.teal.withOpacity(0.3),
              onPressed: () => setState(() => _spectrumType = 1)),
          ActionChip(label: const Text('波浪', style: TextStyle(color: Colors.white, fontSize: 11)),
              backgroundColor: _spectrumType == 2 ? Colors.teal : Colors.teal.withOpacity(0.3),
              onPressed: () => setState(() => _spectrumType = 2)),
        ]),
      ]),
    );
  }
}

class SpectrumPainter extends CustomPainter {
  final Float32List audioData;
  final double time;
  final int spectrumType;
  
  SpectrumPainter(this.audioData, this.time, this.spectrumType);
  
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a1520));
    
    switch (spectrumType) {
      case 0:
        _drawBarSpectrum(canvas, size);
        break;
      case 1:
        _drawCircularSpectrum(canvas, size);
        break;
      case 2:
        _drawWaveSpectrum(canvas, size);
        break;
    }
  }
  
  void _drawBarSpectrum(Canvas canvas, Size size) {
    final barWidth = size.width / audioData.length;
    
    for (int i = 0; i < audioData.length; i++) {
      final barHeight = audioData[i] * size.height * 0.8;
      final hue = (i / audioData.length * 180 + time * 20) % 360;
      
      final rect = Rect.fromLTWH(i * barWidth, size.height - barHeight, barWidth - 2, barHeight);
      canvas.drawRect(rect, Paint()..color = HSVColor.fromAHSV(0.8, hue, 0.8, 1).toColor());
    }
  }
  
  void _drawCircularSpectrum(Canvas canvas, Size size) {
    final center = Offset(size.width / 2, size.height / 2);
    final baseRadius = min(size.width, size.height) * 0.2;
    
    for (int i = 0; i < audioData.length; i++) {
      final angle = (i / audioData.length) * 2 * pi - pi / 2;
      final radius = baseRadius + audioData[i] * 100;
      final hue = (i / audioData.length * 360 + time * 30) % 360;
      
      final x = center.dx + cos(angle) * radius;
      final y = center.dy + sin(angle) * radius;
      
      canvas.drawLine(
        Offset(center.dx + cos(angle) * baseRadius, center.dy + sin(angle) * baseRadius),
        Offset(x, y),
        Paint()..color = HSVColor.fromAHSV(0.8, hue, 0.8, 1).toColor()..strokeWidth = 4..strokeCap = StrokeCap.round,
      );
    }
  }
  
  void _drawWaveSpectrum(Canvas canvas, Size size) {
    final path = Path();
    final stepX = size.width / (audioData.length - 1);
    
    for (int i = 0; i < audioData.length; i++) {
      final x = i * stepX;
      final y = size.height / 2 + audioData[i] * size.height * 0.4;
      
      if (i == 0) {
        path.moveTo(x, y);
      } else {
        path.lineTo(x, y);
      }
    }
    
    final hue = time * 20 % 360;
    canvas.drawPath(path, Paint()..color = HSVColor.fromAHSV(0.8, hue, 0.8, 1).toColor()..style = PaintingStyle.stroke..strokeWidth = 2);
  }
  
  @override
  bool shouldRepaint(covariant SpectrumPainter old) => true;
}

class MusicPulseDemo extends StatefulWidget {
  const MusicPulseDemo({super.key});
  @override
  State<MusicPulseDemo> createState() => _MusicPulseDemoState();
}

class _MusicPulseDemoState extends State<MusicPulseDemo> with TickerProviderStateMixin {
  late AnimationController _animController;
  late AudioPlayer _audioPlayer;
  Float32List _audioData = Float32List(64);
  double _energy = 0, _bass = 0, _mid = 0, _treble = 0;
  double _time = 0;
  bool _isPlaying = false;
  
  static const String _audioUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3';

  @override
  void initState() {
    super.initState();
    _initAudio();
    _animController = AnimationController(vsync: this, duration: const Duration(milliseconds: 33))..repeat();
    _animController.addListener(_update);
  }
  
  Future<void> _initAudio() async {
    _audioPlayer = AudioPlayer();
    final session = await AudioSession.instance;
    await session.configure(const AudioSessionConfiguration.music());
    _audioPlayer.playerStateStream.listen((s) => setState(() => _isPlaying = s.playing));
    try { await _audioPlayer.setUrl(_audioUrl); } catch (e) { debugPrint('加载失败: $e'); }
  }
  
  void _update() {
    _time += 0.033;
    for (int i = 0; i < 64; i++) {
      if (_isPlaying) {
        final freq = (i / 64) * 8 + 1;
        _audioData[i] = _audioData[i] * 0.85 + (sin(_time * freq) * 0.5 + 0.5) * 0.15;
      } else {
        _audioData[i] *= 0.95;
      }
    }
    
    double total = 0, bassE = 0, midE = 0, trebleE = 0;
    for (int i = 0; i < 64; i++) {
      total += _audioData[i];
      if (i < 16) bassE += _audioData[i];
      else if (i < 40) midE += _audioData[i];
      else trebleE += _audioData[i];
    }
    _energy = total / 64;
    _bass = bassE / 16;
    _mid = midE / 24;
    _treble = trebleE / 24;
    
    setState(() {});
  }

  @override
  void dispose() {
    _animController.dispose();
    _audioPlayer.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('音乐脉冲')),
      body: Stack(children: [
        CustomPaint(painter: MusicPulsePainter(_energy, _bass, _mid, _treble, _time), size: Size.infinite),
        Positioned(bottom: 30, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.8), borderRadius: BorderRadius.circular(16)),
      child: Column(mainAxisSize: MainAxisSize.min, children: [
        Row(children: [
          const Icon(Icons.music_note, color: Colors.green),
          const SizedBox(width: 8),
          const Expanded(child: Text('SoundHelix - Song 1', style: TextStyle(color: Colors.white, fontSize: 14))),
          Container(padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
              decoration: BoxDecoration(color: _isPlaying ? Colors.green : Colors.grey[800], borderRadius: BorderRadius.circular(12)),
              child: Text(_isPlaying ? '播放中' : '暂停', style: const TextStyle(color: Colors.white, fontSize: 12))),
        ]),
        const SizedBox(height: 12),
        Row(mainAxisAlignment: MainAxisAlignment.center, children: [
          IconButton(icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow, color: Colors.green, size: 36),
              onPressed: () => _isPlaying ? _audioPlayer.pause() : _audioPlayer.play()),
        ]),
      ]),
    );
  }
}

class MusicPulsePainter extends CustomPainter {
  final double energy;
  final double bass;
  final double mid;
  final double treble;
  final double time;
  
  MusicPulsePainter(this.energy, this.bass, this.mid, this.treble, this.time);
  
  @override
  void paint(Canvas canvas, Size size) {
    final bgColor = Color.lerp(const Color(0xFF0a0a15), const Color(0xFF1a2a1a), energy)!;
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = bgColor);
    
    final center = Offset(size.width / 2, size.height / 2);
    final maxRadius = min(size.width, size.height) * 0.4;
    
    for (int i = 0; i < 10; i++) {
      final radius = maxRadius * (i / 10) + bass * 50;
      final pulse = sin(radius * 0.05 - time * 3) * 0.5 + 0.5;
      final alpha = (1 - i / 10) * 0.3 * pulse;
      
      canvas.drawCircle(center, radius, Paint()..color = Colors.green.withOpacity(alpha));
    }
    
    final coreRadius = 20 + energy * 30;
    final coreColor = HSVColor.fromAHSV(0.8, 120 + energy * 60, 0.8, 1).toColor();
    canvas.drawCircle(center, coreRadius, Paint()..color = coreColor);
  }
  
  @override
  bool shouldRepaint(covariant MusicPulsePainter old) => true;
}

class PlasmaDemo extends StatefulWidget {
  const PlasmaDemo({super.key});
  @override
  State<PlasmaDemo> createState() => _PlasmaDemoState();
}

class _PlasmaDemoState extends State<PlasmaDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  double _time = 0;
  double _speed = 1.0;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _controller.addListener(() {
      _time += 0.016 * _speed;
      setState(() {});
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('等离子效果')),
      body: Stack(children: [
        CustomPaint(painter: PlasmaPainter(_time), size: Size.infinite),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(12)),
      child: Column(children: [
        Text('速度: ${_speed.toStringAsFixed(1)}x', style: const TextStyle(color: Colors.white70, fontSize: 11)),
        Slider(value: _speed, min: 0.1, max: 3, onChanged: (v) => setState(() => _speed = v), activeColor: Colors.yellow),
      ]),
    );
  }
}

class PlasmaPainter extends CustomPainter {
  final double time;
  
  PlasmaPainter(this.time);
  
  @override
  void paint(Canvas canvas, Size size) {
    for (int x = 0; x < size.width; x += 3) {
      for (int y = 0; y < size.height; y += 3) {
        final value1 = sin(x * 0.02 + time);
        final value2 = sin(y * 0.02 + time * 0.5);
        final value3 = sin((x + y) * 0.02 + time * 0.7);
        final value4 = sin(sqrt(x * x + y * y) * 0.02 + time);
        
        final plasma = (value1 + value2 + value3 + value4) / 4;
        final hue = (plasma * 180 + 180 + time * 20) % 360;
        
        canvas.drawRect(Rect.fromLTWH(x.toDouble(), y.toDouble(), 3, 3),
            Paint()..color = HSVColor.fromAHSV(1, hue, 0.8, 1).toColor());
      }
    }
  }
  
  @override
  bool shouldRepaint(covariant PlasmaPainter old) => true;
}

class KaleidoscopeDemo extends StatefulWidget {
  const KaleidoscopeDemo({super.key});
  @override
  State<KaleidoscopeDemo> createState() => _KaleidoscopeDemoState();
}

class _KaleidoscopeDemoState extends State<KaleidoscopeDemo> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  double _time = 0;
  int _segments = 8;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 16))..repeat();
    _controller.addListener(() {
      _time += 0.016;
      setState(() {});
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('万花筒')),
      body: Stack(children: [
        CustomPaint(painter: KaleidoscopePainter(_time, _segments), size: Size.infinite),
        Positioned(bottom: 20, left: 20, right: 20, child: _buildControls()),
      ]),
    );
  }
  
  Widget _buildControls() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(12)),
      child: Column(children: [
        Text('对称段数: $_segments', style: const TextStyle(color: Colors.white70, fontSize: 11)),
        Slider(value: _segments.toDouble(), min: 2, max: 16, divisions: 14, 
            onChanged: (v) => setState(() => _segments = v.toInt()), activeColor: Colors.pink),
      ]),
    );
  }
}

class KaleidoscopePainter extends CustomPainter {
  final double time;
  final int segments;
  
  KaleidoscopePainter(this.time, this.segments);
  
  @override
  void paint(Canvas canvas, Size size) {
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), Paint()..color = const Color(0xFF0a0a15));
    
    final center = Offset(size.width / 2, size.height / 2);
    final maxRadius = min(size.width, size.height) / 2;
    
    for (int seg = 0; seg < segments; seg++) {
      final baseAngle = (seg / segments) * 2 * pi;
      
      canvas.save();
      canvas.translate(center.dx, center.dy);
      canvas.rotate(baseAngle);
      
      for (int r = 0; r < maxRadius; r += 5) {
        final angle = (r / maxRadius) * pi / segments + time;
        final hue = (r / maxRadius * 180 + time * 30) % 360;
        
        final x = r * cos(angle);
        final y = r * sin(angle);
        
        canvas.drawCircle(Offset(x, y), 3, Paint()..color = HSVColor.fromAHSV(0.7, hue, 0.8, 1).toColor());
      }
      
      canvas.restore();
    }
  }
  
  @override
  bool shouldRepaint(covariant KaleidoscopePainter old) => true;
}

🎯 六、总结与展望

📝 6.1 本文要点回顾

复制代码
着色器编程核心概念:

1. 着色器基础
   - 渲染管线
   - 顶点与片段着色器
   - GLSL 语言

2. 经典效果
   - 渐变与颜色
   - 噪声与分形
   - 波纹与扭曲

3. 音频驱动
   - 频谱可视化
   - 节拍响应
   - 参数映射

4. Flutter 集成
   - Shader API
   - 着色器加载
   - 实时更新

🚀 6.2 性能优化建议

复制代码
着色器优化策略:

1. 减少分支
   - 避免复杂条件
   - 使用数学函数替代
   - 向量化计算

2. 纹理优化
   - 使用纹理图集
   - 合理的纹理大小
   - Mipmap 使用

3. 计算优化
   - 预计算常量
   - 减少循环次数
   - 利用 GPU 并行

4. 内存管理
   - 减少 uniform 数量
   - 使用纹理存储数据
   - 避免动态分配

💡 提示:着色器是现代图形编程的核心技术,通过GPU加速可以实现令人惊叹的视觉效果。掌握GLSL语言和渲染管线是创建高性能可视化的关键。

相关推荐
2601_949593652 小时前
Harmony Flutter 跨平台开发实战:鸿蒙与音乐律动艺术、分布式联觉震动:鸿蒙多端同步的节奏共鸣
flutter·harmonyos
2601_949593652 小时前
Harmony Flutter 跨平台开发实战:鸿蒙与音乐律动艺术、元胞自动机:生命游戏的音频演化逻辑
flutter
早點睡3902 小时前
Harmony Flutter 跨平台开发实战:鸿蒙与音乐律动艺术、Lissajous 利萨茹曲线:频率耦合的轨迹艺术
flutter
果粒蹬i2 小时前
鸿蒙跨平台实战:React Native在OpenHarmony上的AccessibilityInfo辅助功能开关详解
react native·华为·harmonyos
2601_949593652 小时前
Harmony Flutter 跨平台开发实战:鸿蒙与音乐律动艺术、Mandelbrot 分形生长:自相似性的音频映射
flutter
菜鸟小芯2 小时前
【GLM-5 陪练式创意 UI 实战】第一篇:创意魔法盒 —— 用 AI 生成 “开心” 主题 Flutter UI,搞定深浅色与响应式
人工智能·flutter·ui
Esaka_Forever2 小时前
「为什么不用 Flutter 做游戏」「为什么不用 Cocos 做 App 界面」
flutter·游戏
特立独行的猫a2 小时前
uniapp-x的HarmonyOS鸿蒙应用开发:tabbar底部导航栏的实现
华为·uni-app·harmonyos·鸿蒙·uniapp-x
特立独行的猫a3 小时前
基于HarmonyOS ArkTS的MVVM架构最佳实践
华为·架构·harmonyos·mvvm·最佳实战