有没有办法更快地绘制数百个点(p5.js)

Is there a way to draw hundreds of points faster (p5.js)

我正在制作一个程序来测试我对 Perlin 噪声生成算法的尝试。 Perlin 噪音本身看起来不错,但我发现在 canvas 上绘制噪音非常慢。这可能是因为对于 canvas 中的每个点,我都必须调用 stroke() 函数来更改下一个像素的颜色,然后绘制该像素。这是在 400*400 像素 canvas 上完成的,所以我使用 stroke() 更改颜色 160,000 次并调用 point() 160,000 次。

这需要一些时间来完成。我想知道是否有任何方法可以使它更快。也许如果我可以将 Perlin 噪声转换为图像,然后加载该图像而不是单独绘制所有 160,000 个点?

我绘制循环的代码如下

function draw() {
  background(220);
  strokeWeight(1);
      
  for(var row = 0; row < height; row ++)
  {
    for(var column = 0; column < width; column ++)
    {
      //takes a noise value from the myNoise array whose elements have a range of [-1,1] and turns it into a value from [0,256], and makes that the color of the next point
      stroke((myNoise[row][column]+1)*128);
      
      point(column,row)
    }
  }
    
  noLoop();
}

编辑:我使用以下代码创建和加载图像。感谢 Samathingamajig 提供小费。

function draw() {
  background(220);
      
  img = createImage(width,height);
  img.loadPixels();
  
  for(var row = 0; row < height; row ++)
  {
    for(var column = 0; column < width; column ++)
    {
      //takes a noise value from the myNoise array whose elements have a range of [-1,1] and turns it into a value from [0,256], and makes that the color of the next pixel in the image
      img.set(row,column,color((myNoise[row][column]+1)*128))      
    }
  }
  img.updatePixels();
  
  image(img,0,0)
  
  noLoop();
  
}

另外Samathingamajig指出400*400是160,000,不是1,600,我在上面改过

我的原始代码 运行 绘制循环花费了大约 4 秒。这个新版本大约需要 0.75 秒。

我还按照 rednoyz 的建议使用 createGraphics() 方法进行了测试。这不如使用图像方法快,因为它仍然需要我调用 stroke() 160,000 次。

这两种解决方案都为我提供了可以快速绘制的图像,但是 createImage() 使我创建图像的时间比 createGraphics() 少得多。

我已经尝试了很多毫秒测试,图像方法是目前为止最好的。正如@Samathinamajig 指出的那样,问题实际上只是您尝试处理的像素数量。

测试:https://editor.p5js.org/KoderM/sketches/6XPirw_98s

只是为了对现有建议添加一些细微差别:

使用pixels[] instead of set(x, y, color):考虑考虑 [r,g,b,a,...] 像素顺序的 1D 索引不太直观,并且(pixelDensity 在视网膜上显示),但速度更快。

文档提到:

Setting the color of a single pixel with set(x, y) is easy, but not as fast as putting the data directly into pixels[]. Setting the pixels[] values directly may be complicated when working with a retina display, but will perform better when lots of pixels need to be set directly on every loop.

在您的情况下,大致如下所示:

img.loadPixels();
  
  let numPixels = width * height;
  for(let pixelIndex = 0; pixelIndex < numPixels; pixelIndex ++)
  {
    //takes a noise value from the myNoise array whose elements have a range of [-1,1] and turns it into a value from [0,256], and makes that the color of the next pixel in the image
    // index of red channel for the current pixel
    // pixels = [r0, g0, b0, a0, r1, g1, b1, a1, ...]
    let redIndex = pixelIndex * 4;
    // convert 1D array index to 2D array indices
    let column = pixelIndex % width;
    let row    = floor(pixelIndex / width);
    // get perlin noise value
    let grey = (myNoise[row][column]+1) * 128;
    // apply grey value to R,G,B channels (and 255 to alpha)
    img.pixels[redIndex]     = grey; // R
    img.pixels[redIndex + 1] = grey; // G
    img.pixels[redIndex + 2] = grey; // B
    img.pixels[redIndex + 3] = 255;  // A
  }
  
  img.updatePixels();

(循环一次而不是嵌套循环也会有所帮助)。

关于 point(),它可能会在幕后使用类似 beginShape(POINTS);vertex(x,y);endShape(); 的东西,这意味着像这样的东西会稍微更有效率:

let numPixels = width * height;
  beginShape(POINTS);
  for(let pixelIndex = 0; pixelIndex < numPixels; pixelIndex ++)
  {
    //takes a noise value from the myNoise array whose elements have a range of [-1,1] and turns it into a value from [0,256], and makes that the color of the next pixel in the image
    // convert 1D array index to 2D array indices
    let column = pixelIndex % width;
    let row    = floor(pixelIndex / width);
    // get perlin noise value
    stroke(color((myNoise[row][column]+1) * 128));
    // apply grey value to R,G,B channels (and 255 to alpha)
    vertex(column, row);
  }
  endShape();

话虽如此,它可能无法按预期工作:

  1. 据我所知,这不适用于 createCanvas(400, 400, WEBGL),因为目前您无法为形状中的每个顶点设置独立的笔划。
  2. 对于典型的 Canvas 2D 渲染器,使用 beginShape()/endShape()
  3. 渲染这么多顶点可能仍然很慢

虽然是一个更高级的主题,但另一个应该很快的选项是 shader()。 (顺便说一句,可能会在 shadertoy.com 上找到一些柏林噪声灵感)。

p5.js 非常适合学习,但它的主要目标不是拥有最高性能的 canvas 渲染器。如果着色器在这个阶段有点太复杂,但您通常对 javascript 感到满意,您可以查看其他库,例如 pixi.js (maybe pixi particle-emitter 可能会很方便?)