处理3改进强化数学计算

时间:2016-04-27 08:08:52

标签: math processing cpu

我写了一个非常简单的草图来模拟两个平面波的干扰,非常容易。

这个问题似乎对cpu有点密集(而且处理只使用一个核心)而且我只得到1 o 2 fps。

知道如何改进这个草图吗?

float x0;
float y0;
float x1;
float y1;
float x2;
float y2;
int t = 0;

void setup() {
  //noLoop();
  frameRate(30);
  size(400, 400, P2D);
  x0 = width/2;
  y0 = height/2;
  x1 = width/4;
  y1 = height/2;
  x2 = width * 3/4;
  y2 = height / 2;
}

  void draw() {
  background(0);

  for (int x = 0; x <= width; x++) {
    for (int y = 0; y <= height; y++) {

      float d1 = dist(x1, y1, x, y);
      float d2 = dist(x2, y2, x, y);
      float factorA = 20;
      float factorB = 80;
      float wave1 = (1 + (sin(TWO_PI * d1/factorA + t)))/2 * exp(-d1/factorB);
      float wave2 = (1 + (sin(TWO_PI * d2/factorA + t)))/2 * exp(-d2/factorB);
      stroke( (wave1 + wave2) *255);
      point(x, y);
    }
  }

  t--; //Wave propagation
  //saveFrame("wave-##.png");
}

2 个答案:

答案 0 :(得分:4)

正如Kevin建议的那样,使用point()并不是最有效的方法,因为它调用beginShape();vertex() and endShape();。使用像素可能会更好。

此外,嵌套循环可以写成单个循环,并且可以避免在幕后使用平方根的dist()(您可以使用具有更高值的平方距离)。

以下是使用这些版本的版本:

float x1;
float y1;
float x2;
float y2;
int t = 0;
//using larger factors to use squared distance bellow instead of dist(),sqrt()
float factorA = 20*200;
float factorB = 80*200;

void setup() {
  //noLoop();
  frameRate(30);
  size(400, 400);
  x1 = width/4;
  y1 = height/2;
  x2 = width * 3/4;
  y2 = height / 2;
  //use pixels, not points()
  loadPixels();
}

void draw() {
  for (int i = 0; i < pixels.length; i++) {
    int x = i % width;
    int y = i / height;

    float dx1 = x1-x;
    float dy1 = y1-y;
    float dx2 = x2-x;
    float dy2 = y2-y;

    //squared distance
    float d1 = dx1*dx1+dy1*dy1;//dist(x1, y1, x, y);
    float d2 = dx2*dx2+dy2*dy2;//dist(x2, y2, x, y);

    float wave1 = (1 + (sin(TWO_PI * d1/factorA + t))) * 0.5 * exp(-d1/factorB);
    float wave2 = (1 + (sin(TWO_PI * d2/factorA + t))) * 0.5 * exp(-d2/factorB);

    pixels[i] = color((wave1 + wave2) *255);
  }
  updatePixels();
  text((int)frameRate+"fps",10,15);
  //  endShape();
  t--; //Wave propagation
  //saveFrame("wave-##.png");
}

使用查找表可以进一步加快这一功能,以实现更耗时的功能,例如sin()exp()

即使在javascript中,您也可以看到粗略(需要调整的数字)预览:

&#13;
&#13;
var x1;
var y1;
var x2;
var y2;
var t = 0;

var factorA = 20*200;
var factorB = 80*200;

function setup() {
  createCanvas(400, 400);
  frameRate(30);
  x1 = width/4;
  y1 = height/2;
  x2 = width * 3/4;
  y2 = height / 2;
  loadPixels();
}

function draw() {
  for (var i = 0; i < pixels.length; i+= 4) {
    var x = i % width;
    var y = i / height;
    
    var dx1 = x1-x;
    var dy1 = y1-y;
    var dx2 = x2-x;
    var dy2 = y2-y;
    
    var d1 = dx1*dx1+dy1*dy1;//dist(x1, y1, x, y);
    var d2 = dx2*dx2+dy2*dy2;//dist(x2, y2, x, y);
    
    var wave1 = (1 + (sin(TWO_PI * d1/factorA + t))) * 0.5 * exp(-d1/factorB);
    var wave2 = (1 + (sin(TWO_PI * d2/factorA + t))) * 0.5 * exp(-d2/factorB);

    pixels[i] = pixels[i+1] = pixels[i+2] = (wave1 + wave2) * 255;
    pixels[i+3] = 255;
  }
  updatePixels();
  text(frameRate+"fps",10,15);
  //  endShape();
  t--; //Wave propagation
  //saveFrame("wave-##.png");
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.24/p5.min.js"></script>
&#13;
&#13;
&#13;

因为你使用数学来合成图像,所以将它写成GLSL着色器可能更有意义。请务必查看PShader tutorial以获取更多信息。

<强>更新

这是一个GLSL版本:代码不那么hacky,而且更具可读性:

float t = 0;
float factorA = 0.20;
float factorB = 0.80;

PShader waves;

void setup() {
  size(400, 400, P2D);
  noStroke();

  waves = loadShader("waves.glsl");
  waves.set("resolution", float(width), float(height));
  waves.set("factorA",factorA);
  waves.set("factorB",factorB);
  waves.set("pt1",-0.5,0.0);
  waves.set("pt2",0.75,0.0);  
}

void draw() {
  t++;
  waves.set("t",t);

  shader(waves);
  rect(0, 0, width, height);  
}
void mouseDragged(){
  float x = map(mouseX,0,width,-1.0,1.0);
  float y = map(mouseY,0,height,1.0,-1.0);
  println(x,y);
  if(keyPressed) waves.set("pt2",x,y);
  else           waves.set("pt1",x,y);
}
void keyPressed(){
  float amount = 0.05;
  if(keyCode == UP)     factorA += amount;
  if(keyCode == DOWN)   factorA -= amount;
  if(keyCode == LEFT)   factorB -= amount;
  if(keyCode == RIGHT)  factorB += amount;
  waves.set("factorA",factorA);
  waves.set("factorB",factorB);
  println(factorA,factorB);
}

和waves.glsl:

#define PROCESSING_COLOR_SHADER

uniform vec2 pt1;
uniform vec2 pt2;
uniform float t;

uniform float factorA;
uniform float factorB;

const float TWO_PI = 6.283185307179586;

uniform vec2 resolution;
uniform float time;


void main(void) {
  vec2 p = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;

  float d1 = distance(pt1,p);
  float d2 = distance(pt2,p);

  float wave1 = (1.0 + (sin(TWO_PI * d1/factorA + t))) * 0.5 * exp(-d1/factorB);
  float wave2 = (1.0 + (sin(TWO_PI * d2/factorA + t))) * 0.5 * exp(-d2/factorB);

  float gray = wave1 + wave2;

  gl_FragColor=vec4(gray,gray,gray,1.0);
}

您可以使用第一个点拖动并按住一个键并拖动第二个点。 此外,使用UP/DOWNLEFT/RIGHT键更改 factorA factorB 。结果看起来很有趣:

GLSL Waves 1

GLSL Waves 2

GLSL Waves 3

此外,您可以从this answer获取一些代码以使用线程保存帧(我建议保存未压缩的)。

答案 1 :(得分:1)

选项1:预渲染草图。

这似乎是一个静态重复模式,因此您可以通过提前运行动画并将每个帧保存到图像来预渲染它。我看到你已经在那里打电话给saveFrame()了。保存图像后,可以将它们加载到新草图中,一次播放一帧。它不应该需要很多图像,因为它似乎很快就重复了。想想一个永远循环的动画GIF。

选项2:降低草图的分辨率。

你真的需要像素完美的400x400分辨率吗?你可以画一幅100x100的图像并放大吗?

或者你可以通过递增1以上来降低for循环的分辨率:

  for (int x = 0; x <= width; x+=2) {
    for (int y = 0; y <= height; y+=2) {

您可以播放增加的数量,然后使用strokeWeight()rect()功能绘制更大的像素。

选项3:降低草图的时间分辨率。

不是每1帧移动1个像素,如果每5帧移动5个像素,该怎么办?加快动画速度,但每隔X帧移动一次,这样整体速度看起来是一样的。您可以将modulo operatorframeCount变量一起使用,仅对每个X帧执行一些操作。请注意,您仍然希望将草图的整体帧率保持为30或60,但您只需每X帧更改一次动画。

选项4:简化动画。

你真的需要计算每一个像素吗?如果你想展示的是一系列尺寸增加的圆圈,那么有更简单的方法可以做到这一点。调用ellipse()函数比调用point()函数要快很多倍。您可以使用其他功能创建模糊效果,而无需每秒调用point()五十次(这是您尝试调用它的频率)。

选项5:重构您的代码。

如果所有其他方法都失败了,那么您将不得不重构您的代码。您的大部分计划时间都用在point()函数中 - 您可以通过在mouseX, mouseY函数末尾的draw()处绘制一个椭圆并比较性能来证明这一点当您在嵌套for循环中注释掉point()的调用时。

计算机并不神奇,因此每秒调用point()函数五十万次并不是免费的。您不得不以某种方式减少该数字,或者通过采用上述选项中的一个(或多个),或者以其他方式重构您的代码。

你如何做到这真的取决于你的实际目标,你还没有说明。如果您只是尝试渲染此动画,那么预渲染它将正常工作。如果您需要与用户进行用户交互,那么可能会降低分辨率。你将不得不牺牲一些东西,它真的取决于你是什么。