使用Processing和PixelFlow库创建交互式流体太极动画

使用Processing和PixelFlow库创建交互式流体太极动画

  • 引言
  • 准备工作
  • 效果展示
  • 代码结构
  • 代码解析
    • 第一部分:导入库和设置基本参数
    • 第二部分:流体类定义
      • `MyFluidDataConfig` 类详解
      • `MyFluidData` 类详解
      • `my_update` 方法详解
      • 流体类定义完整代码
    • 第三部分:太极类定义
      • 太极类定义完整代码
    • 结语

引言

本教程将指导您如何使用Processing编程环境结合PixelFlow库来创建一个交互式的流体太极动画。PixelFlow是一个基于GPU的实时图形库,它提供了高效的流体模拟功能。我们将通过麦克风输入的音频振幅来驱动流体模拟,并在流体之上绘制一个动态的太极图案。

准备工作

  1. 安装Processing:访问Processing官网下载并安装Processing IDE。

  2. 导入DwPixelFlow库:在Processing中,通过菜单"Sketch" -> “Import Library…” -> “Add Library…”,搜索并安装DwPixelFlow库。

  3. 导入声音库:同样地,搜索并安装Minim库,用于处理声音输入。

效果展示

在这里插入图片描述

代码结构

我们的项目将包含以下几个主要部分:

  1. 设置窗口和基本参数:定义窗口大小、背景色和线条颜色。
  2. 声音输入设置:使用Processing的AudioInAmplitude类来获取麦克风输入的音频振幅。
  3. 流体模拟设置:初始化PixelFlow库,创建流体对象,并设置其参数。
  4. 太极图案设置:创建太极对象,并为其分配绘制层。
  5. 主循环:在draw函数中更新流体模拟和太极图案。

代码解析

第一部分:导入库和设置基本参数

import com.thomasdiewald.pixelflow.java.DwPixelFlow;
import com.thomasdiewald.pixelflow.java.fluid.DwFluid2D;

import processing.core.*;
import processing.opengl.PGraphics2D;
import processing.sound.*;

int viewport_w = 600; // 定义窗口宽
int viewport_h = 600; // 定义窗口高
color bg_color = #FFFFFF; // 背景色
color line_color = #000000; // 线条颜色

DwFluid2D fluid; // 流体
PGraphics2D pg_fluid; // 流体层

// 声音麦克风输入
AudioIn audioIn;
// 振幅-音量
Amplitude rms;

Taichi taichi; // 太极
PGraphics2D pg_taichi;  // 太极图层
PGraphics2D pg_obstacles; // 障碍物
float taichi_radius = 100;

void settings() {
  size(viewport_w, viewport_h, P2D);
}

void setup() {
  background(bg_color);
  frameRate(60);

  // 设定声音
  setupSound();
  // 设置流体参数
  setupFluid();

  // 设置太极图
  setupTaichi();
}

void setupSound() {
  audioIn = new AudioIn(this, 0);
  audioIn.play();

  rms = new Amplitude(this);
  rms.input(audioIn);
}

void setupFluid() {
  // 初始化pixelflow
  DwPixelFlow context = new DwPixelFlow(this);
  context.print();
  context.printGL();

  // 流体模拟
  fluid = new DwFluid2D(context, width, height, 1);
  fluid.param.dissipation_velocity = 0.70f;
  fluid.param.dissipation_density  = 0.60f;

  fluid.addCallback_FluiData(new MyFluidData(rms));

  // 流体层
  pg_fluid = (PGraphics2D)createGraphics(width, height, P2D);

  // 障碍物层
  pg_obstacles = (PGraphics2D)createGraphics(viewport_w, viewport_h, P2D);
  pg_obstacles.smooth(4);
}

void setupTaichi() {
  pg_taichi = (PGraphics2D)createGraphics(viewport_w, viewport_h, P2D);
  pg_taichi.smooth(4);

  taichi = new Taichi(new PVector(width / 2, height / 2), 100, pg_taichi, rms);
}

void draw() {
  // 流体更新
  drawFluid();
  // 太极图
  drawTaichi();
}

void drawFluid() {
  // 绘制障碍物
  drawObstacles();

  // 给流体增加障碍物
  fluid.addObstacles(pg_obstacles);
  // 流体更新
  fluid.update();
  fluid.renderFluidTextures(pg_fluid, 0);

  // 显示流体层
  image(pg_fluid, 0, 0);
  // 显示障碍物层
  image(pg_obstacles, 0, 0);
}

void drawObstacles() {
  pg_obstacles.beginDraw();
  pg_obstacles.blendMode(REPLACE);
  pg_obstacles.clear();

  float x = width * 0.5;
  float y = height * 0.5;
  pg_obstacles.pushMatrix();
  pg_obstacles.translate(x, y);
  pg_obstacles.stroke(line_color);
  pg_obstacles.strokeWeight(2);
  pg_obstacles.noFill();
  pg_obstacles.circle(0, 0, 500);
  pg_obstacles.popMatrix();

  pg_obstacles.endDraw();
}

void drawTaichi() {
  pg_taichi.beginDraw();
  pg_taichi.blendMode(REPLACE);
  pg_taichi.clear();

  pg_taichi.pushMatrix();
  taichi.display();
  pg_taichi.popMatrix();

  pg_taichi.endDraw();

  image(pg_taichi, 0, 0);
}

第二部分:流体类定义

从您提供的代码看来,这段代码是用于在Processing环境中结合DwFluid库创建一个模拟流体的视觉效果的。代码中涉及到很多类和方法,主要包含以下几个方面:

  1. MyFluidDataConfig 类:用于配置流体运动的参数,比如圆的位置、半径、运动的角度和速度以及颜色等。

  2. MyFluidData 类:实现了 DwFluid2D.FluidData 接口,用于更新流体物理仿真的状态,同时整合声音输入作为流体动态变化的一部分。

  3. my_update 方法:是流体物理仿真更新的核心,它计算流体粒子的速度和位置,并且将这些数据传递给流体库来模拟流动。

现在,让我们进一步分析并解释每个部分的作用以及如何操作。

MyFluidDataConfig 类详解

这个类定义了流体仿真所需要的一些配置参数。例如:

  • x, y: 圆心位置。
  • radius: 半径。
  • isClockwise: 定义旋转方向。
  • angleSpeed: 角速度。
  • rx, ry, prx, pry: 当前和之前的位置坐标。
  • angle: 当前角度。
  • c: 颜色。

MyFluidData 类详解

此类使用 MyFluidDataConfig 中定义的配置来创建两个配置对象 config1config2。它们负责控制流体仿真中两个独立的流动体的行为。此外,它接收一个Amplitude对象(rms),它可能用于分析音频信号并将其影响应用于流体动画。

my_update 方法详解

这个方法是每一帧都会调用的,用于更新流体的状态。步骤如下:

  1. 使用极坐标计算弧上点的位置。
  2. 随机量产生抖动,使流动效果更自然。
  3. 计算速度,并根据是否顺时针调整y轴速度。
  4. 将计算的位置和速度用来更新流体对象的状态。
  5. 根据声音级别调整流体密度的半径大小。
  6. 添加颜色和密度到流体中。

流体类定义完整代码

public class MyFluidDataConfig {
  float x;  // 圆心位置x
  float y; // 圆心位置y
  float radius = 100; // 半径
  boolean isClockwise; // 是否是顺时针
  float angleSpeed = 0.04;
  float rx, ry, prx, pry; // 圆周运动,弧上的点位置,以及上一帧的点位置
  float angle = 0; // 角度
  color c; // 颜色
}


public class MyFluidData implements DwFluid2D.FluidData {
  MyFluidDataConfig config1;
  MyFluidDataConfig config2;
  Amplitude rms;

  MyFluidData(Amplitude rms) {
    this.rms = rms;
    
    float x1 = width * 0.5;
    float y1 = height * 0.5;
    
    config1 = new MyFluidDataConfig();
    config1.x = x1;
    config1.y = y1;
    config1.radius = 130;
    config1.isClockwise = true;
    config1.angleSpeed = 0.05;
    config1.c = color(0.0, 0.0, 0.0);
    config1.angle = PI / 2;

    config2 = new MyFluidDataConfig();
    config2.x = x1;
    config2.y = y1;
    config2.radius = 130;
    config2.isClockwise = true;
    config2.angleSpeed = 0.04;
    config2.c = color(0.0, 0.0, 0.0);
    config2.angle = - PI / 2;
  }

  void my_update(DwFluid2D fluid, MyFluidDataConfig config) {
    float vscale = 14;
    
    float soundLevel = rms.analyze() * 1000;

    float delta = random(-3, 3);
    // 极坐标下计算弧上点的位置,用一个随机量进行抖动
    config.rx = config.x + (config.radius + delta) * cos(config.angle); 
    config.ry = config.y + (config.radius + delta) * sin(config.angle);

    // 计算速度
    float vx = (config.rx - config.prx) * vscale;
    float vy = (config.ry - config.pry) * vscale;
    // 顺时针的话,需要乘以-1,因为y轴相反
    if (config.isClockwise) {
      vy = (config.ry - config.pry) * (-vscale);
    }

    float px = config.rx;
    float py = config.ry;
    // 顺时针的话,需要乘以-1,因为y轴相反
    if (config.isClockwise) {
      py = height - config.ry;
    }

    // 给流体上的点添加速度
    fluid.addVelocity(px, py, 16, vx, vy);
    
    float radius1 = map(soundLevel, 10, 600, 15, 20);
    float radius2 = map(soundLevel, 10, 500, 8, 12);
    println(soundLevel, radius1, radius2);
    
    // 给流体上的点添加密度,颜色为c,半径为radius1,稍微大点
    fluid.addDensity(px, py, radius1, red(config.c) / 255.0, green(config.c) / 255.0, blue(config.c) / 255.0, 1.0f);
    // 给流体上的点添加密度,颜色为白色,半径为radius2,稍微小点
    fluid.addDensity(px, py, radius2, 1.0f, 1.0f, 1.0f, 1.0f);
    //fluid.addTemperature(px, py, 30, 10);

    // 现在终将成为过去
    config.prx = config.rx;
    config.pry = config.ry;

    // 增加弧度角,用于下一帧计算,才能旋转
    float angleSpeed = constrain(radians(soundLevel * 0.03), 0.01, 0.08) * 3;
    println(soundLevel, angleSpeed);
    config.angle += angleSpeed;
  }

  @Override
    public void update(DwFluid2D fluid) {
    my_update(fluid, config1);
    my_update(fluid, config2);
  }
}

第三部分:太极类定义

太极类定义完整代码

class Taichi {
  
  PVector location;
  float radius;
  PGraphics2D pg;
  float angle = 0;
  Amplitude rms;  

  Taichi(PVector location, float radius, PGraphics2D pg, Amplitude rms) {
    this.location = location;
    this.radius = radius;
    this.pg = pg;
    this.rms = rms;
  }

  void display() {
    float d = 2 * radius;
    float soundLevel = rms.analyze();
    angle += TWO_PI/360 * soundLevel * 20+6;
    
    pg.noStroke();
    pg.translate(location.x, location.y);  // 平移坐标系,方便使用相对位置进行绘制
    pg.rotate(angle);
    pg.fill(0);
    pg.arc(0, 0, d, d, PI / 2, PI * 3 / 2);

    pg.fill(255);
    pg.arc(0, 0, d, d, -PI / 2, PI / 2);

    pg.fill(255);
    pg.circle(0, d / 4, radius);

    pg.fill(0);
    pg.circle(0, -d / 4, radius);

    pg.fill(0);
    pg.circle(0, d / 4, radius / 5);

    pg.fill(255);
    pg.circle(0, -d / 4, radius / 5);
  }
}

结语

通过本教程,您将学习到如何结合Processing的声音输入和PixelFlow的流体模拟功能,创建一个动态的太极动画。您可以根据自己的创意进一步扩展这个项目,例如添加更多的交互元素或改变流体的行为。记得在编写代码时保持耐心,实践是学习编程的最佳方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/607422.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

无监督式学习

1.是什么? 无监督式学习与监督式学习**最大的区别就是:**没有事先给定的训练实例,它是自动对输入的示例进行分类或者分群; 优点:不需要标签数据,极大程度上扩大了我们的数据样本,其次不受监督信…

部署 Sentinel 控制台:实现流量管理和监控

序言 Sentinel 是阿里巴巴开源的一款流量防护与监控平台,它可以帮助开发者有效地管理微服务的流量,实现流量控制、熔断降级、系统负载保护等功能。本文将介绍如何在项目中部署和配置 Sentinel 控制台,实现微服务的流量防护和监控。 一、Sen…

WEB基础--单元测试与三层架构

单元测试 为什么要进行单元测试 减少创建类,我们希望在一个类中,并且测试时不需要改代码,那么我们就要用到junit单元测试 常见测试分类 黑盒测试 黑盒测试也叫功能测试,主要关注软件每个功能是否实现,并不关注软件代…

【websocket-客户端可视化工具】

postman 新版postman (版本v11以上) ,除了http协议,还支持了Websocket,MQTT,gRPC等多种连接协议,可以作为多种协议的客户端,使用起来非常方便。 使用 服务端代码 这里以websocket协议举例,代…

【Linux】网络接口绑定和组合的操作实例

网络接口绑定和组合的操作实例 (一)网卡1. 增2. 查3. 激活——设置网络接口 (二)网络接口绑定1. 概述2. 实验操作3. 删除绑定 (三)网络接口组合1. 概述2. 实验操作 (一)网卡 1. 增 …

分割模型Maskformer系列

maskformer:Per-Pixel Classification is Not All You Need for Semantic Segmentation 论文地址:https://arxiv.org/pdf/2107.06278 1.概述 传统的语义分割方法通常采用逐像素分类(per-pixel classification),而实…

【JAVA基础之装箱和拆箱】自动装箱和自动拆箱

🔥作者主页:小林同学的学习笔录 🔥mysql专栏:小林同学的专栏 目录 1.包装类 1.1 概述 1.2 Integer类 1.3 装箱和拆箱 1.4 自动装箱和自动拆箱 1.5 基本类型与字符串之间的转换 1.5.1 基本类型转换为字符串 1.5.2 字符串转…

带你破解晶振PCB的布局要点!

一、晶振的分类 01 无源晶振 无源晶振器是一种 passives 振荡器,它不需要外部电源来维持振荡。它的振荡频率由晶体的物理尺寸和材料特性决定(一般都采无源晶振)。 02 有源晶振 有源晶振器是一种 active 振荡器,它需要外部电源来…

计算机中GPU快不行的几个标志,看下有没有你遇到的

GPU是处理图形密集型任务的主要组件。尽管它非常耐用,但它最终会磨损并开始失效。在到达生命的终结之前,它通常会显示出即将发生故障的迹象,需要及时修复或更换。本指南详细介绍了这些标志。 在我们开始之前 在深入研究GPU故障的迹象之前,重要的是要承认,下面提到的一些…

卷积通用模型的剪枝、蒸馏---蒸馏篇--RKD关系蒸馏(以deeplabv3+为例)

本文使用RKD实现对deeplabv3+模型的蒸馏;与上一篇KD蒸馏的方法有所不同,RKD是对展平层的特征做蒸馏,蒸馏的loss分为二阶的距离损失Distance-wise Loss和三阶的角度损失Angle-wise Loss。 一、RKD简介 RKD算法的核心是以教师模型的多个输出为结构单元,取代传统蒸馏学习中以教…

Leetcode—394. 字符串解码【中等】

2024每日刷题&#xff08;131&#xff09; Leetcode—394. 字符串解码 实现代码 class Solution { public:string decodeString(string s) {string curstr;int curNum 0;stack<pair<string, int>> st; for(char c: s) {if(isdigit(c)) {curNum curNum * 10 (c…

电脑中的两个固态硬盘比一个好,想知道为什么吗

你当前的电脑很有可能有一个NVME SSD作为主驱动器&#xff0c;但可能至少还有一个插槽可以放另一个SSD&#xff0c;而且这样做可能是个好主意。 两个SSD可以提高性能 如果你有两个固态硬盘&#xff0c;你可以从中获得比有一个更好的性能。一种方法是使用RAID 0将两个驱动器组…

Python_AI库 Pandas的loc和iloc的区别与使用实例

Python中Pandas的loc和iloc的区别与使用实例 在Pandas中&#xff0c;loc和iloc是两个常用的方法&#xff0c;用于基于标签&#xff08;label&#xff09;和整数位置&#xff08;integer location&#xff09;来选择数据。尽管两者在功能上有重叠&#xff0c;但它们在用法和性能…

OceanBase开发者大会实录:SaaS 场景降本50%!石基零售应用 OB Cloud 实践

本文来自2024 OceanBase开发者大会&#xff0c;石基零售助理总裁 、 ROC 产品事业部负责人陈亮的演讲实录—《石基零售与 OB Cloud 零售行业应用实践》。完整视频回看&#xff0c;请点击这里&#xff1e;> 大家下午好&#xff01;我是石基零售的陈亮。今天和大家分享一下石基…

struct和union大小计算规则

Union 一&#xff1a;联合类型的定义 联合也是一种特殊的自定义类型&#xff0c;这种类型定义的变量也包含一系列的成员&#xff0c;特征是这些成员公用同一块空间&#xff08;所以联合也叫共用体&#xff09; 比如&#xff1a;共用了 i 这个较大的空间 二&#xff1a; 联合的…

数据分析从入门到精通 2.pandas修真之前戏基础

从爱上自己那天起&#xff0c;人生才真正开始 —— 24.5.6 为什么学习pandas numpy已经可以帮助我们进行数据的处理了&#xff0c;那么学习pandas的目的是什么呢? numpy能够帮助我们处理的是数值型的数据&#xff0c;当然在数据分析中除了数值型的数据还有好多其他类型…

信通院智能体标准发布,实在智能牵头编写

4月28日&#xff0c;由人工智能关键技术和应用评测工业和信息化部重点实验室、中国信息通信研究院&#xff08;以下简称&#xff1a;中国信通院&#xff09;人工智能研究所共同主办的“人工智能”高质量发展研讨会顺利召开&#xff0c;会上中国信通院正式发布全国首个Agent&…

【C++】string类的使用②(容量接口Capacity || 元素获取Element access)

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; STL || C 目录 前言&#x1f525;容量接口&#xff08;Capacity&#xff09;size和lengthcapacitymax_sizereserveresizeclearemptyshrink_to_fit &#x1f525;元素获取&#xff08;Ele…

从零开始打造个性化生鲜微信商城小程序

随着移动互联网的普及&#xff0c;小程序商城已经成为越来越多商家的选择。本文将通过实战案例分享&#xff0c;教您如何在五分钟内快速搭建个性化生鲜小程序商城。 步骤一&#xff1a;登录乔拓云网后台&#xff0c;进入商城管理页面 打开乔拓云官网&#xff0c;点击右上角的“…

【连连国际注册_登录安全分析报告】

连连国际注册/登录安全分析报告 前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨…